基本概念
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");
}
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
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()){
public void handleMessage(Message msg) {
// 在这里接收消息,这里的处理是在子线程中执行的。
}
};
handler.sendMessage(..)//发送消息
笔者心想,由于线程的不稳定性,handlerThread.getLooper()
会不会取到个空值啊。点进去一看,瞬间觉得自己想多了。可以看到,如果为空就将线程挂起等待,即时你手动唤醒,还有个while循环进行保障。1
2
3
4
5
6
7
8
9
10
11
12
13public 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
14public 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 |
|
可以看出,用了HandlerThread
,同时也初始化了一个Handler
。看完这里。我想你心里一定有谱了。大约明白了内部工作原理。通过HandlerThread
,利用Handler
发送消息转移到子线程中,然后处理任务。
onStartCommand
为了验证我们的猜想。继续阅读源码,找到onStartCommand
。1
2
3
4
5
6
7
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
public void onStart(Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
在onStart
内部,终于验证了我们的猜想。将intent
和startId
,发送到了子线程中。于是我们找到Handler内部的handleMessage方法一探究竟。
Handler内部的handleMessage
1 | private final class ServiceHandler extends Handler { |
看到这里,终于明白了onHandleIntent
为什么不需再开线程。也明白了为什么可以自动销毁Service。
但是,机智的你可能心里在嘀咕。那个HandlerThread
呢?里面是个死循环,不能自动停止,一直占有着资源怎么办?那么。我们再来看一下Service的最后一个生命周期。
onDestroy
1 |
|
可以看出,在销毁的同时也退出了Looper
循环。
最后
IntentService构造方法传的那个参数是来干嘛的
我们先来看一下IntentService的构造函数。可以看出最终传给了HandlerThread。而给Thread传一个名字更加便于调试,不然线程的默认名是"Thread-" + id;
,不方便调试。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public IntentService(String name) {
super();
mName = name;
}
public void onCreate() {
//..
//省略了部分源码
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
//..
//省略了部分源码
}为什么不在
onHandleIntent
中退出Looper?
那是因为跟普通Service一样,IntentService也可以启动多次。如果在结束服务之前又有新任务提交过来了,stopSelf(int startId)
并不能结束服务。因为startId
必须与最后一次启动相同时才会结束服务。所以在onDestory中退出更加合适。