Handler源码分析

不太喜欢长篇大论和裹脚布般的源码分析,经常看到很多博主冷不丁的就冒出来几个新词汇,然后一股脑就钻到代码细节上,搞的人很懵逼,习惯性的吐槽和无可奈何的强迫式专研,终才搞懂。但是即便懂了,再回头看原先的部分文章,还是忍不住吐槽。直到某一天,鸡排大佬的一句:”你行你上” 点醒了我,于是乎,就开始自己来折腾。

1
不仅仅只做源码分析和流程总结,最好是把核心流程动手敲一遍,结合设计模式思考这样设计的优缺点

首先,假装我们什么都不懂。。

准备工作:

1.Handler:线程之间的交互
2.Message :消息
3.MessagePool:缓存消息和回收
4.Looper: 为线程提供驱动,从MessageQueue中提取消息
5.MessageQueue :消息存储单元,名为队列,实则单链表
6.ThreadLocal:以当前线程作为key,looper作为value存储,可以理解成是Map,但是内部实现是table数组
6.消费者生产者模式
7.多线程常识
8.队列,链表
9.基本常识
a.UI线程就是主线程
b.主线程默认可以使用handler是因为ActivityThread的main方法中就为主线程提供了looper。

我们以 “问题<==>Demo”的形式作为切入点,来进行源码分析:

问题1:主线程和子线程为毛要进行交互呢,交互场景是什么呢??

常识1:

Android内,你能写的绝大多数的代码(除了开线程或者用到三方库内部开了线程外)都是在主线程内,但是主线程不能做过多的耗时操作,不然轻则卡顿(The application may be doing too much work on its main thread),重则产生ANR
image

理论上Activity是5秒,广播则十秒,但是实际经测试发现,具体时间是跟系统版本和手机有关,另外实测5.0以上的模拟器貌似出现不了这种弹窗。不知道为毛。
image

小插曲:记得有次面试,面试官问我,主线程能否做耗时操作,答之不能,那货强行说可以,耗时是抽象词汇,5秒也算耗时。无语~

不过其实这里也可以扩展到内存优化的知识点,暂时就不跑偏了。继续刚才的问题,交互场景

既然主线程不能做耗时操作,要分流一部分工作交给子线程,例如联网请求,子线程得到值,赋值给控件时,就会产生下列问题:

Demo2

1
2
3
4
5
6
7
new Thread(new Runnable() {
@Override
public void run() {
button.setText("button");
Toast.makeText(MainActivity.this, "已点击", Toast.LENGTH_LONG).show();
}
}).start();

报错信息:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

这段报错检验,实际上来自于ViewRootImpl的checkThread()方法。这里暂时不做扩展

1
2
3
4
5
6
7
 void checkThread() {
if (mThread != Thread.currentThread()) {
throw new CalledFromWrongThreadException(
"Only the original thread that created a view hierarchy can touch its views.");
}
}
报错信息说明,只能在主线程做UI更新操作

这里就产生了几个问题
1.为什么不能在子线程做UI更新操作?
2.就想在子线程创建handler,做ui更新操作不行吗?

1.UI线程是单线程模型,多线程并发访问UI,会导致UI不可预期,如果加锁机制,一方面逻辑很复杂,另外一方面降低UI效率,同时锁机制可能会造成线程阻塞

2.不能在其他线程创建handler,没有调用Looper.prepare()方法

Can’t create handler inside thread that has not calle Looper.prepare()

Demo3

随手Google一番,查阅到几个更新UI的方法
1.runOnUiThread()
2.View.post(Runnable r)
3.handler

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
//方法一:   
@Override
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
//mUiThread的赋值是Activity在attach()时获取当前线程,那肯定就是主线程(UI线程)。
mHandler.post(action);
} else {
action.run();
}
}

//方法二:
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
//mAttachInfo在View的dispatchAttachedToWindow()中赋值
//在dispatchDetachedFromWindow()中置空
//所以肯定不为null,会走if内部的方法,实际上还是handler.post()
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}
getRunQueue().post(action);
return true;
}

//方法三
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {//结束
switch (msg.what) {
case 1:
button.setText(String.valueOf(msg.obj));
break;
default:
}
super.handleMessage(msg);
}
};

@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = Message.obtain();//切入点2
message.what = 1;
message.obj = "button";
handler.sendMessage(message);//切入点1
}
}).start();//start()才是开启线程,run()只是一个普通的方法,run执行完才代表结束。
}

方法一和方法二内部是在调用mHandler.post()。而三者内部实际上都是在调用sendMessageAtTime(),也就是说这三种实际上都是同一种。都是采用的handler机制。为了让主线程和子线程进行通信

这里又会引发出几个问题
1.Java的多线程通信是怎么做的?
2.Android为何不沿用Java的多线程通信方式,而偏偏要用handler处理?
3.具体的交互流程是什么?这样设计有什么好处?

结合入口的sendMessage()和出口的handleMessage()就可以推导出整个handler内部的流程

逆推法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public boolean sendMessageAtTime(Message msg, long     uptimeMillis) {
MessageQueue queue = mQueue;//来自于构造方法Looper.myLooper()
if (queue == null) {
RuntimeException e = new RuntimeException(
this + " sendMessageAtTime() called with no mQueue");
Log.w("Looper", e.getMessage(), e);
return false;
}
return enqueueMessage(queue, msg, uptimeMillis);
}

public Handler(Callback callback, boolean async) {//Handler的空参构造是有调用这里的
if (FIND_POTENTIAL_LEAKS) {
final Class<? extends Handler> klass = getClass();
if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
(klass.getModifiers() & Modifier.STATIC) == 0) {
Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
klass.getCanonicalName());
}
}

mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

public Handler() {
this(null, false);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;//message存到MessageQueue时 会做一个标记
return queue.enqueueMessage(msg, uptimeMillis);//这里的方法就是在对Message进行排队
}

for (; ; ) {
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
if (needWake && p.isAsynchronous()) {
needWake = false;
}
}
msg.next = p; // invariant: p == prev.next
prev.next = msg;

从这里可以看出几点

1.Message会做个标记target,然后存入到MessageQueue中。
2.一个MessageQueue对应一个looper()。

1
2
3
  public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}

3.Looper是从sThreadLocal中得到的。

1
2
3
4
5
6
 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));
}

这里就有一个疑问,为什么要有ThreadLocal这个类?
因为ThreadLocal不仅仅用于looper机制,而且还有activityThread,以及AMS等。如果不用ThreadLocal的话,就需要用一个全局的哈希表,而且还要对应的manager类来进行管理,就比较麻烦。
另外一方面,可以从设计者的角度来考虑,有一大好处
处理存取工作,抽取到专门的类中处理,无论是解耦还是复用性。

4.在looper的prepare()方法中,set了一个looper.

1
2
3
4
5
6
7
8
 public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

5.ThreadLocal的set方法,实际上就是得到一个ThreadLocalMap,以当前线程作为key,值作为value,进行存储。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 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();
}

private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}

切入点2:Message.obtain();消息是如何产生的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
 public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}

public void recycleUnchecked() {
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;

synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}

从这里就可以分析出整个流程了

1.我们都知道程序的入口是ActivityThread的main()方法,其中调用了loop.prepareMainLooper(),为主线程提供一个looper,并且把looper和当前线程Thread存放到ThreadLocal中
2.从handler的构造中拿到Looper,而这个looper是从ThreadLocal.get获取到的
3.message.target实际上就是把message和handler相绑定,一起存入到MessageQueue中,一个Looper对应一个MessageQueue。
4.在MessageQueue中进行排列

入口方向就可以分析到这里

那么出口handleMessage又是如何得到的Message的?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public void handleMessage(Message msg) {//是由dispatchMessage()在调用
}

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

而dispatchMessage()则是在Looper的loop()方法中

1
2
3
4
5
6
7
final MessageQueue queue = me.mQueue;
for (;;) {//死循环获取数据
Message msg = queue.next();
if (msg == null) {//如果消息没有了,就return出这个死循环
return;
}
msg.recycleUnchecked();//消息被回收

Looper的loop()又是在ActivityThread中被调用,从而完成了整个流程

这就是handler的一个流程分析。

image

当然也涉及到非常多细节部分
1、ThreadLocal是如何保证当前线程就是
2、MessagePool是如何操作Message的
3、MessageQueue是如何存放Message的
4、looper又是如何取Message的
5、即便是整个流程知道了,那对我们开发又有什么好处?

Demo6

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
 @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
new Thread() {
@Override
public void run() {
button.setText("111");
}
}.start();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
new Thread() {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
button.setText("111");
}
}.start();
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button = findViewById(R.id.button);
button.setOnClickListener(this);
}

@Override
public void onClick(View v) {
new Thread() {
@Override
public void run() {
button.setText("111");
}
}.start();
}

第一个demo没有报错,第二个和第三个demo报错了

MessageQueue用到了生产者和消费者的设计模式。因为N多子线程 各自自带的消息数量都不一样

仿写handler源码

这里只是初步仿写,完成一个主架构
核心在于两点
1:Message利用handler发送消息
2:MessageQueue的生产者消费者模式
后续会逐步完善
还需要对多线程,数据结构以及设计模式再熟悉一下,然后重新回顾一下handler

完全理解handler,除了一些基本流程之外,还需要知道
关于无限循环的阻塞问题。
Messagequeue中的for循环问题
单链表和队列问题
handler引起的内存泄漏以及解决办法
非静态内部类隐士持有外部类引用。
弱引用即可==》还涉及到四大引用==》配合线程和线程池以及各大三方库
另外补充一些关于handler的面试题

未完待续。。。

igding wechat
2018 依计行事 持续精进
坚持原创技术分享,您的支持将鼓励我继续创作!