1. 简介
Handler是一套 Android 消息传递机制
可以说只要有异步线程与主线程通信的地方就一定会有 Handler。
在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理
使用Handler消息传递机制主要是为了多个线程并发更新UI的同时,保证线程安全
2. 相关概念解释
Handler、Message、Message Queue、Looper
Android消息机制:
- 以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。
- Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。
- 在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。
Handler示意图:
消息机制的模型:
- Message:需要传递的消息,可以传递数据;
- MessageQueue:消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next);
- Handler:消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage);
- Looper:不断循环执行(Looper.loop),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。
消息机制的架构
- 在子线程执行完耗时操作,当Handler发送消息时,将会调用 MessageQueue.enqueueMessage ,向消息队列中添加消息。
- 当通过 Looper.loop 开启循环后,会不断地从线程池中读取消息,即调用 MessageQueue.next
- 然后调用目标Handler(即发送该消息的Handler)的 dispatchMessage 方法传递消息,然后返回到Handler所在线程,目标Handler收到消息,调用 handleMessage 方法,接收消息,处理消息。
3. Handler 的基本使用
3.1 创建 Handler
Handler 允许我们发送延时消息,如果在延时期间用户关闭了 Activity,那么该 Activity 会泄露。
这个泄露是因为 Message 会持有 Handler,而又因为 Java 的特性,内部类会持有外部类,使得 Activity 会被 Handler 持有,这样最终就导致 Activity 泄露。
解决方案:将 Handler 定义成静态的内部类,在内部持有 Activity 的弱引用,并及时移除所有消息。
public class HandlerActivity extends AppCompatActivity {
private Button bt_handler_send;
private static class MyHandler extends Handler {
//弱引用持有HandlerActivity , GC 回收时会被回收掉
private WeakReference<HandlerActivity> weakReference;
public MyHandler(HandlerActivity activity) {
this.weakReference = new WeakReference(activity);
}
@Override
public void handleMessage(Message msg) {
HandlerActivity activity = weakReference.get();
super.handleMessage(msg);
if (null != activity) {
//执行业务逻辑
Toast.makeText(activity,"handleMessage",Toast.LENGTH_SHORT).show();
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.HandlerActivity);
//创建 Handler
final MyHandler handler = new MyHandler(this);
bt_handler_send = findViewById(R.id.bt_handler_send);
bt_handler_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable() {
@Override
public void run() {
//使用 handler 发送空消息
handler.sendEmptyMessage(0);
}
}).start();
}
});
}
@Override
protected void onDestroy() {
//移除所有回调及消息
myHandler.removeCallbacksAndMessages(null);
super.onDestroy();
}
}
3.2 Message 获取
获取 Message 大概有如下几种方式:
Message message = myHandler.obtainMessage(); //通过 Handler 实例获取
Message message1 = Message.obtain(); //通过 Message 获取
Message message2 = new Message(); //直接创建新的 Message 实例
通过查看源码可知,Handler 的 obtainMessage() 方法也是调用了 Message 的 obtain() 方法
public final Message obtainMessage()
{
return Message.obtain(this);
}
通过查看 Message 的 obtain 方法
public static Message obtain(Handler h) {
//调用下面的方法获取 Message
Message m = obtain();
//将当前 Handler 指定给 message 的 target ,用来区分是哪个 Handler 的消息
m.target = h;
return m;
}
//从消息池中拿取 Message,如果有则返回,否则创建新的 Message
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();
}
为了节省开销,我们在使用的时候尽量复用 Message,使用前两种方式进行创建。
3.3 Handler 发送消息
Handler 提供了一些列的方法让我们来发送消息,如 send()系列 post()系列 。
不过不管我们调用什么方法,最终都会走到 MessageQueue.enqueueMessage(Message,long) 方法。
以 sendEmptyMessage(int) 方法为例:
//Handler
sendEmptyMessage(int)
-> sendEmptyMessageDelayed(int,int)
-> sendMessageAtTime(Message,long)
-> enqueueMessage(MessageQueue,Message,long)
-> queue.enqueueMessage(Message, long);
从中可以发现 MessageQueue 这个消息队列,负责消息的入队,出队。
4. Handler机制原理
Handler是Android消息机制的上层接口。Handler的使用过程很简单,通过它可以轻松地将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是更新UI
如下就是使用消息机制的一个简单实例:
public class Activity extends android.app.Activity {
private Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
System.out.println(msg.what);
}
};
@Override
public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
super.onCreate(savedInstanceState, persistentState);
setContentView(R.layout.activity_main);
new Thread(new Runnable() {
@Override
public void run() {
...............耗时操作
Message message = Message.obtain();
message.what = 1;
mHandler.sendMessage(message);
}
}).start();
}
}
再举个例子:怎么从主线程发送消息到子线程?(虽然这种应用场景很少)
Thread thread = new Thread(){
@Override
public void run() {
super.run();
//初始化Looper,一定要写在Handler初始化之前
Looper.prepare();
//在子线程内部初始化handler即可,发送消息的代码可在主线程任意地方发送
handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
//所有的事情处理完成后要退出looper,即终止Looper循环
//这两个方法都可以,有关这两个方法的区别自行寻找答案
handler.getLooper().quit();
handler.getLooper().quitSafely();
}
};
//启动Looper循环,否则Handler无法收到消息
Looper.loop();
}
};
thread.start();
//在主线程中发送消息
handler.sendMessage();
先来解释一下第一行代码Looper.prepare();,先看看Handler构造方法
//空参的构造方法,这个方法调用了两个参数的构造方法
public Handler() {
this(null, false);
}
//两个参数的构造方法
public Handler(Callback callback, boolean async) {
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;
}
- Handler的构造方法中会验证Looper,如果Looper为空,那么会抛出空指针异常
- Handler在构造方法中还做了一件事,将自己的一个全局消息队列对象(mQueue)指向了Looper中的消息队列,即构造方法中的这行代码mQueue = mLooper.mQueue;
第二行代码初始化了Hanlder并且重写HandleMessage()方法,没啥好讲的。
然后调用了Looper.loop()方法,后面会解释。
我们先来看看最后一行代码handler.sendMessage(message) 的主要作用:
先看看这行代码之后的代码执行流程:
sendMessage()之后代码通过图中所示的几个方法,最终执行到了MessageQueue的enqueueMessage()方法,我们来就看看它:
boolean enqueueMessage(Message msg, long when) {
if (msg.target == null) {
throw new IllegalArgumentException("Message must have a target.");
}
synchronized (this) {
msg.markInUse();
msg.when = when;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
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;
}
- MessageQueue是一个单向列表结构,而MessageQueue 的 enqueueMessage()方法主要做的事情就是将 Handler发送过来的 Message插入到列表中。
- 调用handler.senMessage()方法的时候,最终的结果只是将这个消息插入到了消息队列中
发送消息的工作已经完成,那么Looper是什么时候取的消息,来看看:
public static void loop() {
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
try {
msg.target.dispatchMessage(msg);
}
}
}
- 这是一个死循环
- 这个循环的目的是从MessageQueue中取出消息
- 取消息的方法是MessageQueue.next()方法
- 取出消息后调用message.target对象的dispatchMessage()方法分发消息
- 循环跳出的条件是MessageQueue.next()方法返回了null
看到这里我们应该自然会想到,message.target.是哪个对象?dispatchMessage有什么作用?
public final class Message implements Parcelable {
/*package*/ int flags;
/*package*/ long when;
/*package*/ Bundle data;
/*package*/ Handler target;
/*package*/ Runnable callback;
// sometimes we store linked lists of these things
/*package*/ Message next;
}
- 在Looper从MessageQueue中取出Message之后,调用了Handler的dispatchMessage()方法
这里我们不禁要问,这个target指向了哪个Handler,再来看看之前的enqueueMessage
//Handler的方法
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this;
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
- 第一行代码就是,将message的target属性赋值为发送message的handler自身
- Looper取出消息后,调用了发送消息的Handler的dispatchMessage()方法,并且将message本身作为参数传了回去。到此时,代码的执行逻辑又回到了Handler中。
接着看handler的dispatchMessage()方法
/**
*handler的方法
* 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);
}
}
看到这里面一个我们非常熟悉到方法了没有?—handleMessage()方法,也是我们处理消息时候的逻辑。
最后再来一个系统的工作示意图:
5. Handler用法(android与java区别)
5.1 刷新UI界面
使用java:
new Thread( new Runnable() {
public void run() {
myView.invalidate();
}
}).start();
可以实现功能,刷新UI界面。但是这样是不行的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。
Thread+Handler:
Handler来根据接收的消息,处理UI更新。Thread线程发出Handler消息,通知更新UI。
Handler myHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case TestHandler.GUIUPDATEIDENTIFIER:
myBounceView.invalidate();
break;
}
super.handleMessage(msg);
}
};
class myThread implements Runnable {
public void run() {
while (!Thread.currentThread().isInterrupted()) {
Message message = new Message();
message.what = TestHandler.GUIUPDATEIDENTIFIER;
TestHandler.this.myHandler.sendMessage(message);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
5.2 定时器(延时操作)
使用java:
使用Java上自带的TimerTask类,TimerTask相对于Thread来说对于资源消耗的更低,引入import java.util.Timer; 和 import java.util.TimerTask;
public class JavaTimer extends Activity {
Timer timer = new Timer();
TimerTask task = new TimerTask(){
public void run() {
setTitle("hear me?");
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timer.schedule(task, 10000); // 延迟delay毫秒后,执行一次task。
}
}
TimerTask + Handler:
public class TestTimer extends Activity {
Timer timer = new Timer();
Handler handler = new Handler(){
public void handleMessage(Message msg) {
switch (msg.what) {
case 1:
setTitle("hear me?");
break;
}
super.handleMessage(msg);
}
};
TimerTask task = new TimerTask(){
public void run() {
Message message = new Message();
message.what = 1;
handler.sendMessage(message);
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
timer.schedule(task, 10000); // 延迟delay毫秒后,执行一次task。
}
}
5.3 定时更新 UI
Runnable + Handler.postDelayed(runnable,time):
private Handler handler = new Handler();
private Runnable myRunnable= new Runnable() {
public void run() {
if (run) {
handler.postDelayed(this, 1000);
count++;
}
tvCounter.setText("Count: " + count);
}
};
然后在其他地方调用
handler.post(myRunnable);
handler.post(myRunnable,time);
5.4 Handler实现延迟执行
Handler延迟2s执行一个runnable
Handler handler=new Handler();
Runnable runnable=new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
if(xsLayout.getVisibility()==View.VISIBLE){
xsLayout.setVisibility(View.GONE);
}
}
};
handler.postDelayed(runnable, 2000);
在runnable被执行之前取消这个定时任务
handler.removeCallbacks(runnable);
6. 总结
Handler 的背后有着 Looper 以及 MessageQueue 的协助,三者通力合作,分工明确。
- Looper :负责关联线程以及消息的分发在该线程下从 MessageQueue 获取 Message,分发给Handler ;
- MessageQueue :是个队列,负责消息的存储与管理,负责管理由 Handler 发送过来的Message;
- Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。
Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。
线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决
转载:https://blog.csdn.net/ly0724ok/article/details/117324053