handler机制学习总结
Posted AC_Jobim
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了handler机制学习总结相关的知识,希望对你有一定的参考价值。
handler机制学习总结
一、Handler介绍
-
Handler的作用
在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理 -
Looper、MessageQueue、Message、Handler的关系
-
Looper
每一个线程只有一个Looper,每个线程在初始化Looper之后,然后Looper会维护好该线程的消息队列,用来存放Handler发送的Message,并处理消息队列出队的Message。它的特点是它跟它的线程是绑定的,处理消息也是在Looper所在的线程去处理,所以当我们在主线程创建Handler时,它就会跟主线程唯一的Looper绑定,从而我们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。那么就会有人问,为什么我们使用Handler的时候从来都不需要创建Looper呢?这是因为在主线程中,ActivityThread默认会把Looper初始化好,prepare以后,当前线程就会变成一个Looper线程。
-
MessageQueue
MessageQueue是一个消息队列,用来存放Handler发送的消息。每个线程最多只有一个MessageQueue。MessageQueue通常都是由Looper来管理,而主线程创建时,会创建一个默认的Looper对象,而Looper对象的创建,将自动创建一个MessageQueue。其他非主线程,不会自动创建Looper。 -
Message
消息对象,就是MessageQueue里面存放的对象,一个MessageQueue可以包括多个Message。当我们需要发送一个Message时,一般使用Message.obtain()来获取Message实例,因为在Message类里面定义了一个消息池,当消息池里存在未使用的消息时,便返回,如果没有未使用的消息,则通过new的方式创建返回,所以使用Message.obtain()的方式来获取实例可以大大减少当有大量Message对象而产生的垃圾回收问题。
总结:
当我们的子线程想修改Activity中的UI组件时,我们可以新建一个Handler对象,通过这个对象向主线程发送信息;而我们发送的信息会先到主线程的MessageQueue进行等待,由Looper按先入先出顺序取出,再根据message对象的what属性分发给对应的Handler进行处理!
-
二、Handler的使用
使用Handler发送消息主要有两种,Handler.sendMessage()
和Handler.post()
Handler的相关方法:
- void handleMessage(Message msg):处理消息的方法,通常是用于被重写!
- sendEmptyMessage(int what):发送空消息
- sendEmptyMessageDelayed(int what,long delayMillis):指定延时多少毫秒后发送空信息
- sendMessage(Message msg):立即发送信息
- sendMessageDelayed(Message msg):指定延时多少毫秒后发送信息
- final boolean hasMessage(int what):检查消息队列中是否包含what属性为指定值的消息 如果是参数为(int what,Object object):除了判断what属性,还需要判断Object属性是否为指定对象的消息
- removeMessages(int what):移除还未处理的消息,即延迟消息
使用Handler的一般步骤:(以sendMessage()为例)
- 创建Handler成员变量对象,并重写其handlerMessage()方法
- 在分/主线程创建Message对象
- 使用handler对象发送Message
- 在handleMessage()中处理消息
//1. 创建Handler成员变量对象, 并重写其handleMessage()
private Handler handler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if(msg.what == 1){
//4. 在handleMessage()中处理消息
String result = (String) msg.obj;
et_handler1_result.setText(result);
pb_handler1_loading.setVisibility(View.INVISIBLE);
}
}
};
public void getSubmit2(View v) {
//显示提示视图(ProgressDialog/ProgressBar)
pb_handler1_loading.setVisibility(View.VISIBLE);
//2.分线程, 联网请求, 并得到响应数据
new Thread(){
@Override
public void run() {
String path = "https://www.bing.com/";
try {
String result = requestToString(path);
//3.主/分 线程,使用handler对象发送Message
//在 分/主 线程创建Message对象
Message message = Message.obtain();
message.what = 1;//标识
message.obj = result;
//使用handler对象发送Message
handler.sendMessage(message);
} catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
-
使用post()方法发送消息也类似,只是不在handleMessage()中进行消息的处理,而是处理时通过调用post()方法传入Runnable对象的run()方法
public void getSubmit3(View view) { //主线程, 显示提示视图(ProgressDialog/ProgressBar) pb_handler1_loading.setVisibility(View.VISIBLE); //2.分线程, 联网请求, 并得到响应数据 Handler handler = new Handler(); new Thread(){ @Override public void run() { String path = "https://www.bing.com/"; try { String result = requestToString(path); //3.在工作线程中 发送消息到消息队列中 handler.post(new Runnable() { @Override public void run() { //4.执行需要的UI操作 et_handler1_result.setText(result); pb_handler1_loading.setVisibility(View.INVISIBLE); } }); } catch (Exception e) { e.printStackTrace(); } } }.start(); }
三、源码分析
原理图:
3.1 创建循环器对象(Looper) & 消息队列对象(MessageQueue)
- 创建Looper对象主要通过方法:Looper.prepareMainLooper()、Looper.prepare();
- 创建消息队列对象(MessageQueue)方法:创建Looper对象时则会自动创建。
private static void prepare(boolean quitAllowed) {
// 1. 判断sThreadLocal是否为null,否则抛出异常
//即 Looper.prepare()方法不能被调用两次 = 1个线程中只能对应1个Looper实例
// 注:sThreadLocal = 1个ThreadLocal对象,用于存储线程的变量
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
// 2. 若为初次Looper.prepare(),则创建Looper对象 & 存放在ThreadLocal变量中
// 注:Looper对象是存放在Thread线程里的
sThreadLocal.set(new Looper(quitAllowed));
}
//Looper的构造方法
private Looper(boolean quitAllowed) {
// 1. 创建1个消息队列对象(MessageQueue)
// 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
在android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)创建时,会自动调用ActivityThread的1个静态的main()方法 = 应用程序的入口main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象
/**
* Looper.prepareMainLooper()
* 作用:为 主线程(UI线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue)
* 注:该方法在主线程(UI线程)创建时自动调用,即 主线程的Looper对象自动生成,不需手动生成
*/
public static void prepareMainLooper() {
//调用prepare()方法
prepare(false);
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
/**
* 源码分析:main()
**/
public static void main(String[] args) {
... // 仅贴出关键代码
Looper.prepareMainLooper();
// 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
// 方法逻辑类似Looper.prepare()
// 注:prepare():为子线程中创建1个Looper对象
ActivityThread thread = new ActivityThread();
// 2. 创建主线程
Looper.loop();
// 3. 自动开启 消息循环 ->>下面将详细分析
}
总结:
- 创建主线程时,会自动调用
ActivityThread
的1个静态的main()
;而main()
内则会调用Looper.prepareMainLooper()
为主线程生成1个Looper
对象,同时也会生成其对应的MessageQueue
对象
- 即 主线程的
Looper
对象自动生成,不需手动生成;而子线程的Looper
对象则需手动通过Looper.prepare()
创建- 在子线程若不手动创建
Looper
对象 则无法生成Handler
对象
- 根据
Handler
的作用(在主线程更新UI
),故Handler
实例的创建场景 主要在主线程 - 生成
Looper
&MessageQueue
对象后,则会自动进入消息循环:Looper.loop()
,即又是另外一个隐式操作。
3.2 创建消息对象
/**
* 源码分析:Message.obtain()
* 作用:创建消息对象
* 注:创建Message对象可用关键字new 或 Message.obtain()
*/
public static Message obtain() {
// Message内部维护了1个Message池,用于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;
}
// 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
}
// 若池内无消息对象可复用,则还是用关键字new创建
return new Message();
}
3.3 发送消息
发送消息有send
和post
的方式:
// 发送消息
Handler handelr = new Handler();
// 发送方式一
handler.sendMessage(msg); // 或者handler.sendEmptyMessage(what);
// 发送方式二
handler.postXXX(runnable);
handler.sendXXX()
和handler.postXXX()
最终都会调用到sendMessageDelayed()
方法。
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
if (delayMillis < 0) {
delayMillis = 0;
}
// 注意sendMessageAtTime方法的第二个参数,当前时间加上延时时间
return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}
然后调用了sendMessageAtTime
方法
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
// 这里拿到的MessageQueue其实就是创建时的MessageQueue,默认情况是当前线程的Looper对象的MessageQueue
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); // uptimeMillis = SystemClock.uptimeMillis() + delayMillis
}
又调用了enqueueMessage()
方法
private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
long uptimeMillis) {
//保存发送消息的handler对象
msg.target = this;
msg.workSourceUid = ThreadLocalWorkSource.getUid();
if (mAsynchronous) {
msg.setAsynchronous(true);
}
//调用消息队列保存消息对象
return queue.enqueueMessage(msg, uptimeMillis);
}
然后又调用了MessageQueue
的enqueueMessage()
方法:调用消息队列保存消息对象
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
// 一个Message,只能发送一次
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("MessageQueue", e.getMessage(), e);
msg.recycle();
return false;
}
// 标记Message已经使用了
msg.markInUse();
msg.when = when;
// 得到当前消息队列的头部
Message p = mMessages;
boolean needWake;
// 我们这里when为0,表示立即处理的消息
if (p == null || when == 0 || when < p.when) {
// 把消息插入到消息队列的头部
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// 根据需要把消息插入到消息队列的合适位置,通常是调用xxxDelay方法,延时发送消息
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;
}
// 如果队列阻塞了,则唤醒
if (needWake) {
nativeWake(mPtr);
}
}
return true;
}
首先,判断了Message是否已经使用过了,如果使用过,则直接抛出异常,这是可以理解的,如果MessageQueue中已经存在一个Message,但是还没有得到处理,这时候如果再发送一次该Message,可能会导致处理前一个Message时,出现问题。
然后,会判断when,它是表示延迟的时间,我们这里没有延时,所以为0,满足if条件。把消息插入到消息队列的头部。如果when不为0,则需要把消息加入到消息队列的合适位置。
最后会去判断当前线程是否已经阻塞了,如果阻塞了,则需要调用本地方法去唤醒它。
再看一下post系列方法:
public final boolean post(Runnable r) {
return sendMessageDelayed(getPostMessage(r), 0);
}
private static Message getPostMessage(Runnable r) {
// 构造一个Message,并让其callback执行传来的Runnable
Message m = Message.obtain();
m.callback = r;
return m;
}
可以看到:post方法只是先调用了getPostMessage方法,将Runnable封装到一个Message,然后就调用了sendMessageDelayed
总结:发送消息就是把Message加入到Handler中的MessageQueue中去
3.4 取出处理消息
此处主要分析的是Looper
类中的loop()
方法
/**
* 源码分析: Looper.loop()
* 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
* 特别注意:
* a. 主线程的消息循环不允许退出,即无限循环
* b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
*/
public static void loop() {
...// 仅贴出关键代码
// 1. 获取当前Looper的消息队列
final Looper me = myLooper();
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}
// myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
// 即loop()执行前必须执行prepare(),从而创建1个Looper实例
final MessageQueue queue = me.mQueue;
// 获取Looper实例中的消息队列对象(MessageQueue)
// 2. 消息循环(通过for循环)
for (;;) {
// 2.1 从消息队列中取出消息
Message msg = queue.next();
if (msg == null) {
return;
}
// next():取出消息队列里的消息
// 若取出的消息为空,则线程阻塞
// 2.2 派发消息到对应的Handler
msg.target.dispatchMessage(msg);
// 把消息Message派发给消息对象msg的target属性
// target属性实际是1个handler对象
// 3. 释放消息占据的资源
msg.recycle();
}
}
调用MessageQueue的next()
方法,从消息队列中获取消息
/**
* 定义:属于消息队列类(MessageQueue)中的方法
* 作用:出队消息,即从 消息队列中 移出该消息
*/
Message next() {
...// 仅贴出关键代码
// 该参数用于确定消息队列中是否还有消息
// 从而决定消息队列应处于出队消息状态 or 等待状态
int nextPollTimeoutMillis = 0;
for (;;) {
if (nextPollTimeoutMillis != 0) {
Binder.flushPendingCommands();
}
// nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
// 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
if (msg != null) {
if (now < msg.when) {
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
} else {
// 取出了消息
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;
} else {
mMessages = msg.next;
}
msg.next = null;
if (DEBUG) Log.v(TAG, "Returning message: " + msg);
msg.markInUse();
return msg;
}
} else {
// 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
// 下次循环时,消息队列则处于等待状态
nextPollTimeoutMillis = -1;
}
......
}
.....
}
}// 回到分析原处
调用handler的dispatchMessage()
方法,派发消息到对应的Handler实例
/**
* 定义:属于处理者类(Handler)中的方法
* 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
*/
public void dispatchMessage(Message msg) {
// 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
// 则执行handleCallback(msg),即回调Runnable对象里复写的run()
// 上述结论会在讲解使用“post(Runnable r以上是关于handler机制学习总结的主要内容,如果未能解决你的问题,请参考以下文章