前言
相信很多人在面试过程中都被问到过Android中Handler机制的原理,这其中可能还会让我们说说Handler中ThreadLocal的作用。下面,我们就从源码的角度,来逐步剖析Handler-ThreadLocal的关系。
ThreadLocal API说明
先来看下官方API对ThreadLocal初步说明:
This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocalinstances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).
简单翻译解释一下,大概的意思是:这个类提供了一些线程局部变量(thread-local),这些变量不同于线程中其他普通变量可以通过诸如它的get或者set方法获取或设置,它是经过独立初始化过的变量。通常来说ThreadLocal的实例在其类中关联的线程是私有静态存在的。
Each thread holds an implicit reference to its copy of a thread-local variable as long as the thread is alive and the ThreadLocal instance is accessible; after a thread goes away, all of its copies of thread-local instances are subject to garbage collection (unless other references to these copies exist).
只要线程是活动的,并且ThreadLocal实例是可访问的,那么每个线程都会持有这个 thread-local 变量的隐式引用。线程消失后,则这些thread-local变量实例就会被GC(垃圾回收器)回收(除非有其它存在对这些变量的引用)。
开始看到这些可能会很蒙圈,我也很蒙圈,下面就结合Handler对ThreadLocal的使用,来了解ThreadLocal的作用。
我们知道,Handler一般是在我们用到线程的时候结合使用,而在非主线程中使用Handler的时候,我们又需要自己来管理Handler机制中的Looper,这里,我们可以借用Android中常用的IntentService源码,来了解Handler在线程中的使用,这应该算是Handler在线程中比较标准的使用,同时也顺便了解IntentService的使用。
IntentService源码解析
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;
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);
}
}
......
@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);
}
......
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
注:为方便阅读,我省略了一部分代码及注释,留下主要代码
我们看到,在IntentService的onCreate() 方法中新建了一个HandlerThread线程,然后通过这个线程的getLooper() 方法获取当前线程的Looper。我们知道,一个Handler只能对应一个Looper(消息循环体),否则程序将会崩溃,下面就来查看HandlerThread源码,来看它获取Looper方法。
HandlerThread.java
首先看getLooper()方法:
/**
* This method returns the Looper associated with this thread. If this thread not been started
* or for any reason isAlive() returns false, this method will return null. If this thread
* has been started, this method will block until the looper has been initialized.
* @return The looper.
*/
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;
}
通过代码注释,我们可以了解到,该方法返回一个当前线程的Looper,当线程未启动或者其他原因使isAlive()返回false时,这个方法则返回null。而如果当前线程已经启动,则这个方法将被阻塞直到mLooper 初始化完成。
回到IntentService的onCreate方法,可以看到先是HandlerThread启动,获取Looper,所以我们再看线程启动后做了什么,即查看HandlerThread的run()方法:
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
可以发现,mLooper就是通过Looper.myLooper()这里初始化的
由此,我们继续点击查看Looper.myLooper()源码:
Looper.java
随着进一步查看,我们定位到下面的方法:
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
这时,我们终于看到了我们本文的主角ThreadLocal,并且,这个ThreadLocal实例在Looper类里是静态私有的形式存在的, 这就首先印证了上面官方API的第一段话。 所以,通过这个方法,可以知道,线程的Looper是通过调用ThreadLocal.get()方法获取这个Looper实例的。
下面就来看下ThreadLocal.get()方法:
ThreadLocal.java
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
通过阅读代码,我们可以知道,ThreadLocal的get()方法里是通过Map键值对方式获取范型T对象实例(本文中T即为Looper)的,而这里是以当前线程(t=Thread.currentThread() )作为键的。那我们都知道,要想取值必先设置,那这个值什么时候设置进去的呢?回到HandlerThread的run()方法,我们可以看到,在get之前,Looper还调用了prepare()方法,点进去追踪查看,定位到Looper里下面的方法:
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));
}
可以发现,Looper实例就是在这时被ThreadLocal变量设置进去的。同时可以发现,只有在ThreadLocal.get()方法返回为null时才能设置Looper,否则抛出异常程序崩溃,这也就保证了一个线程只能对应一个Looper。下面看这个set方法:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
相应的,我们可以从上面代码看到,set方法用当前线程作为key来映射Looper。
最终,总结上述源码,可以得出以下几个结论,下面结论仅对Handler机制来说
- 一个线程只能对应一个Looper
- Looper是由静态变量ThreadLocal通过Map的形式,以当前线程未key来设置和获取的
延伸: - 在Handler机制中,ThreadLocal用来保存Looper对象
- 同一进程中,ThreadLocal可以保存多个Looper对象,同时因为是以键值对的方式保存的,所以每个线程都能很准确对应到各自的Looper。
延伸阅读理解Handler机制以及IntentService
前面我们通过源码分析出了ThreadLocal的作用及工作原理,下面就来看看它是怎么让Looper与Handler对应起来的。
回到最初的IntentService的onCreate()方法:
@Override
public void onCreate() {
super.onCreate();
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
thread.start();
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
可以看到,通过第9行代码,我们就吧Handler与Looper联系在一起了,如果还不太清楚,可以继续往下追踪,这里不再解释。
在说说IntentService,我们知道,启动Service,首先会执行onCreate()方法,然后是onStart()方法。前面我们已经多次研究了onCreate()方法,它先是启动了一个HandlerThread线程,然后配置了Handler(就是mServiceHandler)与Looper完成了一套完整Handler机制,下面来看onStart()方法:
IntentService.java
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
可以看到,onStart方法直接发送了以Handler消息,那相应的,我们就到ServiceHandler里查看handleMessage方法:
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和stopSelf方法,我们知道,stopSelf方法的作用是停止当前服务的,在看onHandleIntent方法,这是一个抽象方法,需要开发者在实例化IntentService类时实现这个方法,也就是说,这个方法是直接让开发者实现具体功能的。
我们知道,mServiceHandler是在线程(HandlerThread)中的,所以,它的handleMessage也是在线程中运行的,自然onHandleIntent方法也是在线程中完成的。我们也可以看onHandleIntent的方法注释:
/**
* This method is invoked on the worker thread with a request to process.
* Only one Intent is processed at a time, but the processing happens on a
* worker thread that runs independently from other application logic.
* So, if this code takes a long time, it will hold up other requests to
* the same IntentService, but it will not hold up anything else.
* When all requests have been handled, the IntentService stops itself,
* so you should not call {@link #stopSelf}.
*
* @param intent The value passed to {@link
* android.content.Context#startService(Intent)}.
* This may be null if the service is being restarted after
* its process has gone away; see
* {@link android.app.Service#onStartCommand}
* for details.
*/
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
简单翻译就是,这个方法是在工作线程中调用以处理任务,同一时间只处理一个Intent任务,但是这个任务执行独立于其他应用逻辑运行的工作线程上,因此,如果这个任务执行需要较长的时间,它将阻止其他相同的IntentService服务,但是不会保留任何其他内容。当所有的请求都处理完成,则IntentService就会自行停止,因此你不需要在调用stopSelf方法。
综合上述分析,我想我们可以简单的总结下IntentService的使用场景:
IntentService使用场景,首先应该是需要在后台完成的任务,而结合它里面有线程和Handler的配合使用,我们猜想它应该是需要执行耗时任务(通过onHandleIntent方法注释也能说明),并且在执行完任务后立即结束这个后台服务(调用了stopSelf方法)。所以,我们可以想到一些我们经常在app上使用的场景如文件下载、后台计时等等。
好了,关于ThreadLocal和IntentService的分析就到这里,本人菜鸡,文章如有错误,烦请诸位大神多多指出,多多指教。