Android Service詳解(一)

Lebens發表於2019-03-03

Service系列一共2篇,主要介紹Service相關的使用,以及使用Service實現IPC通訊。本文的重點是介紹Service相關的使用,通過Service 實現IPC通訊放在下一篇講解。

What is a Service

根據官方的介紹:

  1. Service既不是一個執行緒,Service通常執行在當成宿主程式的主執行緒中,所以在Service中進行一些耗時操作就需要在Service內部開啟執行緒去操作,否則會引發ANR異常。
  2. 也不是一個單獨的程式。除非在清單檔案中宣告時指定程式名,否則Service所在程式就是application所在程式。

Service存在的目的有2個:

  1. 告訴系統,當前程式需要在後臺做一些處理。這意味著,Service可以不需要UI就在後臺執行,不用管開啟它的頁面是否被銷燬,只要程式還在就可以在後臺執行。可以通過startService()方式呼叫,這裡需要注意,除非Service手動呼叫stopService()或者Service內部主動呼叫了stopSelf(),否則Service一直執行。
  2. 程式通過Service對外開放某些操作。通過bindService()方式與Service呼叫,長期連線和互動,Service生命週期和其繫結的元件相關。

Service Lifecycle

public class MyService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return startCommandReturnId;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}
複製程式碼

要解釋這個首先要知道Service的實現,需要實現抽象方法onBind,以及重寫onStartCommand,這2個方法會在下文介紹到。

通過上面的介紹可以知道,Service有3種啟動方式:

  1. startService()
  2. bindService()
  3. 同時呼叫

這幾種方式啟動的Service生命週期略微不同。

startService方式

startService()只要一個Intent引數,指定要開啟的Service即可

Intent intent = new Intent(MainActivity.this, MyService.class);
複製程式碼
  1. 當呼叫Service的startService()後,

    • Service首次啟動,則先呼叫onCreate(),在呼叫onStartCommand()
    • Service已經啟動,則直接呼叫onStartCommand()
  2. 當呼叫stopSelf()或者stopService()後,會執行onDestroy(),代表Service生命週期結束。

  3. startService方式啟動Service不會呼叫到onBind()。
    startService可以多次呼叫,每次呼叫都會執行onStartCommand()。
    不管呼叫多少次startService,只需要呼叫一次stopService就結束。
    如果startService後沒有呼叫stopSelf或者stopService,則Service一直存活並執行在後臺。

  4. onStartCommand的返回值一共有3種

    • START_STICKY = 1:service所在程式被kill之後,系統會保留service狀態為開始狀態。系統嘗試重啟service,當服務被再次啟動,傳遞過來的intent可能為null,需要注意。
    • START_NOT_STICKY = 2:service所在程式被kill之後,系統不再重啟服務
    • START_REDELIVER_INTENT = 3:系統自動重啟service,並傳遞之前的intent

    預設返回START_STICKY;

bindService方式

通過bindService繫結Service相對startService方式要複雜一點。
由於bindService是非同步執行的,所以需要額外構建一個ServiceConnection物件用與接收bindService的狀態,同時還要指定bindService的型別。

//1. 定義用於通訊的物件,在Service的onBind()中返回的物件。
public class MyBind extends Binder {
        public int mProcessId;
 }

//2. 定義用於接收狀體的ServiceConnection
mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                //和服務繫結成功後,服務會回撥該方法
                //服務異常中斷後重啟,也會重新呼叫改方法
                MyService.MyBind myBinder = (MyService.MyBind) service;
            }

            @Override
            public void onNullBinding(ComponentName name) {
                //Service的onBind()返回null時將會呼叫這個方法,並不會呼叫onServiceConnected()
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                // 當服務異常終止時會呼叫。
                // 注意,unbindService時不會呼叫
            }
        };
        
//3. 在需要的地方繫結到Service
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
複製程式碼

bindService()也可以呼叫多次,與startService()不同,當發起物件與Service已經成功繫結後,不會多次返回ServiceConnection中的回撥方法。

通過bindService方式與Service進行繫結後,當沒有物件與Service繫結後,Service生命週期結束,這個過程包括繫結物件被銷燬,或者主動掉呼叫unbindService()

startService和bindService同時開啟

當同時呼叫startService和bindService後,需要分別呼叫stopService和unbindService,Service才會走onDestroy()

一個Service必須要在既沒有和任何Activity關聯又處理停止狀態的時候才會被銷燬。

IntentService

通過上面的介紹我們知道,通過StartService形式開啟Service時,如果不主動呼叫stopService,Service將在後臺一直執行。同時如果我們在Service中執行耗時操作還是引起ANR異常,為了解決這2個問題,IntentService出現了。
當我們需要執行某些一次性、非同步的操作時,IntentService能很好的滿足這個場景。

IntentService相比於普通的Service,在使用時將不再需要實現onStartCommand(),同時需要實現onHandleIntent()。
真正需要我們處理的邏輯就在onHandleIntent()實現,IntentService會內部自動呼叫stopSelf()關閉自己。

至於防止ANR異常,具體的實現方式其實還是挺簡單,就是在內部新建了子執行緒,並在子執行緒中內部的Looper來分發事件,具體程式碼就不貼了。

下一篇Android Service詳解(二)將介紹通過Service實現IPC通訊

相關文章