IntentService源碼分析

前言

在Android中,Service是運行在主線程之中的。如果要在Service中執行耗時的任務,那麼我們需要手動創建子線程。並且,在任務完成的時候,我們需要手動停止Service。爲了簡化這些操作,Android提供了一個IntentService類。IntentService是Service的子類,它使用了一個HandlerThread線程來依次處理每個啓動Service的請求,並在所有的請求都處理完畢之後自動停止Service。

基礎知識

IntentService是一個Service,我們需要熟悉Service的生命週期,具體可以閱讀 Service的生命週期 這篇文章。

IntentService內部使用了一個HandlerThread線程,我們需要熟悉HandlerThread的工作原理,具體可以閱讀 HandlerThread源碼分析 這篇文章。

基本用法

首先,我們需要寫一個繼承自IntentService的類,並實現 onHandleIntent() 抽象方法。IntentService沒有默認的構造方法,所以我們還需要聲明一個默認的構造方法。示例代碼如下所示:

public class MyIntentService extends IntentService {

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

    @Override
    protected void onHandleIntent(Intent intent) {
        // execute your task

    }
}

其中,構造方法調用了super()方法,它用來命名IntentService內部的HandlerThread線程名。onHandleIntent()方法是在HanderThread線程中執行的,我們可以直接在該方法中執行耗時的任務。

然後,我們就可以在其它的組件(比如,Activity和BroadcastReceiver)中調用startService()方法來啓動該IntentService了。示例代碼如下所示:

Intent intent = new Intent(this, MyIntentService.class);
startService(intent);

最後,別忘了要在AndroidManifest.xml文件中註冊該IntentService類。示例代碼如下所示:

<application
    ...
    <service
        android:name=".MyIntentService"
        android:exported="false" />
</application>

源碼分析

IntentService是一個繼承自Service的抽象類,它聲明瞭一個onHandleIntent()抽象方法。

public abstract class IntentService extends Service {
    ...

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

首先,我們來看IntentService的構造方法。

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

構造方法必須被子類的構造方法所調用。它保存了一個字符串在mName成員變量中,用來命名IntentService內部的HandlerThread線程名。

當我們在其它的組件(比如,Activity和BroadcastReceiver)中調用startService()方法來啓動Service時,如果Service還沒有創建,那麼系統將創建Service並回調Service的onCreate()方法。所以,接下來我們來看IntentService的onCreate()方法。

@Override
public void onCreate() {
    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

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

在onCreate()方法中,IntentService創建了一個HanderThread線程,並啓動了該線程。接着,它獲取了該線程的Looper,並使用該Looper創建了一個與該線程相關聯的Handler。這樣,IntentService就可以通過該Handler來控制HandlerThread線程執行耗時的任務。

回調onCreate()方法之後,系統將回調Service的onStartCommand()方法。所以,接下來我們來看IntentService的onStartCommand()方法。

@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

onStartCommand()方法調用了onStart()方法。

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

在onStart()方法中,IntentService使用了與HandlerThread線程相關聯的Handler發送了一條Message。因此,其它的組件每次調用startService()方法來啓動Service時,IntentService都會發送一條Message給HandlerThread線程。

HandlerThread線程的Looper從MessageQueue中循環地取出Message進行處理。最終Message是在與HandlerThread線程相關聯的Handler中進行處理的。所以,接下來我們來看與HandlerThread線程相關聯的Handler是如何處理Message的。

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

在handleMessage()方法中,onHandleIntent()方法被調用。因爲該Handler是與HandlerThread線程相關聯的,所以onHandleIntent()方法是在HandlerThread線程中執行的,我們可以直接在該方法中執行耗時的任務。通過Message中Intent,我們可以區分不同的啓動Service請求。因爲Looper是從MessageQueue中循環地取出Message進行處理的,所以IntentService是以隊列的方式來處理多個啓動Service的請求。

在handleMessage()方法的最後調用了Service的stopSelf()方法來自動停止Service。啓動Service的啓動ID被傳遞給stopSelf()方法,以確保處理完最後一次啓動Service的請求之後才停止Service。

在停止Service時,系統會回調Service的onDestroy()方法。所以,最後我們來看IntentService的onDestroy()方法。

@Override
public void onDestroy() {
    mServiceLooper.quit();
}

在onDestroy()方法中,IntentService調用了與HandlerThread線程相關聯的Looper的quit()方法。quit()方法退出了HandlerThread線程的Looper消息循環,結束了HandlerThread線程。

總結

IntentService是Service的子類,它使用了一個HandlerThread線程來依次處理每個啓動Service的請求,並在所有的請求都處理完畢之後自動停止Service。

例子

接下來舉一個簡單的例子來實踐IntentService。項目源碼地址:https://github.com/chongyucaiyan/ServiceDemo

首先,新建一個繼承自IntentService的類。具體的代碼和註釋如下所示:

public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";

    // Action
    public static final String ACTION_DO_TASK1 = "com.github.cyc.intentservice.MyIntentService.do_task1";
    public static final String ACTION_DO_TASK2 = "com.github.cyc.intentservice.MyIntentService.do_task2";

    public MyIntentService() {
        // 指定HandlerThread線程名
        super("MyIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate(), current thread is " + Thread.currentThread().getName());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i(TAG, "onStartCommand(), current thread is " + Thread.currentThread().getName());
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        if (intent != null) {
            Log.i(TAG, "onHandleIntent(), current thread is " + Thread.currentThread().getName());
            if (ACTION_DO_TASK1.equals(intent.getAction())) {
                // 執行任務1
                doTask1();
            } else if (ACTION_DO_TASK2.equals(intent.getAction())) {
                // 執行任務2
                doTask2();
            }
        }
    }

    private void doTask1() {
        Log.i(TAG, "doTask1(), start");

        // 模擬耗時任務
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "doTask1(), end");
    }

    private void doTask2() {
        Log.i(TAG, "doTask2(), start");

        // 模擬耗時任務
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        Log.i(TAG, "doTask2(), end");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy(), current thread is " + Thread.currentThread().getName());
    }
}

我們在onCreate()、onStartCommand()、onHandleIntent()和onDestroy()方法中打印了當前線程的名字。在onHandleIntent()方法中,我們根據Intent的Action來區分具體執行哪個耗時任務。

然後,我們在MainActivity頁面中放置了兩個按鈕。具體的代碼和註釋如下所示:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initContentView();
    }

    private void initContentView() {
        findViewById(R.id.btn_main_start_service1).setOnClickListener(this);
        findViewById(R.id.btn_main_start_service2).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_main_start_service1:
                // 啓動Service
                Intent intent1 = new Intent(this, MyIntentService.class);
                intent1.setAction(MyIntentService.ACTION_DO_TASK1);
                startService(intent1);
                break;

            case R.id.btn_main_start_service2:
                // 啓動Service
                Intent intent2 = new Intent(this, MyIntentService.class);
                intent2.setAction(MyIntentService.ACTION_DO_TASK2);
                startService(intent2);
                break;

            default:
                break;
        }
    }
}

當點擊Start Service1按鈕時,啓動Service執行任務1。當點擊Start Service2按鈕時,啓動Service執行任務2。

最後,別忘了要在AndroidManifest.xml文件中註冊該IntentService類。具體的代碼如下所示:

<application
    ...
    <service
        android:name=".MyIntentService"
        android:exported="false" />
</application>

接下來,我們來運行一下。先點擊Start Service1按鈕,打印的信息如下圖所示:

啓動Service執行任務1打印的信息.png

可以看到,先是創建Service,然後在onHandleIntent()方法中執行任務,最後任務執行完畢自動停止Service。根據打印的線程信息,我們知道onCreate()、onStartCommand()、和onDestroy()方法都是在主線程之中執行的,而onHandleIntent()方法是在HandlerThread線程中執行的。

然後,快速地點擊Start Service1和Start Service2按鈕各一下,打印的信息如下圖所示:

啓動Service執行任務1和2打印的信息.png

可以看到,啓動Service、任務執行、停止Service的過程與上面類似。需要注意的是,任務2是在任務1執行完畢之後纔開始執行的,這驗證了IntentService是以隊列的方式來處理多個啓動Service的請求。

參考

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章