IntentService意图服务 源码解读

基本概念

IntentService作用

IntentService是Service类的子类,常用来在后台执行耗时的异步请求。我们不用去关心Service的创建和销毁的细节。也不用单独开线程,只管处理自己的任务,处理完过后系统会自动销毁该服务,启动IntentService的方式和普通Service相同,但是使用起来却极其简单。使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//自定义一个IntentService
public class MyService extends IntentService {

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

@Override
protected void onHandleIntent(Intent intent) {
String url=intent.getStringExtra("url");
//在这处理你的任务
//...
}
}


//启动IntentService
Intent intent= new Intent(this,MyService.class);
intent.putExtra("url","......");
startService(intent);

IntentService疑问

看完上面的基本概念后,你可能会产生如下疑问。

  • IntentService构造方法传的那个参数是来干嘛的
  • IntentService内部是怎么工作的?
  • IntentService怎么做到任务结束后自动销毁?

让我们带着疑问,再次去源码里探索吧。

初识API

HandlerThread

初看这个名字,心里在想,难道是内部自带Handler的线程?笔者怀着好奇的心,点开了HandlerThread这个类。因为HandlerThread继承于Thread,于是反射性的找到了run方法里面的源码进行研究,源码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 @Override
public void run() {
mTid = Process.myTid();
Looper.prepare();//初始化Looper

synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);

onLooperPrepared();
Looper.loop();//开启Looper无限循环
mTid = -1;//这句话永远执行不到,除非Looper被退出
}

果然如此,很常规的Looper初始化。有Looper,肯定会有Handler,不然Looper岂不是浪费了。但是在Looper.prepare();Looper.loop()之间,并没有看到Handler的踪影。笔者心想,莫非谷歌用了什么黑科技用法,于是快速把源码过了一遍。WTF!Handler呢?并没有找到啊。既然没有,干嘛叫HandlerThread,这名字取的只给1分。于是查看了API说明。说明如下:

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.
大意:这个类用于创建一个拥有Looper的线程。这个Looper可以用来创建Handler类,虽然如此,start()仍然必须调用。

看到这里算是明白了,原来这个线程只是用来提供Looper的啊,以免我们在子线程中使用Handler过于麻烦。必须调用start是因为,Looper的初始化在run方法内。使用示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
//启动带Looper的线程
HandlerThread handlerThread=new HandlerThread("handlerThread");
handlerThread.start();

//初始化Handler
Handler handler=new Handler(handlerThread.getLooper()){
@Override
public void handleMessage(Message msg) {
// 在这里接收消息,这里的处理是在子线程中执行的。
}
};

handler.sendMessage(..)//发送消息

笔者心想,由于线程的不稳定性,handlerThread.getLooper()会不会取到个空值啊。点进去一看,瞬间觉得自己想多了。可以看到,如果为空就将线程挂起等待,即时你手动唤醒,还有个while循环进行保障。

1
2
3
4
5
6
7
8
9
10
11
12
13
public Looper getLooper() {
//...
//省略部分源码
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}

当Looper初始化完毕,就会唤醒等待的线程,唤醒方法就在run方法中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public void run() {
//...
//省略部分源码
Looper.prepare();//初始化Looper

synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();//唤醒等待线程
}
//...
//省略部分源码
Looper.loop();//开启Looper无限循环

}

可以看出handlerThread.getLooper()是个阻塞方法。

工作原理

IntentService的用法,前面已经介绍过了,现在开始来梳理流程。
我们知道,只需在onHandleIntent里面编写耗时任务,系统就会自动开启线程去处理,处理完毕后,自动销毁Service。这一过程,尤其显得格外人性化。

那么,IntentService内部流程是怎么样的?IntentService继承与Service,那我们就按照Service的生命周期来一步一步进行探索,由于源码比较简单,这里就快速过一遍。

onCreate

1
2
3
4
5
6
7
8
9
10
11
@Override
public void onCreate() {
super.onCreate();
//启动带Looper的线程
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();

//初始化Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}

可以看出,用了HandlerThread,同时也初始化了一个Handler。看完这里。我想你心里一定有谱了。大约明白了内部工作原理。通过HandlerThread,利用Handler发送消息转移到子线程中,然后处理任务。

onStartCommand

为了验证我们的猜想。继续阅读源码,找到onStartCommand

1
2
3
4
5
6
7
 @Override
public int onStartCommand(Intent intent, int flags, int startId) {

onStart(intent, startId);

return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

可以看出,内部调用了onStart

1
2
3
4
5
6
7
@Override
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}

onStart内部,终于验证了我们的猜想。将intentstartId,发送到了子线程中。于是我们找到Handler内部的handleMessage方法一探究竟。

Handler内部的handleMessage

1
2
3
4
5
6
7
8
9
10
11
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为什么不需再开线程。也明白了为什么可以自动销毁Service。
但是,机智的你可能心里在嘀咕。那个HandlerThread呢?里面是个死循环,不能自动停止,一直占有着资源怎么办?那么。我们再来看一下Service的最后一个生命周期。

onDestroy

1
2
3
4
@Override
public void onDestroy() {
mServiceLooper.quit();
}

可以看出,在销毁的同时也退出了Looper循环。

最后

  • IntentService构造方法传的那个参数是来干嘛的
    我们先来看一下IntentService的构造函数。可以看出最终传给了HandlerThread。而给Thread传一个名字更加便于调试,不然线程的默认名是"Thread-" + id;,不方便调试。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public IntentService(String name) {
    super();
    mName = name;
    }


    @Override
    public void onCreate() {
    //..
    //省略了部分源码
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();
    //..
    //省略了部分源码

    }
  • 为什么不在onHandleIntent中退出Looper?
    那是因为跟普通Service一样,IntentService也可以启动多次。如果在结束服务之前又有新任务提交过来了,stopSelf(int startId)并不能结束服务。因为startId必须与最后一次启动相同时才会结束服务。所以在onDestory中退出更加合适。