Android的Handler消息传递机制
能简单说得我们尽量不复杂:
为了避免ANR,我们会通常把 耗时操作放在子线程里面去执行,因为子线程不能更新UI,所以当子线程需要更新的UI的时候就需要借助到安卓的消息机制,也就是Handler机制了。
一个程序的运行,就是一个进程的在执行,一个进程里面可以拥有很多个线程。
主线程:也叫UI线程,或称ActivityThread,用于运行四大组件和处理他们用户的交互。 ActivityThread管理应用进程的主线程的执行(相当于普通Java程序的main入口函数),在Android系统中,在默认情况下,一个应用程序内的各个组件(如Activity、BroadcastReceiver、Service)都会在同一个进程(Process)里执行,且由此进程的主线程负责执行。
ActivityThread既要处理Activity组件的UI事件,又要处理Service后台服务工作,通常会忙不过来。为了解决此问题,主线程可以创建多个子线程来处理后台服务工作,而本身专心处理UI画面的事件。
子线程: 用于执行耗时操作,比如 I/O操作和网络请求等。(安卓3.0以后要求耗访问网络必须在子线程种执行)更新UI的工作必须交给主线程,子线程在安卓里是不允许更新UI的。
一、 基本概念
什么是消息机制? —— 不同线程之间的通信。
什么安卓的消息机制,就是 Handler 运行机制。
安卓的消息机制有什么用? —— 避免ANR(Application Not Responding) ,一旦发生ANR,程序就挂了,奔溃了。
什么时候会触发ANR?(消息机制在什么时候用?) —— 以下两个条件任意一个触发的的时候就会发生ANR
- 在activity中超过5秒的时间未能响应下一个事件
- BroadcastReceive超过10未响应
造成以上两点的原因有很多,比如网络请求, 大文件的读取, 耗时的计算等都会引发ANR
如何避免ANR
首先明白两点:
- 主线程不能执行耗时操作(避免ANR)
- 子线程不能直接更新UI界面
结合起来这两点的解决办法是:把耗时操作放到子线程去执行,然后使用Handler去更新UI
二、消息机制的分析理解
安卓的异步消息处理机制就是handler机制
主线程,ActivityThread被创建的时候就会创建Looper
Looper被创建的时候创建MessageQueue。
也就是说主线程会直接或简介创建出来Looper和MessageQueue。
Handler的工作机制简单来说是这样的
1、Handler发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue
2、Looper不断轮询调用MeaasgaQueue的next方法
3、如果发现message就调用handler的dispatchMessage,ldispatchMessage被成功调用,接着调用handlerMessage()
简图
1、Handler的铁三角—— Handler、MessageQueue和Lopper
android的消息机制就是指Handler机制,Handler机制的运行需要MeeageQueue和Looper的辅助。
2、MessageQueue(消息队列)的工作原理
MessageQueue中文翻译就是消息队列,它内部存储了一组信息,存放的是Message,以队列的形式对外提供了插入和删除的工作(虽然名字叫做队列,但是其内部的 存储结构是单链表)
主要 插入 和 读取 两个操作,这两个操作对应着两个方法:
- 插入(入队) enqueueMessage(Message msg, long when)
- 读取(出队) next()
enqueueMessage方法
boolean enqueueMessage(Message msg, long when) {
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) {
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) {
// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
} else {
// Inserted within the middle of the queue. Usually we don't have to wake
// up the event queue unless there is a barrier at the head of the queue
// and the message is the earliest asynchronous message in the queue.
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;
}
- 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
可以看出enqueueMessage主要是单链表的插入操作。
next方法
next方法在这里是一个无限循环的方法,如果消息队列里面没有消息,那么他就会处于阻塞状态,当有新的消息到来的时,next就会返回这条消息并且将其从单链表中移除。
3、Looper的工作原理
Looper中文翻译是轮询器或者消息泵或者循环。个人还是叫做轮询器比较形象一些。
3.1、Looper的作用
Looper是一个轮询器,它的作用不断轮询MessageQueue,当如果有新的消息就交给Handler处理,如果轮询不到新的消息,那就自身就处于阻塞状态。
3.2、Looper的构造函数创建了MessageQueue
我们通过查看Loop而这个类,可以发现的他的构造方法里面创建了一个MessageQueue,然后将当前线程的对象保存起来
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
- 1
- 2
- 3
- 4
3.3、new Handler的hanlder不能没有Looper
new出来一个Handler但是没有创建Looper的话就会报错。
"Can't create handler inside thread that has not called Looper.prepare()"); ,
- 1
解决办法就是new Handler的时候加上Looper.prepare();
3.4、主线程(ActivityThread),被创建的时候就会创建一个Looper
线程默认是没有Looper的,但是为什么在主线程没有创建的Looper就可以使用Handler?主线程是特别的。主线程,也就是ActivityThread,主线程被创建的时候就会创建一个Looper,这点是比较特殊的,也正因为这点,所以我们在主线程创建了Handler就直接能用了。
3.5、Looper的ThreadLocal
Looper有一个特殊的概念,那就是ThreadLocal,(他并不是线程),他的作用是帮助Handler获得当前线程的Looper(多个线程可能有多个Looper)
Looper 的几个方法
- 创建:Looper.prepare() : 为当前线程创建一个Looper,prepareMainLooper() : UI线程(ActivityThread)创建Looper的
- 开启:Looper.loop() : 开启消息轮询
- 退出:quit() : 直接退出Looper,
quitSafely() : 设定一个标记,只有当目前已有消息处理完毕之后才会执行退出操作。
Looper这个类里面最重要的方法就是loop()开启消息循环这个方法了,
public static final void loop() {
Looper me = myLooper();
MessageQueue queue = me.mQueue;
while (true) {
Message msg = queue.next(); // might block
if (msg != null) {
if (msg.target == null) {
return;
}
if (me.mLogging!= null) me.mLogging.println(
">>>>> Dispatching to " + msg.target + " "
+ msg.callback + ": " + msg.what
);
msg.target.dispatchMessage(msg);
if (me.mLogging!= null) me.mLogging.println(
"<<<<< Finished to " + msg.target + " "
+ msg.callback);
msg.recycle();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
可以看到,这个方法从第4行开始,进入了一个死循环,然后不断地调用的MessageQueue的next()方法,我想你已经猜到了,这个next()方法就是消息队列的出队方法。不过由于这个方法的代码稍微有点长,我就不贴出来了,它的简单逻辑就是如果当前MessageQueue中存在mMessages(即待处理消息),就将这个消息出队,然后让下一条消息成为mMessages,否则就进入一个阻塞状态,一直等到有新的消息入队。继续看loop()方法的第14行,每当有一个消息出队,就将它传递到msg.target的dispatchMessage()方法中,那这里msg.target又是什么呢?其实就是Handler啦,你观察一下上面sendMessageAtTime()方法的第6行就可以看出来了。接下来当然就要看一看Handler中dispatchMessage()方法的源码了,如下所示:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
在第5行进行判断,如果mCallback不为空,则调用mCallback的handleMessage()方法,否则直接调用Handler的handleMessage()方法,并将消息对象作为参数传递过去。这样我相信大家就都明白了为什么handleMessage()方法中可以获取到之前发送的消息了吧!
4、Handler的工作原理
主要工作:消息的 发送 和 接受 。
Handler消息发送的形式
先附上两份最简单的日常正常使用post和send方式的代码
send方式sendEmptyMessage方法的小demo
public class MainActivity extends Activity {
private static final int MSG_CHANGE_TEXT = 0x2001;
private TextView mTv;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case MSG_CHANGE_TEXT:
mTv.setText("send方式修改的文字");
break;
}
}
};;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTv= (TextView) findViewById(R.id.mTv);
mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
handler.sendEmptyMessage(MSG_CHANGE_TEXT);
}
});
}
}
- 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
post方式的postDelayed方法的小demo
public class MainActivity extends Activity {
private Handler handler;
private TextView mTv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
handler = new Handler();
mTv = (TextView) findViewById(R.id.mTv);
mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
change();
}
});
}
private void change(){
new Thread(new Runnable() {
@Override
public void run() {
handler.postDelayed(new Runnable() {
@Override
public void run() {
mTv.setText("啊哈哈哈");
}
},300);
}
}).start();
}
}
- 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
两种形式,post和send,其实post最终还是会调用send
Handler消息接收的形式
再一遍简要地附上handler工作形式
1、Handler发送消息仅仅是调用MessageQueue的enqueueMessage向插入一条信息到MessageQueue
2、Looper不断轮询调用MeaasgaQueue的next方法
3、如果发现message就调用handler的dispatchMessage,ldispatchMessage被成功调用,接着调用handlerMessage()
如果是post发送来的message,那么就让这个message所持有的Runnable执行run方法,非常简单。
Message的Callback 是一个Runnable对象,Handler的post的重载的函数不管参数多少,肯定都是有Runnable的。
private static void handleCallback(Message message) {
message.callback.run();
}
- 1
- 2
- 3
如果是send方法发送的,那么就执行handleMessage,这个方法我们非常熟悉了,google的给的备注的也说了,子类必须实现方法以接受这些Message。这也就是我们最常见的最常用的方式了。
/**
* Subclasses must implement this to receive messages.
*/
public void handleMessage(Message msg) {
}
- 1
- 2
- 3
- 4
- 5
推荐阅读
-
Android中ACTION_CANCEL的触发机制与滑出子view的情况
-
Android Handler的使用详解
-
Android的线程通信:消息机制原理(Message,Handler,MessageQueue,Looper),异步任务AsyncTask,使用JSON
-
Android 入门第十讲02-广播(广播概述,使用方法(系统广播,自定义广播,两个activity之间的交互和传值),EventBus使用方法,数据传递,线程切换,Android的系统广播大全)
-
Android中Activity之间跳转和参数传递的实例
-
Android中传递对象的三种方法的实现
-
Android中使用Intent在Activity之间传递对象(使用Serializable或者Parcelable)的方法
-
详解Android Scroller与computeScroll的调用机制关系
-
Android之利用EventBus发送消息传递示例
-
Android编程中的消息机制实例详解