Handler是我们常用的东西,为什么呢?因为他可以将我们所需要的反馈事件切换到handler所在的线程中运行,从而实现在子线程中实现耗时间的操作,然后将返回的结果通知到主线程,然后实现更新UI的过程。今天我们就从实现原理以及源码的角度分析一下,Android的消息机制,看看handler是怎样实现线程切换的。
消息机制的实现
基本实现方法
我们首先来实现一个最基本的通过handler的消息传递。
public void showDialog(String str) {
Message message = new Message();
message.what = START_DIALOG;
Bundle bundle = new Bundle();
bundle.putString("str",str);
message.setData(bundle);
handler.sendMessage(message);
}
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case STOP_DIALOG:
DialogUtil.getIntance().closeDialog();
break;
case START_DIALOG:
DialogUtil.getIntance().waitDialog(BaseActivity.this).show();
break;
default:
break;
}
}
};
在实例中,我们通过实现了一个handler,然后通过调用showDialog,达到显示dialog的目的。
但是在完成这一过程中,不仅仅只是一个Handler就完成了所有的操作,而且handler只是一个用于帮助我们快速时间的工具。
消息机制组成元素
消息的传递我们一共可以分为3个部分,Handler,MessageQueue,Looper。
Messag只是起我们作为传递过程中,传递参数的作用。
Message:线程之间的消息传递的信息载体,存放少量信息。可以传递的信息有int(what arg1 arg2) Object (obj).
Handler:事件的发出者和处理者,消息机制的辅助处理类。主要通过Handler.sendMessage()
发出信息,和Handler.handleMessage()
处理信息。
MessageQueue:一个实现了先进先出效果的单链表,并不是我们所谓的队列数据结构。主要用于存放所有通过Handler.sendemessage
发送的消息。即向消息池投递消息(MessageQueue.enqueueMessage
)和取走消息池的消息(MessageQueue.next
)。会一直存在消息队列中,等待被处理。每个线程只有一个MessageQueue对象。
Looper:在(Looper.loop
)中不断循环执行,从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。即将事件交给所对应的Handler处理。
然后我们就可以理解到,消息传递就是将消息先放到每个线程中的MessageQueue中,然后通过Looper将MessageQueue中的Message发送到所对应的Handler中去。让Handler去处理这些事件。
从而达到将一个线程中的事件传递到Handler所在的线程中去。
消息机制模型
然后我们再用图解的方式,看一下之间的实现关系。
消息机制流程
我们按照流程图,一步一步分析消息的传递。
首先在新线程中,我们实现了执行了耗时间操作,现在需要通知主线程更改UI。
- 通过Handler的引用,发送一个Message。
handler.sendMessage(message);
- 通过Handler调度,将使用
Handler.enqueueMessage
将消息传递到MessageQueue的队列中。 - 存放在MessageQueue中的消息,将会因为
Looper.loop()
的开启,一直调用MessageQueue.next
,一直从队列中取出消息。 - 在Looper中被取出的Message将会通过
msg.target.dispatchMessage(msg);
将会分发一个事件给我们所引用的Handler。 - Handler的到事件,进行处理。更改UI。
注意:
1.每个线程中只有一个Looper实例。而MessageQueue是在Looper中实例的,所以MessageQueue也只有一个。
2.实际上每个线程中的Looper都是要我们自己实现的,只不过因为在Activity中,在ActivityThread中就已经帮我们Looper.prepareMainLooper();
和Looper.loop();
了,所以我们才能直接使用Handler,但在新开的线程中,我们就要自己通过这两句代码开始新线程中的Looper。
以上是对消息机制的实现以及消息传递的基本流程。下面我们将通过源码的方式来理解内部的实现原理。
源码以及理解
我们还是按照上面的流程图,一个个分析。
消息机制流程
发送消息
-
创建handler
开发文档中的创建Handler方法.png
//申明默认的Handler
public Handler() {
this(null, false);
}
public Handler(Callback callback, boolean async) {
if (FIND_POTENTIAL_LEAKS) {
......
//必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
mLooper = Looper.myLooper();
if (mLooper == null) {
throw new RuntimeException(
"Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue; //从这我们看出MessageQueue是在Looper中实例的
mCallback = callback; //回调方法
mAsynchronous = async; //设置消息是否为异步处理方式
}
-
发送消息
发送消息我们有种方式发送消息,一种是通过send方法发送message,一种是使用post方法,传递一个Runnable,当该条消息被发送给对应的handler的时候,handler直接执行内部代码。典型例子就是Android中的runOnUiThread(),直接将内部代码交由UI线程的中的handler处理。
send
send.png
post
post.png
但不管是send还是post发送信息,最终都会调用sendMessageAtTime
这个函数。
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);
}
并且就连子线程中调用Activity中的runOnUiThread()中更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。
public final void runOnUiThread(Runnable action) {
if (Thread.currentThread() != mUiThread) {
mHandler.post(action);
} else {
action.run();
}
}
在看看sendMessageAtTime最后调用的enqueueMessage
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
//target就是发送message的那个handler的引用
msg.target = this;
if (mAsynchronous) {
//是否异步执行
msg.setAsynchronous(true);
}
//将消息放置在消息队列中
return queue.enqueueMessage(msg, uptimeMillis);
}
可以看出,Handler通过对Looper中的MessageQueue的引用,将得到的信息放到消息队列中。
传递消息
MessageQueue
boolean enqueueMessage(Message msg, long when) {
//target就是发送message的那个handler的引用
if (msg.target == null) {
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) { //正在退出时,回收msg,加入到消息池
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;
Message p = mMessages;
boolean needWake;
if (p == null || when == 0 || when < p.when) {
//p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
//将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
//消息队头存在barrier,并且同时Message是队列中最早的异步消息。
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;
}
获取消息
分发消息
网友评论