IntentService面試必備

面試中經常被問到intentservice,在這裏稍微總結一下,以備不時之需。

IntentService與Service的區別

1、IntentService是繼承並處理異步請求的一個抽象類,在IntentService內有一個工作線程來處理耗時操作,需要實現onHandleIntent方法,該方法也是在主線程回調的方法。
2、啓動IntentService的方式和啓動傳統的Service一樣,同時,當任務執行完後,IntentService會自動停止,而不需要我們手動去控制或stopSelf()。
3、IntentService可以啓動多次,而每一個耗時操作會以工作隊列的方式在IntentService的onHandleIntent回調方法中執行,並且,每次只會執行一個工作線程,執行完第一個再執行第二個,以此類推。

啓動intent service的方式

我們知道Service可以通過startService和bindService這兩種方式啓動,IntentService是繼承自Service的,自然也是可以通過上面兩種方式啓動。但是呢,是不建議使用 bindService 去啓動的。爲什麼呢?我們看下IntentService的源碼:

  @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    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);
        }
    }

通過bindService的方式啓動,如果IntentService沒有啓動過,確實會走onCreate方法,但是不會走onStart 方法,也就不會去發送Message 通知內部ServiceHandler 去處理消息。因爲只有handleMessage方法調用後纔會調用onHandleIntent方法去處理任務。

所以正確使用IntentService的方式:

我們應該使用startService的方式啓動IntentService。並且通過源碼我們知道,處理異步任務是在onHandleIntent中的。所以我們應該講任務邏輯放在onHandleIntent中處理。

源碼分析:
先看看 oncreate 中的代碼:

 @Override
    public void onCreate() {
        // TODO: It would be nice to have an option to hold a partial wakelock
        // during processing, and to have a static startService(Context, Intent)
        // method that would launch the service & hand off a wakelock.

        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();

        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }

在onCreate裏會開啓一個線程,這個線程HandlerThread,
看下這個HandlerThread是個啥
看註釋

/**
 * Handy class for starting a new thread that has a looper. The looper can then be 
 * used to create handler classes. Note that start() must still be called.
 */

HandlerThread就是擁有一個Looper的Thread
看它的Run方法


    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    

HandlerThread的其他方法
getLooper獲得當前線程的Looper對象

/**
     * This method returns the Looper associated with this thread. If this thread not been started
     * or for any reason isAlive() returns false, this method will return null. If this thread
     * has been started, this method will block until the looper has been initialized.  
     * @return The looper.
     */
    public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

先判斷線程是否還在, 如果線程不是存活的,則直接返回null
如果線程創建完成後,looper還沒有創建成功,則等待,looper 創建成功後通知線程,實現同步調用,

HandlerThread+Handler構建成了一個帶有消息循環機制的異步任務處理機制。因此開發者就可以將異步任務封裝成消息的形式發送到工作線程中去執行了
接下來繼續回到 intent service 看一下instart方法

@Override
    public void onStart(Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

該方法中通過mServiceHandler獲得一個消息對象msg,然後將startId作爲該消息的消息碼,將異步任務請求intent作爲消息內容封裝成一個消息msg發送到mServiceHandler消息執行者中去處理,這由回到我剛開始貼的ServiceHandler源碼中,

  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);
        }
    }

最後就是調用onHandleIntent 方法處理程序員的耗時操作了。所以我們必須實現onHandleIntent方法,他也是IntentService中唯一一個抽象方法

到此,分析完畢

總結:

  1. 子類需繼承IntentService並且實現裏面的onHandlerIntent抽象方法來處理intent類型的任務請求。
  2. 子類需要重寫默認的構造方法,且在構造方法中調用父類帶參數的構造方法。
  3. IntentService類內部利用HandlerThread+Handler構建了一個帶有消息循環處理機制的後臺工作線程,客戶端只需調用Content#startService(Intent)將Intent任務請求放入後臺工作隊列中,且客戶端無需關注服務是否結束,非常適合一次性的後臺任務。比如瀏覽器下載文件,退出當前瀏覽器之後,下載任務依然存在後臺,直到下載文件結束,服務自動銷燬。
    只要當前IntentService服務沒有被銷燬,客戶端就可以同時投放多個Intent異步任務請求,
  4. IntentService服務端這邊是順序執行當前後臺工作隊列中的Intent請求的,也就是每一時刻只能執行一個Intent請求,直到該Intent處理結束才處理下一個Intent。因爲IntentService類內部利用HandlerThread+Handler構建的是一個單線程來處理異步任務
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章