2018年终于终于快结束了!这一年发生了很多事,有很多的成长,无论是技术还是人生。2018是很难忘的一年,以此写篇博客,记录过去,期待2019年的美好。希望2019年能有好的开始,完美的结尾!加油加油!
前言
这篇博客来自于整理很早之前的一篇学习笔记,Handler很早之前已经写过一篇文章了,当时的理解可能不是很深,所以后来又深入学习了下,有了一个全新的认识。学习之前先了解一些基础知识点,本篇涉及到ThreadLocal,Handler,Looper,Message,MessageQueue,HandlerThread,IntentService等知识点。
知识点
ThreadLocal:
ThreadLocal是一个线程的数据存储类,ThreadLocal为每个使用该数据的线程提供独立的数据副本,所以每个线程都可以独立改变自己的数据副本,而不会影响其他线程对应的数据,其实也可以理解为在不同的线程间对同一个共享数据的隔离,两种理解角度我觉得都没问题,就看个人怎么理解。
那么ThreadLocal是怎么做到为每个线程提供独立的数据副本的呢?就是利用内部的ThreadLocalMap类,它是ThreadLocal的一个静态内部类,ThreadLocalMap类以当前ThreadLocal实例为key,以数据为value,内部使用一个实体数组实现,数组初始大小为16,随着数据的增多会调用reSize方法对数组扩容,扩容大小为当前数组大小的2倍
static class ThreadLocalMap {
//ThreadLocalMap的内部静态实体类,以ThreadLocal的弱引用为key,以数据为value
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
//ThreadLocal内部以实体数组的方式,为每个线程存储所对应的value值
private Entry[] table;
//ThreadLocalMap的set方法
private void set(ThreadLocal<?> key, Object value) {
...
tab[i] = new Entry(key, value);
//计算需不需要对实体数组扩容
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
...
}
ThreadLocal的set方法就是数据的存储方法,首先会获取当前线程的ThreadLocalMap实例,再调用ThreadLocalMap的set方法存储当前线程对应的值,篇幅原因ThreadLocal的get方法和set方法的实现,可以去百度或查看源码了解一下。
Message:
Message是实现了Parcelable接口的实体类,表示一个消息载体,其中的target变量表示当前发送和要处理此消息的Handler对象
MessageQueue:
MessageQueue消息队列主要有消息的插入读取操作,读取操作往往伴随着删除操作。MessageQueue的内部通过单链表实现,因为单链表的插入和删除有优势。
Handler
对Handler的理解我们从一段代码看起
new Thread(new Runnable() {
@Override public void run() {
Looper.prepare();
handler = new TestHandler();
//do something
handler.sendEmptyMessage(1);
Looper.loop();
//Looper.quit()退出循环后执行
//do something
}
}).start();
上面的代码就是完整的使用Handler的示例,那么Looper.prepare()方法的作用是什么呢,其实就是创建一个Looper实例,并将当前looper实例设置给当前线程的ThreadLocal变量,从判断我们可知,一个线程只能有一个Looper,当然的,也只能有一个MessageQueue(消息队列),相关源码如下:
//Looper的prepare方法
public static void prepare() {
prepare(true);
}
private static void prepare(boolean quitAllowed) {
//一个线程只能有一个Looper对象
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}
//looper的构造方法
private Looper(boolean quitAllowed) {
//在Looper对象初始化时,创建消息队列,Looper在线程中是唯一的,那么消息队列也是唯一的
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
那为什么Handler必须需要Looper呢,Looper在消息处理过程中的作用又是什么呢,其实想想也是,Handler是消息(Message)的处理者,而Looper的作用就是循环的取出消息,交给Handler去处理。示例中Looper.loop()方法的作用就是开启消息循环,源码如下,我们看看,其实就是循环从消息队列中取出一个消息,并把该消息分发到对应的Handler回调中。
public static void loop() {
//获取当前线程的Looper对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
final MessageQueue queue = me.mQueue;
...
//开启消息循环
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// 没有消息时退出本次循环
return;
}
...
try {
//这里msg.target变量就是当前发出这条消息的 Handler,
//这也是为什么有多个Handler时发消息时,只能接受到自己的消息的原因
msg.target.dispatchMessage(msg);
} finally {
}
...
msg.recycleUnchecked();
}
}
明白了消息的循环和消息的分发,我们再看看消息的发送过程,消息的发送就是把消息放置消息队列中,以handler.sendEmptyMessage(1)方法为例,最终会调用MessageQueue的enqueueMessage方法。MessageQueue会根据post delay的时间排序放入到链表中,链表头的时间小,尾部时间最大。因此能保证时间Delay最长的不会block住时间短的。当每次post message的时候会进入到MessageQueue的next()方法,会根据其delay时间和链表头的比较,如果更短则,放入链表头,并且看时间是否有delay,如果有,则block,等待时间到来唤醒执行,否则将唤醒立即执行。
tips:
MessageQueue.IdleHandler可以用来在线程空闲的时候,进行一个操作
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
/**
* 返回值boolean 意思是needKeep
* true,表示要保留保留, 代表不移除这个idleHandler,可以反复执行
* false代表执行完毕之后就移除这个idleHandler, 也就是只执行一次
*/
//do something
return false;
}
});
总结:
学习完Handler的原理,再结合上面的示例,可能有人就有想法了:既然在Looper.loop()方法可以开启一个消息循环,去处理Handler发送的各种消息,那么我们可以把示例作为一个不间断的异步任务呀,有任务处理的时候,就发送一条消息,然后处理相应的任务,处理完调用Looper.quit()退出,哈哈,对的,刚才说的这种异步任务就是接下来要说的HandlerThread。
HandlerThread
HandlerThread继承自Thread,可以用来执行多个耗时操作,而不需要多次开启线程,也就是刚才我们总结中说的那样。HandlerThread的用法如下:
//HandlerThread的用法
HandlerThread handlerThread = new HandlerThread("Test_HandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper()) {
@Override public void handleMessage(Message msg) {
super.handleMessage(msg);
//在此处理耗时任务
post(new Runnable() {
@Override public void run() {
//ui do something
}
});
}
};
handler.sendEmptyMessage(1);
//结束任务
handlerThread.quit();
我们看看HandlerThread的run方法的实现,Android为我们实现了Handler在子线程中的创建。哈哈,是不是和前面Handler中的示例一样。而HandlerThread的退出,其实就是调用Looper.quit()结束Looper的循环,就不贴代码了
@Override
public void run() {
mTid = Process.myTid();
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}
思考:这里我们思考一个面试问题,如果让HandlerThread处理多个任务,那执行过程会是怎样的呢?答案就是任务会放在任务队列中,一个一个的执行。其实细细一想我们就会明白,这里所谓的任务队列就是MessageQueue,就是消息队列,HandlerThread的耗时任务会在Handler的callback中执行,而Handler的callback又运行在Looper的loop方法,只有处理完一个消息(Handler的callback方法执行结束),这里可以说执行完一个任务,才会进行下一次循环,才会取出下一个消息,再次处理。所以我们也就能理解HandlerThread和IntentService处理多个任务时的执行过程。
总结:
看完Handler的示例和HandlerThread的原理,可能有人又有想法了,如果我们在Service中使用Handler的示例和HandlerThread,那不就可以让Service一直循环的后台执行任务了嘛,执行完调用对应方法,岂不美滋滋,是的,这种是接下来要说的IntentService。
IntentService
IntentService继承自Service,内部的实现很简单,使用HandlerThread实现,我们直接读源码并分析其实现
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);
}
}
public IntentService(String name) {
super();
mName = name;
}
@Override
public void onCreate() {
super.onCreate();
//我们在这里可以看到标准的HandlerThread的使用
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);
}
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
//任务结束调用stopSelf方法后,结束HandlerThread
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
//IntentService默认IBinder返回null
return null;
}
//耗时任务的处理方法
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
总结
至此我们就明白了异步消息处理在Android中的应用是有很多场景,其中消息处理这块是重中之重。在阅读Looper的源码的时候,发现一个方法:prepareMainLooper(),字面意思可知,是初始化主线程的Looper,很好奇看了看该方法在android中的调用地方,发现在android中有两个调用,分别是ActivityThread类的main方法和SystemServer类的main方法中,ActivityThread就是我们说的主线程,而SystemServer就是Android的系统进程,相关的详细介绍会放到ActivityThread的理解和APP的启动过程这篇博客中。