IntentService的本质是一个带有HandlerThread worker线程的一个Service其特点是运行完之后自动停止,里面有个HandlerThread类型的成员变量,通过这个handler向worker线程发送消息,然后Service在工作线程中做耗时操作。说到这就有疑问了
- Q1) HandlerThread是个什么东西,里面长什么样?怎么工作的?
- Q2) Looper什么是怎么回事,怎么实现的?
- Q3) 一直在提到Looper使用ThreadLocal存储的,那ThreadLocal到底是个什么东西呢?
- Q4) 绕一圈终于回来了,那HandlerThread作为一种拥有Looper的Thread是如何在IntentService中发挥自己的光和热的呢?
看官莫急,接下来我们就详解分解。
1) HandlerThread
ThreadHandler其实就是一个带有Looper的Thread,所以在HandlerThread身上会有一个getLooper()方法。通过getLooper()方法获取线程的looper,使用这个Looper创建Handler,创建的handler可以向Looper发送Message了。另,此时Handler中的handlerMessage()方法就运行在这个worker线程中。
1.1) HandlerThread 中的run方法
@Override
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
onLooperPrepared();
Looper.loop();
}
可以看出在run()方法中做的主要工作是创建Looper,给成员变量mLooper赋值,调用Looper.loop()让Looper开始工作。而且给 mLooper = Looper.myLooper(); notifyAll();加了同步,为什么呢要看getLooper()方法;
1.2) HandlerThread中的getLooper()方法
public Looper getLooper() {
if (!isAlive()) {
return null;
}
// If the thread has been started, wait until the looper has been created.
synchronized (this) {
while (isAlive() && mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
getLooper方法中当mlloer为空的时候就会循环等待直到mLooper不为空才返回,这是防止当run()方法中还没有对mLooper进行赋值,导致获取的mlooper为空。
2) Looper
Looper 主要的功能内部维护这个一个存放Message的消息队列MessageQueue,把Looper和线程存在这绑定关系,一个Looper实例对应一个线程。循环读取消息队列Message中的Message交给Message的target也就是发送这个消息的handler中handler dispatchMessage()方法处理,从而实现线程通讯。
2.1) Looper里面的主要成员变量
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;
final MessageQueue mQueue;
final Thread mThread;
2.2) Looper里面的主要方法
Looper.prepare();
Looper.looper();
Looper.myLooper();
2.3) prepare()
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
- prepar()方法的主要作用就是初始化Looper,并把他存在ThreadLocal里面。
- 每个线程只能创建一个Looper,如果已经创建过就会抛出异常。 Looper的初始化是用构造函数进行的如下
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
2.4) Looper.myLooper();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
见名知意,myLooper()方法很简单就是从ThreadLocal中获取Looper并返回。
2.5) Looper.looper();
looper()方法是从Looper中取出MessageQueue,然后循环读取message,交给message的target处理也就是发送这个消息的handler处理。
public static void loop() {
final Looper me = myLooper();
final MessageQueue queue = me.mQueue;
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
return;
}
}
try {
msg.target.dispatchMessage(msg);
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
msg.recycleUnchecked();
}
}
2.6) 一直先循环为什么不会卡死?
- epoll模型当没有消息的时候会epoll.wait,等待句柄写的时候再唤醒,这个时候其实是阻塞的。
- 消息,你的各种点击事件,所以就会有句柄写操作,唤醒上文的wait操作,所以不会被卡死了。
3) ThreadLocal
ThreadLocal类提供了如下几个方法
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
3.1) get()
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
- 第一句是取得当前线程,然后通过getMap(t)方法获取到一个map,map的类型为ThreadLocalMap。然后接着下面获取到<key,value>键值对,注意这里获取键值对传进去的是 this(也就是当前的ThreadLocal),而不是当前线程t。
- 如果当前还没有通过set()方法设置value的时候会调用setInitialValue()方法设置并返回,值是通过initialValue()方法设置,如果没有设置,则默认返回null;
3.2) set()
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = t.threadLocals;
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
一个线程中可能存在多个ThreadLoacl,因而在Thread里面维护着一个ThreadLocalMap,这个map键就是ThreadLocal,值就是我们存的值。
3.3) 移除ThreadLocal当前线程数据
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
3.4) ThreadLocal 小结
使用ThreadLocal存储的数据,会为每个线程存储一份数据拷贝,比如线程A和线程B都要一个名字叫mData的数据存储在ThreadLocal中,各自存取互不影响。Looper使用ThreadLocal存储,不同的Thread线程对应着不同的Looper,假如每个程序猿都有女朋友,虽然都加女朋友(Looper)但是每个程序猿(Thread)获取的女朋友是不一样的,当然如果一样就麻烦了,不做讨论。
4) IntentService
4.1) 关于IntentService
IntentService里面开启一个worker线程,线程工作跑完之后自动关闭。 IntentService中有一个ServiceHandler内部类
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);
}
}
其本质还是一个Handler,覆盖了Handler中的handlerMessage()方法,交给外面的onHandlerMessage处理。
4.2) onHandleIntent()
IntentService中onHandleIntent()方法运行在worker线程中。
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
- IntentService的onCreate()中创建的了HandlerThread并开始运行,从handlerThread中获取looper创建handler。
- 在onstart()方法中像handler发送消息,最终消息在serviceHandler的OnHandlerIntent()方法中接受处理(处于woker线程)。
结
我们通过四个问题驱动了解了ThreadLocal、Looper、HandlerThread、IntentService。简单的说可以总结为:
- IntentService是一个带工作线程的Service,新建一个IntentService就可以在其onHandleIntent()做耗时操作了,这个方法运行在工作线程中。
- IntentSevice中的工作线程就是使用的HandlerThread,因为HandlerThread中有Looper,可以在主线程中(Service的onStart()方法)中向worker线程发送消息。
- Looper是使用ThreadLocal存储的,Looper里面维护了一个消息队列。一个线程和一个Looper对应,Looper通过looper()方法取出MessageQueue中的Message,并把Message交给handler处理(message.target)
- ThreadLocal 为每个线程中数据存取一份数据,各个线程之间互不影响。