Android消息机制梳理
来源:互联网 发布:了不起的nodd.js 编辑:程序博客网 时间:2024/06/06 01:01
从源码的角度解析android的消息机制,结合7.0源码,重新梳理一下android的消息机制。 Message ,Looper,Handler,MessageQueue的关系。
我们都知道Android的UI不是线程安全的,所以不能在子线程中操作UI组件,如果子线程中有操作UI的需求。我们都会通过handler来进行线程间的通信。
例如:
Handler handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //操作ui } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); new Thread(new Runnable() { @Override public void run() { //构建Message对象 Message message=Message.obtain(); //发送消息 handler.sendMessage(message); } }).start(); }
例如上面的代码,在子线程中,构建一个message对象,然后通过handler发送这个message对象。然后我们就可以在handler的handleMessage方法中接收这个消息,然后操作ui组件。
这个代码我们写的太多了,闭着眼睛也可以写了。但是如果要深究原理的话,就会有很多疑问。
比如:
1.为什么handleMessage里方法就可以操作ui组件呢(ps:在android中,我们把响应用户操作的线程称为主线程,也叫ui线程。线程名为:”main”,既然handleMessage方法中能够操作ui组件,那说明他执行的线程就是在main线程中的,但为什么它执行在main线程中的呢)?
2.为什么handler.sendMessage方法后,为什么会回调handleMessage呢?他们之间的调用栈是怎样的?
第一个问题,有点大,我们先来看第二个问题! 跟进sendMessage的源码,发现除了postAtFrontOfQueue这个方法外,所有的handler的sendxx,postxx方法,最后都会回调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); }
那个方法没有调用sendMessageAtTime方法,但是在里面调用了sendMessageAtFrontOfQueue方法。这个方法的实现和sendMessageAtTime完全相同。然后我们看sendMessageAtTime这个方法的实现,可以看到先给MessageQueue 对象赋值,然后调用了enqueueMessage方法,代码如下。 最后调用了queue对象的enqueueMessage方法。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
我们先整理一下MessageQueue对象的值是从哪来来的,然后再看enqueueMessage方法的实现。
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; }
可以看到我们mQueue是从mLooper中取出来的。 那这个Looper又是干什么的呢? 带着疑问,我继续看一下MessageQueue的enqueueMessage方法。
...... 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; } ...... }
enqueueMessage的方法代码量有点多,具体的逻辑看不太懂。 但是大概的意思是判断了一通,然后把message对象插入到链表中。代码分析到这里就有点蒙了 。 what… 我们从handler.sendxx方法,沿着调用栈,分析了一通,发现handler.sendxx方法就是把message插入到Looper对象的MessageQueue链表中。 那handleMessage方法啥时候调用呢? 不说清楚别想走 .哈哈。 别急,这个时候我们就需要回头想想,平时我们在主线程给子线程中发消息,需要构建子线程handler对象,然后还需要做什么呢? 对了。我们需要构建Looper然后,调用Looper的Loop方法。 例如下面这样:
public class MainActivity extends Activity { Handler handler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //向子线程发送 Message message=Message.obtain(); handler.sendMessage(message); new Thread(new Runnable() { @Override public void run() { Looper.prepare(); handler=new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //执行在子线程 } }; Looper.loop(); } }).start(); }}
我们想想,同样是handler发送消息,为什么子线程中需要调用Looper.prepare(), Looper.loop()呢,而主线不需要,还有就是发送消息和接收消息,为什么要调用Looper.prepare()和 Looper.loop()方法呢? 想清楚这两个问题,我们之前的疑惑就自然而然的解开了。
我们先看一下Looper.prepare()方法:
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
可以看到在prepare方法中,构建了一个Looper对象,然后放入到ThreadLocal对象中。看到这里我不禁想起之前看到的handle构造方法里面的那段代码:
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;
原来这个looper对象是用来构建handler的时候使用的呀。然后跟进去myLooper方法,
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
可以看到Looper对象是从ThreadLocal中取出的。 这里得说明一下ThreadLocal类的作用,这个类日常开发的时候很少使用。但是在一些场景用起来却是很适合。这个类可以保证每个线程都保存一份变量,线程与线程间的数据相互隔离。比如,我们在哪个线程中get出来的对象,就是我们之前在这个进程set进去的对象。
然后看一下Looper.myLooper()方法,
final Looper me = myLooper(); final MessageQueue queue = me.mQueue; ...... for (;;) { Message msg = queue.next(); // might block ...... try { msg.target.dispatchMessage(msg); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } ...... } }
Looper.myLooper()方法中代码比较多,我们只看关键代码,可以看到先是取出当前线程的looper对象,然后用一个无限循环从Looper中的queue中读取消息(queue.next()),然后传递给msg.target的dispatchMessage方法。这里msg的tag就是我们的handler对象。 分析到这里,我们就梳理清楚handler消息机制了,我们总结一下:先是handler发送消息,插入到当前线程Looper中MessageQueue链表中,然后在Looper的loop方法中无限循环从MessageQueue读取消息,传递给handler.dispatchMessage方法! 然后我们看一下dispatchMessage方法,代码如下:
/** * 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); } }
可以看到我们处理消息有3个顺序,首先是msg.callback,mCallback.handleMessage,handleMessage,跟上源码看一下赋值过程:首先是msg.callback的赋值过程,代码如下:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }
这个callback就是我们在handler.post的时候赋值的runable对象。然后回到dispatchMessage方法,如果
msg.callback不为空执行就执行handleCallback,代码如下:
private static void handleCallback(Message message) { message.callback.run(); }
所以我们知道handler的post方法也是执行在主线程中的。而且post处理消息的优秀级最高。
然后我们看一下mCallback,这个mCallback就是我们下面这种方式创建的handler的时候的Handler.Callback对象:
Handler handler=new Handler(new Handler.Callback() { @Override public boolean handleMessage(Message msg) { return false; } });
Handler.Callback处理消息的优先级也比handler.post低,但是高于handleMessage方法。
现在我们分析清楚了handler的消息机制,以及message是怎么传递和被处理的。然后我们回头看看主线程中的消息是怎么被处理的,它也应该和子线程一样使用handler一样,需要构建一个looper,然后调用looper的loop方法。代码在哪里呢,我们找一下:
public static void main(String[] args) { ...... Looper.prepareMainLooper(); ...... Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
原来ActivityThread的main方法中。我们知道,当我们应用程序启动的时候,首先Zygote fork出一个新的进程,然后会调用ActivityThread的main方法。 原来我们在主线程中使用handler的时候系统已经默认帮我们做了Looper的初始化工作。
分析到这里我们已经知道,handler发送消息之后是怎么传递的,会回调handler的哪个方法,子线程和主线程的looper初始化工作是怎么实现的。 但是还是有一个最大的疑问,为什么消息传递到handler的dispatchMessage方法中后,我们就发现如果是主线程的handler,dispatchMessage方法就执行在主线程,如果是子线程的handler,dispatchMessage就执行在子线程? 换句话说为什么是 dispatchMessage方法是执行在Looper所在的线程? >- -< 回答这个问题,因为dispatchMessage方法是在Looper.loop方法中回调的,所以当前执行在loop方法所在的线程。loop方法执行在Looper所在的线程。
然后我们先整理一下其他的几个特殊的方法:
Activity的runOnUiThread方法:
public final void runOnUiThread(Runnable action) { if (Thread.currentThread() != mUiThread) { mHandler.post(action); } else { action.run(); } }
可以看到如果不是在主线程,就调用handler的post方法(这里的这个mHandler对象是Activity的,构造在主线程中),如果是在主线程就直接执行。
View的post方法:
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Postpone the runnable until we know on which thread it needs to run. // Assume that the runnable will be successfully placed after attach. getRunQueue().post(action); return true; }
查看源码可知,这里的attachInfo!=null表示View已经添加到Window中,就执行主线程的handler.post方法(attachInfo.handler是在Activity的handler对象)。
如果View没有attach到window中,那就会在ViewRootImpl下一次performTraversals中执行。 这种情况比较复杂,可以看一下文章后面的参考链接中讲解View.post的文章。
总结几点:
- 1.handler的.dispatchMessage方法是执行在Looper所在的线程的。
- 2.每个线程的Looper只有一个,也就是说每个线程的MessageQueue只有一个,无论这个线程中有多少个handler。 ps:现在很多的性能监控组件会监控app的Ui卡顿情况,很多的实现思路就是,定时的向主线程的Looper发送消息,判断消息处理的时间间隔是否达到一定的时间。 从而判断ui线程是否卡顿。
参考链接:
http://blog.csdn.net/a740169405/article/details/69668957
- Android消息机制梳理
- 安卓核心基础知识梳理之Android消息处理机制
- android IPC通信机制梳理
- 【Android】从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及handler的内存泄露)
- 【知识梳理1】Android触摸事件机制
- Android知识梳理之事件传递机制
- Android View 事件分发机制梳理
- Android开发中的事件分发机制梳理
- android Handler 机制梳理
- Android杂谈(25)Handler机制梳理
- java 线程池讲解
- Keras:1 基础知识与安装
- POJ 3982 序列(高精度加法)
- 2017 Multi-University Training Contest
- 8.24--练习赛D题--Treats for the cows(区间DP)
- Android消息机制梳理
- 【C#学习】可空类型
- 程序员必备基础知识:通信协议——Http、TCP、UDP
- uva 10820 Send a Table 欧拉函数
- 【深入PHP 面向对象】读书笔记(五)
- 导入工程出错
- JSON串中的null与"null"
- expdp/impdp优化
- 【洛谷3384】【模板】树链剖分