IntentService 和 HandlerThread 的原理

居合子發表於2017-05-19

相信大家在 Android 開發中也用到過 IntentService 和 HandlerThread 這兩個類,那麼我們今天就來分析一下這兩個類。

什麼是 IntentService?

IntentService 是 Service 的一個子類,它的內部有一個 Handler 和 HandlerThread。所以 IntentService 與 Service 最大的不同就是 IntentService 在後臺開啟了一個子執行緒,而 Service 並沒有,它還是在 UI 執行緒裡。

IntentService 通過 Handler 和 HandlerThread 來開啟一個執行緒,那麼我們先來看一看 HandlerThread 的原始碼。

HandlerThread

只貼 HandlerThread 的部分程式碼:

package android.os;

public class HandlerThread extends Thread {
    int mPriority;
    int mTid = -1;
    Looper mLooper;

    public HandlerThread(String name) {
        super(name);
        mPriority = Process.THREAD_PRIORITY_DEFAULT;
    }

    public HandlerThread(String name, int priority) {
        super(name);
        mPriority = priority;
    }

    /**
     * Call back method that can be explicitly overridden if needed to execute some
     * setup before Looper loops.
     */
    protected void onLooperPrepared() {
    }

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare(); //在本執行緒中建立一個 Looper。
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);    //設定執行緒優先順序
        onLooperPrepared();    
        Looper.loop();    //looper 迴圈,開始從 MessageQueue 獲取 messages
        mTid = -1;
    }
}複製程式碼

從程式碼裡可以看到,HandlerThread 繼承於 Thread ,並重寫了 run() 方法。
在 run() 方法裡,通過 Looper.prepare() 建立當前執行緒的 Looper。隨後設定當前執行緒的優先順序,並呼叫 Looper.loop() 使當前執行緒的 Looper 開始迴圈,開始從 Looper 自帶的 MessageQueue 中獲取訊息。所以,HandlerThread 就是一個自帶訊息佇列的 Thread。

使用 HandlerThread 的時候要配合 Handler 一起使用,使用例子如下:

HandlerThread handlerThread = new HandlerThread("Juhezi");
handlerThread.start();    //開啟執行緒,同時也開啟了訊息迴圈
Looper looper = handlerThread.getLooper();    //獲取 HandlerThread 中的 Looper
Handler handler = new Handler(looper);        //通過 Looper 初始化 Handler複製程式碼

這時,通過 handler 傳送的訊息,都會儲存在 handlerThread 中的訊息佇列裡。

如果想讓 handlerThread 退出,只需要執行 handlerThread 的 quit()\quitSafely() 方法,這裡其實是呼叫了 looper 的 quit()\quitSafely() 方法。

IntentService

接下來就開始正式分析 IntentService。

IntentService 中有一個內部類 ServiceHandler,它繼承了 Handler,它的構造方法需要一個 Looper。IntentService 就是這個 ServiceHandler 的例項來進行傳送訊息,處理訊息的。
ServiceHandler 過載的 handleMessage() 中就是對訊息的處理方式,從程式碼可見,它首先執行一個 onHandleIntent() 方法,然後執行 stopSelf 結束 Service(自己)。那個 onHandlerMessage() 是一個抽象方法。在這裡可以得出一個結論,

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);    //結束 Service
    }
}複製程式碼

然後來看 onCreate() 方法,仔細一看,不就是和我們上面講過的 HandlerThread 一個套路嘛:建立一個 HandlerThread,呼叫其 start 方法,然後獲取根據 HandlerThread 的 Looper 建立一個 Handler,不過這裡是它的子類:ServiceHandler。

@Override
public void onCreate() {

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

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}複製程式碼

接著來看 onStart() 方法,很明顯,就是把傳入的 Intent 和 startId 包裝成 message,通過 mServiceHandler 傳送出去,這個時候,你就可以在過載的 onHandlerMessage 中處理這個資訊。
注意:這個過載的 onHandlerMessage 是在工作執行緒中被呼叫的,所以你可以在這裡執行一些耗時任務。

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

通過原始碼,我們還知道,不建議通過 bindService() 的 方式啟動 IntentService,建議使用 startService() 的方式。

最後,我們看一下這個 IntentService 的具體使用方法:

public class TestService extends IntentService {

    private static final String ACTION_FOO = "com.juhezi.test.action.FOO";
    private static final String ACTION_BAZ = "com.juhezi.test.action.BAZ";

    private static final String EXTRA_PARAM1 = "com.juhezi.test.extra.PARAM1";
    private static final String EXTRA_PARAM2 = "com.juhezi.test.extra.PARAM2";

    public static final String TAG = "TestService";

    public TestService() {
        super("TestService");
    }

    public static void startActionFoo(Context context, String param1, String param2) {
        Intent intent = new Intent(context, TestService.class);
        intent.setAction(ACTION_FOO);
        intent.putExtra(EXTRA_PARAM1, param1);
        intent.putExtra(EXTRA_PARAM2, param2);
        context.startService(intent);
    }

    public static void startActionBaz(Context context, String param1, String param2) {
        Intent intent = new Intent(context, TestService.class);
        intent.setAction(ACTION_BAZ);
        intent.putExtra(EXTRA_PARAM1, param1);
        intent.putExtra(EXTRA_PARAM2, param2);
        context.startService(intent);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_FOO.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM1);
                final String param2 = intent.getStringExtra(EXTRA_PARAM2);
                handleActionFoo(param1, param2);
            } else if (ACTION_BAZ.equals(action)) {
                final String param1 = intent.getStringExtra(EXTRA_PARAM1);
                final String param2 = intent.getStringExtra(EXTRA_PARAM2);
                handleActionBaz(param1, param2);
            }
        }
    }

    /**
     * Handle action Foo in the provided background thread with the provided
     * parameters.
     */
    private void handleActionFoo(String param1, String param2) {
        Log.i(TAG, "handleActionFoo: " + param1 + " " + param2);
    }

    /**
     * Handle action Baz in the provided background thread with the provided
     * parameters.
     */
    private void handleActionBaz(String param1, String param2) {
        Log.i(TAG, "handleActionBaz: " + param1 + " " + param2);
    }
}複製程式碼

感謝閱讀。

相關文章