IntentService源码分析

来源:互联网 发布:智慧的云计算 pdf 编辑:程序博客网 时间:2024/06/01 18:00

前言

在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()方法。

@Overridepublic 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()方法。

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

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

@Overridepublic 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()方法。

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

参考

  • Android 7.1.1 (API level 25)
  • https://developer.android.com/reference/android/app/IntentService.html
  • https://developer.android.com/guide/components/services.html
原创粉丝点击