Forver.微笑

面带微笑未必真的开心,但笑起的那一刻,心中的那些不开心的事已经不重要了~

0%

Handler基础

Handler是由Android提供的,为了解决应用多线程之间(主要是UI线程和子线程)的通信问题(子线程不能更新UI,而UI不执行耗时操作,否则会导致ANR)的一套消息处理机制。
虽然现在出现了很多框架替代Handler的通信方式,比如EventBus,RxJava,AsynTask等,实际上底层仍然是对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
class MainActivity : AppCompatActivity() {
val handler = MyHandler(this)
var messageCount=0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

btn_send_message.setOnClickListener {
MyThread(handler,messageCount++).start()
}
}
}

class MyThread(var handler: MyHandler,
val messageCount:Int) : Thread() {
override fun run() {
super.run()
try {
sleep(1000)
val message = handler.obtainMessage()
message.what = 1
message.obj = "apple $messageCount"
handler.sendMessage(message)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}

class MyHandler(activity: Activity) : Handler() {
private var mRef: WeakReference<Activity>? = null

init {
mRef = WeakReference(activity)
}

override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
when (msg?.what) {
1 ->
mRef?.get()?.let {
Toast.makeText(it, "get messege ${msg.obj.toString()}", Toast.LENGTH_SHORT)
.show()
}

}
}
}

Handler原理

4个重要角色

  • Handle:主要负责发送和处理消息,运行需要底层的MessageQueue和Looper支撑。
  • Message:是要传递的消息,是Handler接收和处理的对象。
  • MessageQueue:是指消息队列,名为队列实际采用单链表实现,是存放消息的容器
  • Looper:以先进先出的方式管理MessageQueue,将发来的消息交按时间顺序交给MessageQuene存放,并将消息取出来给Handler处理。

线程通信过程

主线程中(ThreadActivity的main方法),系统已经初始化了一个Looper对象,因此直接创建Handler即可。就可以通过Handler发送消息处理消息。
程序自己启动的子线程,必须自己创建Looper对象,并启动它,通过Looper.prepare方法(注意保证每个线程只能有一个Looper对象);通过Looper.loop方法启动Looper,通过一个死循环不断的查询MessageQueue中的消息给Handler处理。
Handler负责发送消息给MessageQueue,处理消息。

Handler,Looper,MessageQueue的数量关系

  • 每个线程有且只有一个Looper对象
  • 每个Looper对象有一个MessageQueue
  • Handler和Looper没有数量上的对应关系,Handler对象创建时,如果存在主线程,会重用主线程的Looper,同一个handler可以在多个子线程发消息。

Handler使用中常见问题

Handler内存泄漏问题

Handler的内存泄漏一般是由于非静态内部类引用外部类引起的。因此:

  • 可以通过将Handler定义为静态内部类,并且通过弱引用持有外部类方式避免内存泄漏。

原因:在Java中,非静态内部类或者匿名内部类会隐式的持有外部类。而Looper的生命周期很长,Activity结束时,仍被引用导致内存泄漏。

  • 在onDestroy中调用handler的removeCallbacksAndMessages方法,清除消息队列里的所有消息。
  • 在类外定义Handler类

为什么子线程创建Handler会抛出异常

  • 主线程中系统已经初始化了Looper对象,可以直接创建Handler。
  • 子线程需要自己初始化Looper对象并启动后才能创建Handler对象。

Handler 里藏着的 Callback 能干什么?

在 Handler 的构造方法中有些要求传入 Callback ,那它是什么,又能做什么呢?
看一下 Handler.dispatchMessage(msg) 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public void dispatchMessage(Message msg) {
//这里的 callback 是 Runnable
if (msg.callback != null) {
handleCallback(msg);
} else {
//如果 callback 处理了该 msg 并且返回 true, 就不会再回调 handleMessage
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
复制代码

可以看到 Handler.Callback 有优先处理消息的权利 ,当一条消息被 Callback 处理并拦截(返回 true),那么 Handler 的 handleMessage(msg) 方法就不会被调用了;如果 Callback 处理了消息,但是并没有拦截,那么就意味着一个消息可以同时被 Callback 以及 Handler 处理

猜想:可以利用 Callback 这个拦截机制来拦截系统的Handler消息。

delay的Message会不会阻塞非delay的Message

不会,因为Messsage的参数when是根据当前时间和delay值计算好的一个long值,在插入队列时已经按when的值对MessageQueue按时间进行了排序

主线程Looper为什么不允许退出

查看源码可知,主线程Looper对象是由主线程创建的,创建时配置了不允许退出,退出时会抛出异常。

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
class Handler{
public static void prepareMainLooper() {
prepare(false);
...
}

private static void prepare(boolean quitAllowed) {
...
sThreadLocal.set(new Looper(quitAllowed));
}

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

public void quit() {
mQueue.quit(false);
}
}

class MessageQueue{
void quit(boolean safe) {
//主线程mQuitAllowed值为false,quit时会抛出异常
if (!mQuitAllowed) {
throw new IllegalStateException("Main thread not allowed to quit.");
}

synchronized (this) {
if (mQuitting) {
return;
}
mQuitting = true;

if (safe) {
removeAllFutureMessagesLocked();
} else {
removeAllMessagesLocked();
}

// We can assume mPtr != 0 because mQuitting was previously false.
nativeWake(mPtr);
}
}
}

Handler相关优化

创建Message实例的最佳方式

由于 Handler 极为常用,所以为了节省开销,Android 给 Message 设计了回收机制,所以我们在使用的时候尽量复用 Message ,减少内存消耗。
有2种方法:

  1. 通过 Message 的静态方法 Message.obtain(); 获取;
  2. 通过 Handler 的公有方法 handler.obtainMessage();

原生关键代码

Handler.java

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
53
54
55
56
57
58
59
60
61
public class Handler {
/**
* 消息分发
* 1.如果Message自带回调,由Message处理消息
* 2.如果Handler对象设置了回调,由Handler的回调处理消息
* 3.如果没有设置任何回调,由Handler的handleMessage处理消息
*/
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

public Handler() {this(null, false);}
public Handler(Callback callback) {this(callback, false);}
public Handler(Looper looper) {this(looper, null, false);}
public Handler(Looper looper, Callback callback) {this(looper, callback, false);}
public Handler(boolean async) {this(null, async);}

public Handler(Callback callback, boolean async) {
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());
}
}

//获取Looper对象
mLooper = Looper.myLooper();
//非主线程没有调用Looper.prepare(),则抛出异常
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread " + Thread.currentThread()
+ " that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;
}

public Handler(Looper looper, Callback callback, boolean async) {
mLooper = looper;
mQueue = looper.mQueue;
mCallback = callback;
mAsynchronous = async;
}
//需要子类实现
public void handleMessage(Message msg) {
}

//系列发送,移除消息方法不一一列举,都是操作MessageQueue存取消息,发送或者移除的方法,最终都会由同一个方法最终实现。
}

Looper.java

Looper的方法都是静态方法,Looper的构造方法是私有的,开发者不能自己创建。
prepare():创建Looper对象

loop():

  1. 获取到Looper对象后, 获取对应的消息队列,
  2. 开启消息循环,通过queue.next从队列取消息,
  3. 通过msg.target.dispatchMessage(msg)将消息分发给Handler。

这2个方法注意顺序,先创建Looper对象,再开启消息循环。
prepareMainLooper():开发者不要自己调用,在ActivityThread的main方法调用,为主线程创建Looper对象,并开启消息循环。

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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public final class Looper {
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

final MessageQueue mQueue;

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

public static void prepare() {
prepare(true);
}

private static void prepare(boolean quitAllowed) {
//每个线程只能有一个Looper对象,调用prepare会使sThreadLocal.get()==null
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//准备好消息队列
sThreadLocal.set(new Looper(quitAllowed));
}

public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

public static Looper getMainLooper() {
synchronized (Looper.class) {
return sMainLooper;
}
}

public static void loop() {
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;

boolean slowDeliveryDetected = false;

for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}

final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;
final long dispatchEnd;
try {
msg.target.dispatchMessage(msg);
dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
} finally {
}
msg.recycleUnchecked();
}
}
}

Message.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public final class Message implements Parcelable {
public int what;
public int arg1;
public int arg2;
public Object obj;
public Messenger replyTo;
public int sendingUid = -1;
long when;
Bundle data;
Handler target;//sendMessage时绑定handler,无需开发者处理
Runnable callback;
Message next;

//以下是处理消息池相关的变量,实现消息重用,避免资源浪费
/** @hide */
public static final Object sPoolSync = new Object();
private static Message sPool;
private static int sPoolSize = 0;
private static final int MAX_POOL_SIZE = 50;
private static boolean gCheckRecycle = true;
}

ThreadLocal

用来保存数据,每个线程之间不影响。即使在不同的线程中使用同一个ThreadLocal对象,在不同的线程中操作互不影响。(注意主线程也是不同的线程,主线程设置的值,在非主线程也无效)
不同线程的Looper对象通过ThreadLocal来保持彼此独立。

扩展知识:更新UI的4种机制

通过查看源码看到,最终都是交给handler做最终的更新处理

1
2
3
4
view.post(runnable)
handler.post(runnable)
runOnUiThread(runnable)
handler.sendMsg(runnable)

handler.post和handler.sendMessage

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
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

public final boolean sendMessage(Message msg)
{
return sendMessageDelayed(msg, 0);
}

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
if (delayMillis < 0) {
delayMillis = 0;
}
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
MessageQueue queue = mQueue;
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);
}

view.post(runnable)

1
2
3
4
5
6
7
8
9
10
11
public boolean post(Runnable action) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null) {
return attachInfo.mHandler.post(action);
}

// Postpone the runnable until we know on which thread it needs to run.
// Assume that the runnable will be successfully placed after attach.
getRunQueue().post(action);
return true;
}

Activity.runOnUiThread

1
2
3
4
5
6
7
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}

参考: