26
2017
09

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的类。具体的代码和注释如下所示:

package com.github.cyc.intentservice;

import android.app.IntentService;
import android.content.Intent;
import android.util.Log;

/** * Created by cyc on 2017/9/24. */

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页面中放置了两个按钮。具体的代码和注释如下所示:

package com.github.cyc.intentservice;

import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

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的请求。

参考

上一篇:写代码的点点滴滴之第四天——实现一个简单的计算器 下一篇:YYCache源码分析(二) - YYDiskCache