1. 程式人生 > >Android 4 學習(19):Services

Android 4 學習(19):Services

參考《Professional Android 4 Development

Services

Serviceinvisible的,因此其優先順序不高於visibleActivity,之所以說不高於,是因為我們可以設定Service為在前臺執行。

建立Service

Android提供了Service抽象類,繼承它便可以建立一個Service類:

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
public class MyService extends
Service {   @Override   public void onCreate() {     super.onCreate();     // TODO: Actions to perform when service is created.   }   @Override   public IBinder onBind(Intent intent) {     // TODO: Replace with service binding implementation.     return null;   } }

建立Service類之後,還需要在Manifest

裡面註冊:

<service android:enabled=”true” android:name=”.MyService” android:permission=”com.paad.MY_SERVICE_PERMISSION”/>

若要預設只有自己的程式可以使用這個Service,需要新增Android:permission屬性。其他Application若要使用這個服務,需要加入這個permission

執行Service

ServicestartService()方法呼叫時,即可引起onStartCommand方法的呼叫,因此需要重寫Service

中的onStartCommand方法,並且onStartCommand方法可能會被多次呼叫。onStartCommand()方法將返回一個int值,用於指定當Serviceruntime殺掉又重啟的時,系統該如何響應:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  startBackgroundTask(intent, startId);
  return Service.START_STICKY;
}

重啟Service

onStartCommand()方法可以返回這些引數,它們的意義是:

START_STICKY:如果service程序被kill掉,保留service的狀態為開始狀態,但不保留遞送的intent物件。隨後系統會嘗試重新建立service,由於服務狀態為開始狀態,所以建立服務後一定會呼叫onStartCommand(Intent,int,int)方法。如果在此期間沒有任何啟動命令被傳遞到service,那麼引數Intent將為null。

START_NOT_STICKY:“非粘性的”。如果在執行完onStartCommand後,服務被異常kill掉,系統不會自動重啟該服務。
START_REDELIVER_INTENT:使用這個返回值時,如果未執行完onStartCommand,服務在呼叫stopSelf之前被kill掉,系統會自動重啟該服務,並將Intent的值傳入。

啟動和停止服務

呼叫startService方法可以啟動服務:

private void explicitStart() {
  // Explicitly start My Service
  Intent intent = new Intent(this, MyService.class);
  // TODO Add extras if required.
  startService(intent);
}
private void implicitStart() {
  // Implicitly start a music Service
  Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
  intent.putExtra(MyMusicService.ALBUM_NAME_EXTRA, “United”);
  intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA, “Pheonix”);
  startService(intent);
}

呼叫stopService方法可以停止服務:

// Stop a service explicitly.
stopService(new Intent(this, MyService.class));
// Stop a service implicitly.
Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
stopService(intent);

服務自殺

服務內部呼叫stopSelf方法,可以停止該服務。

繫結ServiceActivity

首先,Service要實現IBind介面:

@Override
public IBinder onBind(Intent intent) {
  return binder;
}
public class MyBinder extends Binder {
  MyMusicService getService() {
    return MyMusicService.this;
  }
}
private final IBinder binder = new MyBinder();

ServiceConnection類用於表示ServiceActivity的繫結,每個繫結都需要建立一個ServiceConnection

// Reference to the service
private MyMusicService serviceRef;
// Handles the connection between the service and activity
private ServiceConnection mConnection = new ServiceConnection() {
    public void onServiceConnected(ComponentName className, IBinder service) {
      // Called when the connection is made.
      serviceRef = ((MyMusicService.MyBinder)service).getService();
    }
    public void onServiceDisconnected(ComponentName className) {
      // Received when the service unexpectedly disconnects.
      serviceRef = null;
    }
};

最後,呼叫bindService方法,傳入用於啟動ServiceIntentServiceConnection和標誌位:

// Bind to the service
Intent bindIntent = new Intent(MyActivity.this, MyMusicService.class);
bindService(bindIntent, mConnection, Context.BIND_AUTO_CREATE);

通常情況下,Android應用在自己的記憶體空間中執行,並不共享記憶體。若要與其他程序通訊,可以使用廣播,或在Intent中新增Bundle引數啟動其他Service的方法。如果需要更緊密的通訊,可以使用Android Interface Defi nition Language(AIDL)。AIDL使用OS級別的簡單變數定義了介面,可以跨應用傳遞物件。

建立前臺服務

使用startForeground方法啟動服務,可以使服務獲得與Visible Activity相同的優先順序,例如:

private void startPlayback(String album, String artist) {
  int NOTIFICATION_ID = 1;
  // Create an Intent that will open the main Activity if the notification is clicked.
  Intent intent = new Intent(this, MyActivity.class);
  PendingIntent pi = PendingIntent.getActivity(this, 1, intent, 0);
  // Set the Notification UI parameters
  Notification notification = new Notification(R.drawable.icon, “Starting Playback”, System.currentTimeMillis());
  notification.setLatestEventInfo(this, album, artist, pi);
  // Set the Notification as ongoing
  notification.flags = notification.flags | Notification.FLAG_ONGOING_EVENT;
  // Move the Service to the Foreground
  startForeground(NOTIFICATION_ID, notification);
}

使用stopForeground方法可以取消Service的前臺屬性:

public void pausePlayback() {
  // Move to the background and remove the Notification
  stopForeground(true);
}

使用後臺執行緒

ServiceActivity一樣,也是在主執行緒中執行的,為了更好地響應使用者,我們需要使用後臺執行緒的方式執行ServiceAndroid提供了兩個抽象類來幫助我們實現:AsyncTaskIntentService

使用AsyncTask

AsyncTask不僅能幫助我們將費時操作放到後臺執行,還可以實現和UI執行緒的同步。AsyncTask適合執行那些耗時比較短並且和UI執行緒有互動的任務,對於耗時較久的任務(例如下載),使用Service更為合適。需要注意的是AsyncTask在應用restart之後會被cancel掉。

private class MyAsyncTask extends AsyncTask<String, Integer, String> {
@Override
protected String doInBackground(String... parameter) {
// Moved to a background thread.
String result = “”;
int myProgress = 0;
int inputLength = parameter[0].length();
// Perform background processing task, update myProgress]
for (int i = 1; i <= inputLength; i++) {
myProgress = i;
result = result + parameter[0].charAt(inputLength-i);
try {
Thread.sleep(100);
} catch (InterruptedException e) { }
publishProgress(myProgress);
}
// Return the value to be passed to onPostExecute
return result;
}
@Override
protected void onProgressUpdate(Integer... progress) {
// Synchronized to UI thread.
// Update progress bar, Notification, or other UI elements
asyncProgress.setProgress(progress[0]);
}
@Override
protected void onPostExecute(String result) {
// Synchronized to UI thread.
// Report results via UI update, Dialog, or notifications
asyncTextView.setText(result);
}
}

使用AsyncTask,首先要建立一個AsyncTask的子類類並實現doInBackgroundonProgressUpdateonPostExecute方法。這三個方法的說明:

  • doInBackground: 這個方法用於執行需要在後臺執行緒中長期執行的操作,可以通過publishProgress方法傳遞引數給onProgressUpdate;在這個方法執行完畢之後,此方法將返回值傳遞給onProgressUpdate。
  • onProgressUpdate: 接收publishProgress方法傳入的引數,更新UI
  • onPostExecute: doInBackground執行結束後,將返回值傳入此方法。

執行AsyncTask

String input = “redrum ... redrum”;
new MyAsyncTask().execute(input);

Intent Service簡介

IntentService可以通過傳入Intent引數呼叫,傳入的Intent將進入一個佇列中被非同步執行。IntentService封裝了訊息的非同步處理,後臺執行緒建立以及與UI執行緒的同步。繼承IntentService類並實現onHandleIntent方法,即可建立一個Intent Service

import android.app.IntentService;
import android.content.Intent;
public class MyIntentService extends IntentService {
  public MyIntentService(String name) {
    super(name);
    // TODO Complete any required constructor tasks.
  }
  @Override
  public void onCreate() {
    super.onCreate();
    // TODO: Actions to perform when service is created.
  }
  @Override
  protected void onHandleIntent(Intent intent) {
  // This handler occurs on a background thread. TODO The time consuming task should be implemented here.
  // Each Intent supplied to this IntentService will be processed consecutively here. When all incoming Intents have been processed the Service will terminate itself.
  }
}

Loader簡介

Loader是一個抽象類,封裝了非同步載入資料的最佳實踐,最典型的就是CursorLoader了。Android中建立Loader類的簡單方法是繼承AsyncTaskLoader類,並實現這兩個功能:

  • 非同步載入資料
  • 監測資料來源的變化並及時更新

手動建立執行緒及GUI執行緒同步

儘管AsyncTaskIntent Service提供了簡單易用的非同步類封裝,但我們也可以建立自定義的非同步執行緒:

// This method is called on the main GUI thread.
private void backgroundExecution() {
  // This moves the time consuming operation to a child thread.
  Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);
  thread.start();
}
// Runnable that executes the background processing method.
private Runnable doBackgroundThreadProcessing = new Runnable() {
    public void run() {
      backgroundThreadProcessing();
    }
};
// Method which does some processing in the background.
private void backgroundThreadProcessing() {
// [ ... Time consuming operations ... ]
}

GUI執行緒同步:

runOnUiThread方法會在UI執行緒執行:

runOnUiThread(new Runnable() {
  public void run() {
  // Update a View or other Activity UI element.
  }
});

此外,可以使用Handler類更新UI執行緒:

//This method is called on the main GUI thread.
private void backgroundExecution() {
  // This moves the time consuming operation to a child thread.
  Thread thread = new Thread(null, doBackgroundThreadProcessing, “Background”);
  thread.start();
}
// Runnable that executes the background processing method.
private Runnable doBackgroundThreadProcessing = new Runnable() {
  public void run() {
    backgroundThreadProcessing();
  }
};
// Method which does some processing in the background.
private void backgroundThreadProcessing() {
// [ ... Time consuming operations ... ]
// Use the Handler to post the doUpdateGUI
// runnable on the main UI thread.
  handler.post(doUpdateGUI);
}
//Initialize a handler on the main thread.
private Handler handler = new Handler();
// Runnable that executes the updateGUI method.
private Runnable doUpdateGUI = new Runnable() {
  public void run() {
    updateGUI();
  }
};
// This method must be called on the UI thread.
private void updateGUI() {
// [ ... Open a dialog or modify a GUI element ... ]
}

Handler類還可以使用postDelayedpostAtTime實現推遲執行和推遲指定時間執行:

// Post a method on the UI thread after 1sec.
handler.postDelayed(doUpdateGUI, 1000);
// Post a method on the UI thread after the device has been in use for 5mins.
int upTime = 1000*60*5;
handler.postAtTime(doUpdateGUI, SystemClock.uptimeMillis()+upTime);

使用ALARMS

Timer不太,Alarms屬於系統服務,獨立於應用程式。即使應用程式為啟動,也可以使用Alarms啟動應用程式並獲取其服務,這樣不僅減少了耦合,也減少了系統資源的佔用。AndroidAlarms常與Broadcast Receiver一起使用。建立Alarm之前,首先要建立AlarmManager

AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);

建立,設定和取消ALARMS

建立Alarm需要這些引數:alarm型別,觸發時間,Alarm將要觸發的Pending Intent。目前Alarm型別有這些:

  • RTC_WAKEUP:在指定時間啟動指定Pending Intent,可以喚醒sleep中的裝置。
  • RTC在指定時間啟動指定Pending Intent,但不能喚醒sleep中的裝置。
  • ELAPSED_REALTIME:在某段時間後啟動指定的Pending Intent,某段時間是從裝置啟動但還沒有喚醒裝置算起。
  • ELAPSED_REALTIME_WAKEUP: 這個和ELAPSED_REALTIME的區別沒搞明白,以後遇到了再查吧。

下面是一個10秒後啟動Pending IntentAlarm示例:

AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
// Set the alarm to wake the device if sleeping.
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
// Trigger the device in 10 seconds.
long timeOrLengthofWait = 10000;
// Create a Pending Intent that will broadcast and action
String ALARM_ACTION = “ALARM_ACTION”;
Intent intentToFire = new Intent(ALARM_ACTION);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
// Set the alarm
alarmManager.set(alarmType, timeOrLengthofWait, alarmIntent);

取消Alarm

alarmManager.cancel(alarmIntent);

這裡的alarmIntent是指使用Alarm啟動的Pending Intent

建立可重複的Alarm

使用setRepeating或setInexactRepeating方法替代前面的set方法,並傳遞響應的引數進去,就可以實現可重複的Alarm

相比setRepeating,setInexactRepeating更省電,但不能指定某個具體的時間間隔。

setInexactRepeating可以接收的時間間隔引數:

  • INTERVAL_FIFTEEN_MINUTES
  • INTERVAL_HALF_HOUR
  • INTERVAL_HOUR
  • INTERVAL_HALF_DAY
  • INTERVAL_DAY

下面這個例子指定半小時後啟動Alarm,然後每隔半小時啟動一次:

// Get a reference to the Alarm Manager
AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
// Set the alarm to wake the device if sleeping.
int alarmType = AlarmManager.ELAPSED_REALTIME_WAKEUP;
// Schedule the alarm to repeat every half hour.
long timeOrLengthofWait = AlarmManager.INTERVAL_HALF_HOUR;
// Create a Pending Intent that will broadcast and action
String ALARM_ACTION = “ALARM_ACTION”;
Intent intentToFire = new Intent(ALARM_ACTION);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intentToFire, 0);
// Wake up the device to fire an alarm in half an hour, and every half-hour after that.
alarmManager.setInexactRepeating(alarmType, timeOrLengthofWait, timeOrLengthofWait, alarmIntent);

相關推薦

Android 4 學習19Services

參考《Professional Android 4 Development》 Services Service是invisible的,因此其優先順序不高於visible的Activity,之所以說不高於,是因為我們可以設定Service為在前臺執行。 建立Service Android提供了Ser

Android 4 學習21對話方塊

對話方塊 建立Dialog的兩種方式: 1. 使用Dialog類或其子類,包括DialogFragment 2. 在Activity中使用Dialog主題(theme) 下面是使用Dialog類的一個例子: // Create the new Dialog.Dialog dialog = n

Android 4 學習18搜尋

參考《Professional Android 4 Development》 搜尋 通過下面這幾種方式可以給應用程式新增搜尋功能: Search Bar Search View Quick Search Box 可搜尋的Content Provider 首

Android 4 學習17使用Content Resolver

Content Resolver簡介 每個應用程式都有一個ContentResolver例項,通過getContentResolver()方法可以獲取: ContentResolver cr = getContentResolver(); 與Content Provider對應,Cont

Android 4 學習20ActionBar

參考《Pro Android 4.0》 ActionBar 11.0之後,ActionBar在Activity中預設存在,可以在程式碼中設定其顯示與否: ActionBar actionBar = getActionBar(); // Hide the Action Bar actionBa

Android 4學習7使用者介面

參考《Professional Android 4 Development》 Android UI基本元素 下面這些概念是Android UI設計的基礎,深入學習和理解它們是Android UI設計的基礎: View:View是所有UI元素,包括Layout在內,的父

Android 4學習6概述

參考:《Professional Android 4 Application Development》 深入瞭解Android Activity 每一個Android Activity都對應於一個使用者介面(UI)。每個Android Application都有一個m

Android NDK學習編譯腳本語法Android.mk和Application.mk

GC make files 文件的 包括 一次 opengl aries 基本語法 一、Android.mk Android.mk分為一下幾部分: LOCAL_PATH:= $(call my-dir), 返回當前文件在系統中的路徑,Android.mk文件開始時必須定義

Android Camera學習如何實現轉動螢幕介面選單跟著轉動效果

最近公司在做車載專案,需要把照相機原本豎向顯示改為橫向顯示。所以研究了下camera選單朝向的問題。 系統提供了一個監聽sensor狀態變化的類OrientationEventListener。在系統程式碼CameraActivity中就是繼承的這個類。 private

Android BLE學習編寫自己的 BLE藍芽讀寫工具功能仿照nrf master control panel

背景 由於nordic官方的nrf master control panel只提供了apk,很多同學學習起來都得自己摸索藍芽的讀寫,專案中整理了BLE模組的基本讀寫方法以及一些常用的UUID,並且抽取了一些藍芽操作的流程,方便Android app程式碼開發,

Android BLE學習 Android搜尋BLE裝置

背景 總結一下最近ble的學習情況。自從入手ble 51822開發板後就開始不停加班,中途出於好奇,業餘時間寫了一些微控制器上json解析相關的東西,妄圖使用藍芽傳輸json資料,不知道是否實用,既然開始寫了,得寫出點樣子,晃晃蕩蕩,2016年的1月份就過去了

Qt 學習之路 2(19):事件的接受與忽略當重寫事件回撥函式時,時刻注意是否需要通過呼叫父類的同名函式來確保原有實現仍能進行!有好幾個例子。為什麼要這麼做?而不是自己去手動呼叫這兩個函式呢?因為我們無法確認父類中的這個處理函式有沒有額外的操作

版本: 2012-09-29 2013-04-23 更新有關accept()和ignore()函式的相關內容。 2013-12-02 增加有關accept()和ignore()函式的示例。 上一章我們介紹了有關事件的相關內容。我們曾經提到,事件可以依情況接受和忽略。現在,我們就

linux命令學習6ps命令

bytes 釋放 ice cti width kthread hellip 名稱 pts Linux中的ps命令是Process Status的縮寫。ps命令用來列出系統中當前運行的那些進程。ps命令列出的是當前那些進程的快照,就是執行ps命令的那個時刻的那些進程,如果想要

ActiveMQ19Consumer高級特性之獨有消費者Exclusive Consumer

consumer高級特性之獨有消費者(exclusive consumer)一、簡介Queue中的消息是按照順序被分發到consumers的。然而,當你有多個consumers同時從相同的queue中提取消息時,你將失去這個保證。因為這些消息是被多個線程並發的處理。有的時候,保證消息按照順序處理是很重要的。如

Windows Phone開發19三維透視效果

end 理論知識 form 之間 3d模型 中間 第一個 一個 好的 三維效果也可以叫透視效果,所以,我幹脆叫三維透視效果。理論知識少講,直接用例開場吧,因為這個三維效果其實很簡單,比上一節中的變換更省事,不信?一起來做一做練習吧。 練習一:把對象沿Y軸旋轉45度。 默認情

JAVA學習方法重載與方法重寫、thiskeyword和superkeyword

格式 hello new 初始 per 而且 方法重寫 學習 方式 方法重載與方法重寫、thiskeyword和superkeyword 1、方法重載 重載可以使具有同樣名稱但不同數目和類型參數的類傳遞給方法。 註: 一是重載方法的參數列表必須與被重載的方法不同

Linux命令學習17ifconfig命令

廣播 參考 vip 統計 協議 cnblogs 還需要 pro 網絡 版權聲明更新:2017-05-22博主:LuckyAlan聯系:[email protected]/* */聲明:吃水不忘挖井人,轉載請註明出處! 1 文章介紹 我們知道,在windows中,

ArcGIS API for JavaScript學習1第一個地圖

樣式表 參數 資源 charset 底層 arcgis 順序 api navi 1.簡介 ArcGIS API for JavaScript跟隨ArcGIS 9.3同時發布,是ESRI根據JavaScript技術實現的調用ArcGIS Server REST API接口的一

Java學習2將鍵盤錄入的內容保存到指定文件中

stream exce 創建 txt 關閉 如果 下午 line 再次 要求:保存鍵盤錄入的內容,當鍵盤輸入end時,錄入結束。 1 /** 2 * 保存鍵盤輸入,並以end結束 3 * 4 * @author xcx 5 * @time 2017年6

RabbitMQ學習遠程結果調用

cells actor ble 隨機 get getenv all 求和 int 場景:我們需要在傳輸消息時得到結果 客服端在發送請求時會發送回調隊列,服務端處理事情完成後會將結果返回到回調隊列中,在增加關聯標誌關聯每個請求和服務返回 客戶端代碼: public