Android的消息机制深度分析
来源:互联网 发布:ubuntu使用管理员账户 编辑:程序博客网 时间:2024/06/15 14:14
1、Looper的工作原理
Looper在Android的消息机制中扮演着消息循环的角色,Looper.loop方法是一个死循环,具体来说就是不停从MessageQueue中查看是否有新消息,如果有新消息,就会立即处理,否则就会一直阻塞在那里。如果调用Looper.quit或者Looper.quitSafely,那么Looper会调用MessageQueue的quit或者quitSafely方法来通知消息队列退出,当消息队列被标记为退出状态的时候,它的next方法就会返回为null,loop方法就会跳出死循环,loop方法跳出死循环的唯一方法就是MessageQueue的next返回为null。
Looper的构造方法:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }在构造方法中它会创建一个MessageQueue,然后将当前线程的对象保存起来。
创建Handler对象的前提是当前线程存在Looper对象,如果没有Looper线程就会报错。那么如何为一个线程创建Looper呢?其实很简单,通过Looper.prepare()即可为当前线程创建一个Looper,通过Loopre.loop()来开启消息循环,如下所示:
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); } }).start();
Looper.loop()的代码实现如下:
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); if (ident != newIdent) { Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent) + " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what); } msg.recycleUnchecked(); } }通过不断的循环调用queue.next()方法从队列中取出msg,如果取出的为null,则return,也就是我们上面讲的调用了Looper.quit()方法。如果不为空,则执行msg.targrt.dispatchMessage(msg)方法,这个msg.targrt正是当前Looper所在线程的handler对象。
2、Handler的工作原理
Handler的作用主要包含消息的发送和接收过程。消息的发送可以动过post的一系列方法以及send的一些列方法来实现,发送一条消息的过程如下:
public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); }
public final boolean sendMessageDelayed(Message msg, long delayMillis) { if (delayMillis < 0) { delayMillis = 0; } return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); }
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); }可以看到,Handler发送消息的过程仅仅是向消息队列中插入一条消息。
再看一下Handler.post(Runnable)的执行过程:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }可以发现post最终也是调用senMessageDelayed方法来实现的,我们来看一下getPostMessage(r)方法的代码实现:
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); m.callback = r; return m; }首先通过Message.obtain()方法获得一个Message对象,这个obtain实际上是个单例模式的实现,然后将参数r赋值给Message的Runnable对象callback。
前面讲的Looper的loop()取得消息后最终交给Handler的dispatchMessage处理,dispatchMessage方法的实现如下:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }当msg.callback不为空的时候,执行handleCallback(msg)方法,实现如下:
private static void handleCallback(Message message) { message.callback.run(); }直接执行callback的run()方法。
当msg.callback为空同时mCallback为空的时候,就会执行handleMessage()方法。
3、MessageQueue的原理
MessageQueue的原理就非常简单,首先MessageQueue并不是一个真的队列,而是一个链表,链表的作用就是为了方便进行插入和删除。它主要有两个方法,一个是next()方法,负责从消息队列中取出消息,另外一个是enqueMessage()方法,主要是负责想笑兮队列中插入一条消息。
4、需要注意的问题
下面我们通过一个场景来观察一个关于执行线程的问题,代码如下:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.e("MainActivity", "11111 " + Thread.currentThread()); final Handler handler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: Log.e("MainActivity", "22222 " + Thread.currentThread()); break; default: break; } } }; new Thread(new Runnable() { @Override public void run() { Message msg = Message.obtain(); msg.what = 1; handler.sendMessage(msg); Log.e("MainActivity", "33333 " + Thread.currentThread()); } }).start(); new Thread(new Runnable() { @Override public void run() { handler.post(new Runnable() { @Override public void run() { Log.e("MainActivity", "44444 " + Thread.currentThread()); } }); } }).start(); }对应的日志输出如下:
08-24 13:35:11.916 6250-6250/com.weilei.fragdemo E/MainActivity: 11111 Thread[main,5,main]08-24 13:35:11.965 6250-6266/com.weilei.fragdemo E/MainActivity: 33333 Thread[Thread-175,5,main]08-24 13:35:12.067 6250-6250/com.weilei.fragdemo E/MainActivity: 22222 Thread[main,5,main]08-24 13:35:12.067 6250-6250/com.weilei.fragdemo E/MainActivity: 44444 Thread[main,5,main]可以发现,handler的handleMessage()和通过handler.post的Runnable都是在主线程里面执行的。
实际上具体在哪个线程里面执行主要看当前的Looper在哪个线程,因为所有的消息最终都是通过Looper来获取,然后交给Looper所在线程的Handler对象去处理。在主线程可以直接通过Handler handler = new Handler()来构建,当然Handler也可以在子线程,通过Handler handler = new Handler(Looper.getMainLooper())创建一个主进程的Handler。
另外:view.post(runnable)实际上也是调用handler.post( )方法,在执行过程中首先构造一个handler,然后调用它的post方法。
(2)看第二个例子;
new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Handler handler = new Handler(); handler.post(new Runnable() { @Override public void run() { Log.e("MainActivity", "test " + Thread.currentThread()); } }); Looper.loop(); } }).start();输出日志如下:
08-24 16:31:05.043 9256-9256/com.weilei.fragdemo E/MainActivity: 11111 Thread[main,5,main]08-24 16:31:05.140 9256-9282/com.weilei.fragdemo E/MainActivity: test Thread[Thread-324,5,main]通过这个例子可以印证我们上面的总结,即Runnable具体在哪个线程执行,关键是看构建Handler的Looper对象在哪个线程。
注意:Looper.loop()方法一定要在post之后执行,否则就会一直处于阻塞状态。
(3)在看第三个例子
public void onAttachedToWindow() { super.onAttachedToWindow(); new Thread(new Runnable() { @Override public void run() { Log.e("MainActivity", "33333-- "); v.post(new Runnable() { @Override public void run() { Log.e("MainActivity", "33333 " + Thread.currentThread()); } }); } }).start(); }输出结果如下:
08-24 16:36:41.670 14136-14136/com.weilei.fragdemo E/MainActivity: 11111 Thread[main,5,main]08-24 16:36:41.722 14136-14162/com.weilei.fragdemo E/MainActivity: 33333-- 08-24 16:36:41.998 14136-14136/com.weilei.fragdemo E/MainActivity: 33333 Thread[main,5,main]这个和我们的猜想一样,最终在主线程里面执行。另外有一点需要注意的是:view.post()一定得在onAttachToWindow()之后才能生效,这也是我们为什么要在onAttachToWindow()的super之后执行这个post方法的原因。原因我们可以看源码是怎么实现的,View的post源码如下:
public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueue().post(action); return true; }当view处于attached状态的时候,attachInfo将不为空,此时直接执行handler.post(action);但是当view不处于attached状态的时候,就会执行RnQueue.post(action)方法,这个方法的作用是什么呢?还是直接看代码:
void post(Runnable action) { postDelayed(action, 0); }
void postDelayed(Runnable action, long delayMillis) { HandlerAction handlerAction = new HandlerAction(); handlerAction.action = action; handlerAction.delay = delayMillis; synchronized (mActions) { mActions.add(handlerAction); } }可以看到这个方法只是将action方法mActions中趋缓存而已,直接上并不会句执行action中的run方法,因此如果我们想保证我们的runnable能够得到执行,就得先保证View处于attached状态,分析到此结束。
可能有人在想,这里的mActions中的action会在什么时候执行了,我们在ViewRootImpl中的可以看到一个方法:
void executeActions(Handler handler) { synchronized (mActions) { final ArrayList<HandlerAction> actions = mActions; final int count = actions.size(); for (int i = 0; i < count; i++) { final HandlerAction handlerAction = actions.get(i); handler.postDelayed(handlerAction.action, handlerAction.delay); } actions.clear(); } }在这个中遍历所有的action,然后调用handler.post方法。同时这个方法又是在什么时候执行的呢?直击上这个方法是在ViewRootImpl的performTraversals( )中执行的,这个方法我们之前在研究View的绘制流程中见过,它正是view的绘制入口方法。
到此为止,关于Andorid的消息机制已全部分析完毕,任何问题都可以从源码中找到答案。另外补充一下,一般刷新UI除了用handler的post活着sendMessage外,还可以调用Activity中的runOnUiThread()方法和view.post(runnable)方法,其实他们的原理都是相同的。
- Android的消息机制深度分析
- Android的消息机制分析
- Android的消息机制分析
- Android消息机制分析
- Android消息机制分析
- Android的消息处理机制源码分析
- 结合源码分析android的消息机制
- Android的消息机制简要分析
- Android 消息机制的源码分析
- Android的消息机制源码分析
- Android消息处理机制深度解析笔记
- android消息机制源码分析
- Android 消息机制源码分析
- Android消息机制源码分析
- Android 消息传递机制分析
- Android消息机制初步分析
- Android消息机制源码分析
- Android 消息机制源码分析
- 弹性计算服务(ECS)
- 有关于Unity3D中有限状态机简单的思路
- 在linux下使用curl访问 多参数url GET参数问题
- 简明 VIM 练级攻略
- 无法加载指定的元数据资源
- Android的消息机制深度分析
- 成为Java GC专家(4)—Apache的MaxClients参数详解及其在Tomcat执行FullGC时的影响
- java中的关键字
- 【Mybatis从0到1-016】Spring与MyBatis整合mapper开发DAO(推荐使用)
- tensorflow使用中的坑:module'tensorflow'has no attribute 'sub'
- 开发框架汇总
- 2017年迄今最新人工智能资源盘点 [转载]
- 学习基础Java(一)
- 1068. 万绿丛中一点红(20)