一、Service的簡介
Service為Android四大元件之一,和Activity一樣,都是Context的子類,只是它沒有介面,Service很適合去執行那些長時間執行又不需要和使用者互動的任務。由於Service本身是在主執行緒執行的,所以如果需要執行耗時操作還是需要另外開啟子執行緒,否則會出現ANR錯誤。
Service包含三種型別:
- Foreground:前臺服務。啟動的時候使用通知(Notification),以提示使用者此服務正在執行。需要注意的是此時的Service還是在後臺執行的。
- Background:後臺服務。執行的時候使用者是不可感知的。
- Bound:繫結服務。當使用bindService()方法啟動Service的時候,此Service為繫結服務。此時Service可以和與它繫結的元件進行互動。可以繫結多個元件,並且當所有繫結的元件對其解綁時此Service才會被銷燬。
二、Service的生命週期
Service 有兩種啟動方式,startService() 和 bindService()。
-
startService() 啟動的生命週期如上圖左邊所示。特別的,會回撥 onStartCommand()方法
- onCreate()在整個生命週期中只會被呼叫一次
- onStartCommand()可能會被多次呼叫,包括 Service 重啟,重複呼叫 startService()
- startService() 啟動的 Service 停止的方法是 Service 呼叫自己的 stopSelf() 方法或是其他其他元件呼叫 stopService(Intent name) 方法,引數中的 name 可以直接 new 一個,只要關聯的 class 為要停止的目標 Service就可以了。
- stopSelf()還有個過載方法 stopSelf(int startId),可單獨停止 startId 對應的請求。startId 為 onStartCommand() 方法傳入的引數 startId,用來區分每次訪問請求,
-
bindService()啟動的生命週期如上圖右邊所示。特別的,會呼叫 onBind() 和 onUnbind()
- onCreate() 在整個生命週期中只會被呼叫一次
- 同一個元件可以繫結多次
- 當多個元件繫結此服務時 onBind() 只在第一次繫結的時候被呼叫。
- 用此方式啟動的 Service 使用 Service.stopSelf() 或者是在元件呼叫 stopService() 來停止服務是無效的
- 此服務會一直執行下去,直到繫結的所有的元件呼叫 unbindService() 方法對其解綁,只要有一個繫結的元件未解綁,則此服務不會停止。
onStartCommand() 方法返回一個整數值,讓系統在系統殺死此 Service 的時候如何處理此 Service
- START_NOT_STICKY:Service 被系統殺死後不會重啟
- START_STICKY:Service 被系統殺死後會重啟,此時呼叫 onStartCommand() 傳入的 Intent 的值為null
- START_REDELIVER_INTENT:Service 被系統殺死後會重啟,此時呼叫 onStartCommand() 傳入的 Intent 值為最後一次呼叫 startService() 時傳入的。
三、建立服務
1、建立後臺服務(無互動)
不可互動的後臺服務的啟動方式為 startService(),
public class MyService extends Service{
public static final String TAG = "MyService";
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG,"onBind");
return null;
}
@Override
public void onCreate() {
Log.e(TAG,"onCreate");
super.onCreate();
runAfterStop();
}
private void runAfterStop(){
new Thread(){
@Override
public void run() {
try {
Thread.sleep(5000);
Log.e(TAG,"stopSelf()");
stopSelf();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG,"onStartCommand");
return START_NOT_STICKY;
}
@Override
public boolean onUnbind(Intent intent) {
Log.e(TAG,"onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.e(TAG,"onDestroy");
super.onDestroy();
}
}
複製程式碼
啟動服務
public class BaseActivity extends AppCompatActivity{
...
Intent intent = new Intent(this, MyService.class);
startService(intent);
...
}
複製程式碼
2、建立前臺服務
前臺服務會在通知欄/狀態列上顯示,並且此時的 Service 的優先順序比較高,
public class ForegroundService extends Service{
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
//id不能為0,否則通知欄/狀態列不會顯示
int id = 1;
startForeground(id,createNotification());
}
private Notification createNotification(){
Intent notifiIntent = new Intent(this, FirstActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this,0,notifiIntent,0);
Notification.Builder builder = new Notification.Builder(this)
.setContentTitle("title")
.setContentText("Content")
.setSmallIcon(R.drawable.ic_launcher_background)
.setContentIntent(pendingIntent);
return builder.build();
}
}
...
//啟動服務
public class BaseActivity extends AppCompatActivity{
...
Intent intent = new Intent(this, MyService.class);
startService(intent);
...
}
複製程式碼
如果要取消前臺服務,可以呼叫 stopForeground(boolean removeNotification) ,
引數 removeNotification 為 true 表示取消前臺服務通知也移除掉通知欄/狀態列的圖示,false 表示不移除
呼叫此方法不會導致服務停止,只是把前臺轉到後臺
當服務停止的時候,通知欄/狀態列的圖示也會同時被移除
3、建立繫結服務(可互動的)
繫結服務(Bound Services)為客戶端-伺服器模式。由於需要互動,所以需要有一箇中間代理物件,此物件需要服務端建立然後返回給客戶端持有,型別為 Binder,由 onBind() 進行返回。
服務端程式碼:
public class BoundService extends Service {
private IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder{
public int add(int a,int b){
return BoundService.this.add(a,b);
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
private int add(int a,int b){
return a + b;
}
}
複製程式碼
客戶端程式碼:
public class BoundActivity extends Activity{
//持有繫結服務返回的 Binder 物件
private BoundService.LocalBinder mBinder;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
serviceBind();
}
@Override
protected void onStop() {
super.onStop();
unbindService(mServiceConn );
}
//繫結服務
private void serviceBind() {
Intent intent = new Intent(this, MyService.class);
bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);
}
//點選事件
public void addClick(View view){
add(2,3);
}
//呼叫繫結服務的方法
private void add(int a,int b){
Log.(TAG,mBinder.add(a,b));
}
//繫結服務後的回撥介面
private ServiceConnection mServiceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"bing Service Connected");
mBinder = (BoundService.LocalBinder) service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG,"unBind Service disConnected");
mBinder = null;
}
};
}
複製程式碼
這個只是基於簡單相同程式內呼叫,如果在 Manifest 檔案中給 BoundService 新增上 process=”:remote” 變成遠處服務,則上面的程式碼就會報錯,丟擲 java.lang.ClassCastException 異常,因為此種情況下返回的是 Binder 的代理物件 BInderProxy,所以丟擲型別轉換錯誤。程式間的通訊要用到 AIDL ,這個只能另外寫篇回顧了。
4、IntentService
IntentService 是 Service 的子類,用來處理非同步請求
特點:
1、會建立一個預設的工作子執行緒處理所有的請求
2、會有一個佇列逐個處理所有的 Intent 並會在 onHandleIntent() 實現
3、當 Service 處理所有的工作後會自動結束 Service,不需要手動呼叫 stopSelf()
4、預設實現返回值為 null 的 onBind() 方法
5、預設實現 onStartCommand(),會把請求的 Intent 放到工作佇列裡
所以我們不需要去管理 IntentService 的生命週期和管理執行緒。並且 IntentService 在處理完所以任務後會自動關閉。當業務不需要涉及到多執行緒任務時,IntentService 就能夠滿足大多數的需求了。
實現 IntentService 的方式很簡單,只需要提供一個構造方法和實現 onHandleIntent() 方法
public class TestIntentService extends IntentService {
publicTestIntentService() {
super("TestIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
//模擬耗時操作
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
複製程式碼
IntentService 的是使用 HandlerThread + Handler 來實現的。HandlerThread 為Thread的子類,Handler 執行在 HandlerThread 執行緒中,處理耗時操作。
四、Service 和執行緒 Thread
Service 是 Android 的一種機制,執行在主執行緒中,如果進行耗時操作,需要建立一個子執行緒執行。
Service 的優先順序高於後臺掛起的 Activity 和其所建立的子執行緒 Thread。系統可能會在記憶體不足的時候,優先殺死後臺掛起的 Activity 或 Thread,而不會輕易殺死 Service。
Thread 的執行是獨立於 Activity 的,當 Activity 被 finish 掉的時候,如果沒有主動停止 Thread 或未執行完任務,那麼它還會繼續執行。此時程式將不再持有這個 Thread 的引用,此時將控制不了此 Thread。
所以當需要長期穩定的在後臺執行某個任務時,需要使用 Service,而當 Service 在執行這個任務時要處理耗時操作時,要另外建立子執行緒來執行。