目录
  1. 1. Handler
  2. 2. 用法
    1. 2.1. 子线程中创建
      1. 2.1.1. 不同线程下写法不同的原因
        1. 2.1.1.1. Handler 层面
        2. 2.1.1.2. Looper 层面
        3. 2.1.1.3. 子线程创建Looper
        4. 2.1.1.4. 主线程创建Looper
  3. 3. 消息分发
    1. 3.1. 消息入队
    2. 3.2. 消息出队
  4. 4. 拓展
    1. 4.1. Handler 为什么能切换线程
    2. 4.2. 其他子线程更新UI的操作
      1. 4.2.1. Handler.post()
      2. 4.2.2. View.post()
      3. 4.2.3. runOnUiThread()
Handler相关

看了老是忘系列

Handler

用法

1
2
3
4
5
6
7
8
9
10

// 主线程创建
myHandler = new Handler(){
public void handleMessage(Message msg) {
// 处理消息
}
};

// 发送消息
myHandler.sendMessage(msg);

子线程中创建

1
2
3
4
5
6
7
// 子线程创建
new Thread(new Runnable() {
@Override
public void run() {
handler2 = new Handler();
}
}).start();

在子线程中直接创建会抛异常

ErrorMessage
1
Can't create handler inside thread that has not called Looper.prepare()

需要再子线程创建Handler之前 调用 Looper.prepare

1
2
3
4
5
6
7
8
// 子线程创建
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
handler2 = new Handler();
}
}).start();

不同线程下写法不同的原因

Handler 层面

打开Handler的源码 在构造方法中 有一段获取当前的Loop对象的逻辑

1
2
3
mLooper = Looper.myLooper(); if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}

Looper 层面

进到Looper.myLooper() 方法中 可以发现

1
2
3
public static final Looper myLooper() {
return (Looper)sThreadLocal.get();
}
1
static final ThreadLocat<Looper> sThreadLocal = new ThreadLocal<Looper> ();

看 sThreadLocal 的定义可以发现 定义了一个 类型为Looper的 ThreadLocal
ThreadLocal -> 线程本地存储区 (Thread Local Storage, 简称为 TLS) 每个线程都有自己的私有的本地存储区域,不同线程之间彼此不 能访问对方的 TLS 区域
这里get的就是当前线程的Looper 对象
因为在新建线程中没有创建Looper对象所以并不能使用Looper相关功能

子线程创建Looper

创建Looper的方法 在抛出的异常里面也说明了 Looper.prepare()

1
2
3
4
5
6
7
8
9
public static void prepare() { 
prepare(true);
}
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created pe r thread");
}
sThreadLocal.set(new Looper(quitAllowed));
}

prepare有一个无参数和一个有参数的方法。
prepare(boolean quitAllowed) 后面会有说明,从参数名能看出 是控制该Looper是否允许退出的。
我们能创建的都是无参的重载方法
在 prepare(boolean quitAllowed)中 sThreadLocal.get() != null 限制了每个线程只能创建一个Looper对象

主线程创建Looper

子线程中需要手动调用 Looper.prepare() 创建一个Looper对象,主线程中是什么时候创建的?

主线程中不需要自己创建 Looper,这是由于在程序启动的时候,系统已经帮我 们自动调用了 Looper.prepare()方法。
查看 ActivityThread 中的 main()方法

1
2
3
4
5
6
7
public static void main(String[] args) {
..........................
Looper.prepareMainLooper();
..........................
Looper.loop();
..........................
}

其中 prepareMainLooper()方法会调用 prepare(false)方法
也就是该Looper不允许退出。

1
2
3
4
5
6
7
8
9
public static void prepareMainLooper() {
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}

消息分发

1
2
3
4
5
6
7
8
9
// 创建
myHandler = new Handler(){
public void handleMessage(Message msg) {
// 处理消息
}
};

// 发送消息
myHandler.sendMessage(msg);

创建了一个Handler后 通过 sendMessage 发送消息事件,通过 handleMessage 处理事件

消息入队

通过 handleMessage 的调用 调用栈 可以发现

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
sendMessage(Message msg);
-> sendMessageDelayed(msg, 0);
-> sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMill is);
-> enqueueMessage(queue, msg, uptimeMillis);
-> queue.enqueueMessage(msg, uptimeMillis);

// 入口方法
public final boolean sendMessage(Message msg){
return sendMessageDelayed(msg, 0);
}
// 我们平常调用的延时方法
public final boolean sendMessageDelayed(Message msg, long delayMillis){
if (delayMillis < 0) {
delayMillis = 0;
}
// 第一个参数是指发送的消息 msg,第二个参数是指延迟多少毫秒发送 这里要注意一下 SystemClock.uptimeMillis() (计算自开机启动到目前的毫秒数) 不是 System.currentTimeMillis() (自1970年1月1号0时0分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);}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}

通过 sendMessageAtTime 可以发现 消息 Message 对象是建立一个消息队列 MessageQueue, 这个对象 MessageQueue 由 mQueue 赋值,而由源码分析得出 mQueue = mLooper.mQueue,而 mLooper 则是 Looper 对象,我们由上面已经知道,每个线 程只有一个 Looper,因此,一个 Looper 也就对应了一个 MessageQueue 对象, 之后调用 enqueueMessage(queue, msg, uptimeMillis)直接入队
MessageQueue里面维护的只有一个Message,因为Message本身就能构建一个Message链 就像链表中的一个Node节点

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
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {//msg.target就是发送此消息的Handler
throw new IllegalArgumentException("Message must have a target.");
}
if (msg.isInUse()) {//表示此消息正在被使用
throw new IllegalStateException(msg + " This message is already in use.");
}

synchronized (this) {
if (mQuitting) {//表示此消息队列已经被放弃了
IllegalStateException e = new IllegalStateException(
msg.target + " sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();
return false;
}

msg.markInUse();
msg.when = when;//将延迟时间封装到msg内部
Message p = mMessages;//消息队列的第一个元素
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//如果此队列中头部元素是null(空的队列,一般是第一次),或者此消息不是延时的消息,则此消息需要被立即处理,此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//如果此消息是延时的消息,则将其添加到队列中,原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。插入延时消息不需要唤醒Looper线程。

needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;
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;
}

// We can assume mPtr != 0 because mQuitting is false.
if (needWake) {//唤醒线程
nativeWake(mPtr);
}
}
return true;
}

源码中用 mMessages 代表当前等待处理的消息,MessageQueue 也 没有使用一个集合保存所有的消息。观察中间的代码部分,队列中根据时间 when 来时间排序,这个时间也就是我们传进来延迟的时间uptimeMills 参数,之后再根据时间的顺序调用 msg.next,从而指定下一个将要处理的消息是什么,最后判断消息是否要处理,是否要唤醒线程

消息出队

在之前的描述中可以发现 Looper中赋值了 MessageQueue,而Looper.loop 是一个死循环的功能

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
public static void loop() { 
// 获取当前的Looper 对象
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't ca lled on this thread.");
}
// 获取当前要处理的数据
final MessageQueue queue = me.mQueue;

Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
// 消息出队
Message msg = queue.next(); // might block
if (msg == null) {
// 没有消息处理时 退出
return;
}
}
......
// 将真正的处理工作交给 message 的 target,即 handler
msg.target.dispatchMessage(msg);
......
msg.recycleUnchecked();
}

最后消息出队调用了MessageQueue 的 next 方法
最后事件的分发处理调用了 msg.target.dispatchMessage(msg);
msg.target 就是当前创建的Handler 在Handler.enqueueMessage 有 msg.target = this

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
next() { 
//...
for (;;) {
//...
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
//...
if (msg != null) {
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake u p when it is ready.
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// Got a message. mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
return msg;
}
} else {
// No more messages.
nextPollTimeoutMillis = -1;
}
// Process the quit message now that all pending messages hav e been handled.
if (mQuitting) {
dispose();
return null;
}
}
}
}

如果当前 MessageQueue 中存在 mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为 mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。

回到 loop 的 msg.target.dispatchMessage(msg)

1
2
3
4
5
6
7
8
9
10
11
12
public void dispatchMessage(Message msg) { 
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}

可以看到 消息的处理也是有优先级的。
最优先的就是 msg的callback

1
2
3
4
5
6
Message msg = Message.obtain(myHandler, new Runnable() {
@Override
public void run() {

}
});

然后就是Handler的构造方法中的Callback

1
2
3
4
5
6
myHandler = new Handler(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message message) {
return false;
}
});

最后就是重写Handler的CallBack

1
2
3
4
myHandler = new Handler(){
public void handleMessage(Message msg) {
}
};

拓展

Handler 为什么能切换线程

Handler 总是依附于创建时所在的线程,比如Handler 是在主线程中创建的,而在子线程中又无法直接对 UI 进行操作,于是通过一系列的发送消息、入队、出队等环节,最后调用到了 Handler 的 handleMessage()方法中,这时的 handleMessage()方法已经是在主线程中运 行的,因而我们当然可以在这里进行 UI 操作了。 所以通过Handler切换线程主要的就是Handler需要在另外的线程中创建。比如需要更新UI 那Handler就需要在主线程创建。你在子线程中创建了Handler 然后在同个线程中发送了刷新了消息 那也是会挂的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
new Thread(() -> {
Looper.prepare();
myHandler = new Handler(){
public void handleMessage(Message msg) {
if (111 == msg.what)
textView.setText("Fuck aaa");
}
};

try {
TimeUnit.SECONDS.sleep(5);
myHandler.sendEmptyMessage(111);

} catch (InterruptedException e) {
e.printStackTrace();
}

Looper.loop();

}).start();

如上代码 最后sleep结束后 也会触发异常 Only the original thread that created a view hierarchy can touch its views.
把 myHandler = new Handler()的定义移出到 Thread外就能正常刷新UI 了

其他子线程更新UI的操作

Handler.post()

1
2
3
4
public final boolean post(Runnable r)
{
return sendMessageDelayed(getPostMessage(r), 0);
}

post方法 最终还是调用了 sendMessageDelayed 方法把 Runnable 包装成了一个Message发送了出去

1
2
3
4
5
private final Message getPostMessage(Runnable r) { 
Message m = Message.obtain();
m.callback = r;
return m;
}

View.post()

1
2
3
4
5
6
7
8
9
public boolean post(Runnable action) { 
Handler handler;
if (mAttachInfo != null) {
handler = mAttachInfo.mHandler;
} else {
ViewRoot.getRunQueue().post(action); return true;
}
return handler.post(action);
}

本质上还是调用了Handler的post方法。 只是说 Handler的获取来源是哪

runOnUiThread()

这个应该用的最多的在子线程中刷新UI的方法了

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

如果当前是主线程 就直接运行 不是就调用 handler.post运行
mHandler 就是页面创建时候创建的Handler

文章作者: Fibonacci
文章链接: http://sovwcwsfm.com/blog/page/handler.html
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Blog