1. 程式人生 > >Android IntentService用法和原始碼分析

Android IntentService用法和原始碼分析

關於IntentService的介紹,我個人覺得還是先看官方描述比較好:

IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.
IntentService是Service的子類,它可以處理非同步處理請求。Client使用startService(Intent),來發起非同步請求。這個Service是會根據需要,使用一個工作執行緒去處理每個請求,當它處理完所有請求的時候會停止它自身。

This “work queue processor” pattern is commonly used to offload tasks from an application’s main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.
這種工作佇列的模式通常是用於從application 的主執行緒載入任務。IntentService 這個類的存在是為了簡化這種模式並且更加關心機制。為了為了IntentServie,繼承IntentService並實現onHandleIntent方法。IntentService將接收Intents,開啟一個工作執行緒,在合適的時候,停止這個Service

All requests are handled on a single worker thread – they may take as long as necessary (and will not block the application’s main loop), but only one request will be processed at a time.
所有的請求都在一個工作執行緒中處理,這些請求可能花費很多請求(不會阻塞application主執行緒),但是在同一時間只會處理一個。

通過官網的介紹,我們可以抓住這幾個重點:IntentService可以用來處理非同步請求,因為它有一個工作執行緒;它只能通過startService(Intent)這個方法來發送請求;它處理完請求後會自動關閉。

在IntentService中,我們可以知道,有一個很重要的方法:onHandleIntent,先看一下它的官方介紹:

This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call stopSelf().
當某個請求需要處理時,這個方法會在工作者執行緒被呼叫,一次僅僅會有一個請求被處理,但是處理過程會執行在工作者執行緒(獨立於其他應用程式邏輯執行)。因此,如果某段程式碼需要執行很長時間,它會阻塞住其他提交到該IntentService的請求,但是不會阻塞住其他任何東西。當所有的請求被處理完成之後,IntentService會停止它自身,因此你不應該手動呼叫stopSelf()方法。

接下來,我們來看看它的用法:

public class HandlerService extends IntentService {

    private static final String TAG = "HandlerService";

    public HandlerService() {
        super("HandlerService");
        Log.d(TAG, "HandlerService: "+Thread.currentThread().getName());
        
    }


    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {

        Log.d(TAG, "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {

        Log.d(TAG, "onStart: ");
        super.onStart(intent, startId);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "onHandleIntent: ");
        Log.d(TAG, "HandlerService: "+Thread.currentThread().getName());
        String path = intent.getStringExtra("path");
        downloadTask(path);

    }

    private void downloadTask(String path){
        Log.d(TAG, "downloadTask: ");
       try {
           Thread.sleep(3*1000);
       }catch (InterruptedException e){
           e.printStackTrace();
       }

      Intent intent = new Intent(MainActivity.BROCAST_MSG);
       intent.putExtra("path",path);
       sendBroadcast(intent);
    }


    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy: ");
        super.onDestroy();
    }

}

然後看看呼叫:

    public static String BROCAST_MSG = "FINISH";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        registerReceiver(broadcastReceiver,new IntentFilter(BROCAST_MSG));

        Intent intent = new Intent(this,HandlerService.class);
        intent.putExtra("path","http://www:xxx.xxx");
        startService(intent);
        startService(intent);
        startService(intent);


    }

    private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            if(intent!=null){
                if(intent.getAction()!=null&&intent.getAction().equals(BROCAST_MSG)){
                    System.out.println(intent.getStringExtra("path"));
                }
            }
        }
    };

記得在Manifest中註冊
我們看看結果:

D/HandlerService: HandlerService: main
D/HandlerService: onStartCommand: 
    onStart: 
D/HandlerService: onHandleIntent: 
    HandlerService: IntentService[HandlerService]
    downloadTask: 
D/HandlerService: onStartCommand: 
    onStart: 
    onStartCommand: 
    onStart: 

D/HandlerService: onHandleIntent: 
D/HandlerService: HandlerService: IntentService[HandlerService]
    downloadTask: 
I/System.out: http://www:xxx.xxx
D/HandlerService: onHandleIntent: 
D/HandlerService: HandlerService: IntentService[HandlerService]
    downloadTask: 
I/System.out: http://www:xxx.xxx
I/System.out: http://www:xxx.xxx
D/HandlerService: onDestroy: 

在這裡,我用的是廣播來傳遞訊息。如果不想使用廣播的,可以使用EventBus。

接下來就來看看IntentService的原始碼:

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj);
            stopSelf(msg.arg1);
        }
    }

  
    public IntentService(String name) {
        super();
        mName = name;
    }

  	
  	//設定Intent是否重投遞,如果重投遞,在onHandleIntent方法返回前,
  	//如果程序死了,會重啟程序,重新分發Intent
  	//但是隻會分發最近的一個Intent
    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
     
        super.onCreate();
        //通過例項化HandlerThreade新建執行緒&啟動
        //故,使用IntentService時,不需要額外新建執行緒。
        //HandlerThread繼承自Thread,內部封裝了Looper
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
		//獲取工程執行緒的Looper&維護自己的工作佇列
        mServiceLooper = thread.getLooper();
		//新建ServiceHandler,繫結上面的Looper
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
    	//獲取ServiceHandler訊息的引用
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        //傳送訊息,新增到訊息佇列
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    	//呼叫omnStart()方法
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
    	//Looper停止
        mServiceLooper.quit();
    }

 
    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

 
    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}


//Service.java
 public final void stopSelf(int startId) {
        if (mActivityManager == null) {
            return;
        }
        try {
            mActivityManager.stopServiceToken(
                    new ComponentName(this, mClassName), mToken, startId);
        } catch (RemoteException ex) {
        }
    }

我們可以看到,在onCreate()方法中,有初始化一個HandlerThread,HandlerThread可以看出,就是Handler+Thread的結合,並且獲取到了工作執行緒的Looper,使用工作執行緒的Looper現在屬於工作執行緒的Handler。

從上面的原始碼中,我們可以看出:IntentService = Handler+HandlerThread。
通過onStartCommand()方法傳遞服務IntentServiceHandler,依次插入Intent到工作佇列,在handleMeassage()中逐個呼叫onHandleIntent()方法。
通過onHandlerIntent()依次處理Intent對應的任務。

因此我們可以通過複寫onHandleIntent(),根據Intent的不同進行不同執行緒操作即可。

在這裡插入圖片描述

注意事項
  1. 工作任務佇列 = 順序執行
    即若一個任務正在IntentService中執行,此時再發送一個新的任務請求,這個新的任務會一直等到直到前面一個任務執行完畢後才開始執行。
    原因:
    1. 由於onCreate()只會呼叫一次,所以只會建立一個工作執行緒。
    2. 當多次呼叫startService時(即onStartCommand()也會呼叫多次),其實不會建立新的工作執行緒,只是把訊息加入訊息佇列,等待執行。
    3. 所以,多次啟動IntentService會按順序執行事件。

若服務停止,則會清楚訊息佇列中的訊息,後續的事件不執行。如果服務已經停止,又地呼叫了startService,將重新開啟一個新的IntentService。

  1. 不建議通過bindService()啟動IntentService
    如果採用bindService()啟動IntentService的宣告週期:

onCreate() ->> onBind() ->> onunbind()->> onDestory()
即,並不會呼叫onStartonStartCommand,所以不會將訊息傳送給訊息佇列,那麼onHandleIntent將不會呼叫。即無法實現多執行緒的操作。

這裡有一個問題,還記得我們的例子嘛?我們連續呼叫了三次Service,onHandlerIntent確實呼叫了三次,但是onDestroy只調用了一次,在原始碼中,我們看到,在handleMessage方法中,在呼叫完onHandlerIntent方法後,立馬呼叫了stopSelf(int)方法,為什麼IntentService沒有立馬銷燬,呼叫onDestroy,而是繼續處理呢?

還記得public int onStartCommand(@Nullable Intent intent, int flags, int startId)方法嗎?這裡的startId和stopSelf(int startId)裡的引數是一一對應的,如果stopSelf(int startId)後,如果onStartCommand成對呼叫後,service就會被銷燬(onDestroy方法就會被呼叫),否則Service一直處於執行狀態。

在呼叫stopSelf(int startId)的過程中,系統會檢測是否還有startId存在,如果存在,則不銷燬service,否則銷燬service,當onStartCommand和它成對的時候,service才會被銷燬,當然如果你直接呼叫stopSelf(-1),那麼將直接銷燬service,系統就不會檢測是否還有其他的startId存在。因此,如果直接呼叫stopSelf(),它會呼叫stopSelf(-1),就會立即銷燬service。

參考:https://www.jianshu.com/p/8a3c44a9173a