开篇
本文针对流程进行分析,不对源码涉入过深,如有不当,望不吝赐教。
协作图
先来看一张直观的协作流程图,通过此流程图,可以快速的在脑海中形成一个协作关系,本文的结构也是按照这流程图来进行描述。
Handler
常规操作
在我们项目中使用Handler可以说是非常的熟练,得益于Handler的多种构造方式,我们操作Handler可是相当简便,比如我们在Activity中的常规操作:
//1
private Handler handler=new Handler();
//2
private Handler handler0=new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
//....处理对应线程的消息,更新UI等
//如果return true了,那么就不会再去执行Handler中的handleMessage(msg);
return false;
}
});
//3
private Handler handler1= new Handler(){
@Override
public void handleMessage(@NonNull Message msg) {
//....处理对应线程的消息,更新UI等
super.handleMessage(msg);
}
};
紧接着,我们会使用上面构造出来的handler进行发送一个Message消息,最后我们会在上面的handleMessage callback中进行更新UI或者刷新一些信息。(这里笔者对于使用仅仅是一笔带过,不做过多讨论。)
构造函数
根据常用的创建Handler方式,首先来看看给我们提供的构造器
大体分为两大类:
- 一类是不带looper参数,会默认获取当前线程的looper对象。
public Handler() {
this(null, false);
}
public Handler(@Nullable Callback callback) {
this(callback, false);
}
public Handler(boolean async) {
this(null, async);
}
上面的常用构建的Handler方式中,不带looper的public Handler (Handler.Callback callback),和public Handler()在Android API Level 30的版本中已经被打上@Deprecated标记
- 一类是带looper对象参数,要求传入对应线程的looper对象;
public Handler(@NonNull Looper looper) {
this(looper, null, false);
}
public Handler(@NonNull Looper looper, @Nullable Callback callback) {
this(looper, callback, false);
}
**但是值得注意的是:**当我们需要在子线程当中新建一个Handler时,如果我们也像在主线程中那样去操作,此时运行便会得到一个抛错:
"Can't create handler inside thread 'xxx' that has not called Looper.prepare()"
对于这个错误,我们也很熟练的能够解决,没错,听报错的,那就是call Looper.prepare();通常如下:
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Handler mTHandler=new Handler();
Message message=Message.obtain();
message.arg1=1;
mTHandler.sendMessage(message);
//先记住,一定要有loop(),否则子线程中的消息并没有被处理
Looper.loop();
}
}).start();
**为什么呢?**接下来我们就以这个进行切入点,来分析Handler消息机制。
-
1、通过上面介绍的第一类构造函数新建的handler对象,都会将Handler中定义的成员变量:mLooper赋值=Looper.myLooper();从而获取到当前线程对应的looper对象。
-
2、拿到的mLooper进行判空操作,如果是null,那么就会抛出一个运行时异常:xxx that has not called Looper.prepare()"。
-
3、如果上面拿到的mLooper不为null,那么就往下走,将Handler中的成员变量mQueue(消息队列)赋值;mQueue=mLooper.mQueue;同时赋值callback与同步/异步标记位。
明显上面我们在子线程中进行new Handler时,并没有传入looper,那当然是会报错了,但是在activity中,我们不也是这么操作的吗,为何不抛同样的错误?那是因为在主线程中,应用程序启动时,便会在ActivityThread的Main方法中执行了同样的Looper.prepareMainLooper();故程序运行时检查,此时myLooper是不为空的。
Looper
先看上面步骤一:Looper.myLooper()
代码内容很简单,就是从threadlocal中返回当前线程的looper。return sThreadLocal.get();
接着上面的错误解决入口点:Looper.prepare();
最终都是调用到:
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是个什么呢?线程本地变量,先记住:它很重要,就是它实现了线程的切换,实现了关联当前线程的looper。这样一来,在myLooper时获取到的looper就不为空了。
Looper.loop();
loop()是消息运转的关键,只有开启了loop才会处理入队的消息,当loop被调用,将会源源不断的从消息队列中取消息。
在这我以loop运作流程图代替直接上代码,以便清晰明了的理解loop。
MessageQueue
主要提供了消息入队和出队的方法,但是取消息并不是自己来取,而是交给了looper,当Looper开启了loop后,它就会去调用MessageQueue#next()方法,从而回到MessageQueue。
-
enqueueMessage 入队
-
next 出队
很重要的一点:MessageQueue消息队列的实现是Message链表结构的,直观的表示为:Message->Message->Message->null
消息的入队,是按照消息的时间when顺序来进行插入的,在调用enqueueMessage时,主要的步骤如下:
Message
定义了消息对象,消息成员变量,这篇文章暂不作分析。
相互持有关系
-
Handler中持有Looper,Looper#mQueue
-
Looper中持有MessageQueue亦即mQueue
-
Message中持有target->Handler
ThreadLocal
ThreadLocal在消息机制里面可以说非常的重要了,简单理解为:它实现了当前线程和属于当前线程的looper相关联,从而实现了线程的切换。
提供给开发者主要核心的方法
-
public void set(T value)
-
public T get()
写到此发现这套机制要写真的内容太多了,目前也只是先说说这一套大概流程。好了,今天的篇幅先到这吧,对于文中的MessageQueue、Message和ThreadLocal讲得很粗,先记住这几点的关系及协作流程,接下来会再深入进行分析。