在应用启动的时候,会执行程序的入口函数main(),main()里面会建立一个Looper对象,而后经过这个Looper对象开启一个死循环,这个循环的工做是,不断的从消息队列MessageQueue里面取出消息即Message对象,并处理。而后看下面两个问题:
循环拿到一个消息以后,如何处理?
是经过在Looper的循环里调用Handler的dispatchMessage()方法去处理的,而dispatchMessage()方法里面会调用handleMessage()方法,handleMessage()就是平时使用Handler时重写的方法,因此最终如何处理消息由使用Handler的开发者决定。
MessageQueue里的消息从哪来?
使用Handler的开发者经过调用sendMessage()方法将消息加入到MessageQueue里面。html
上面就是Android中消息机制的一个总体流程,也是 “Android中Handler,Looper,MessageQueue,Message有什么关系?” 的答案。经过上面的流程能够发现Handler在消息机制中的地位,是做为辅助类或者工具类存在的,用来供开发者使用。java
对于这个流程有两个疑问:android
这两个问题会在后面给出答案,下面先来经过源码,分析一下这个过程的具体细节:安全
首先main()方法位于ActivityThread.java类里面,这是一个隐藏类,源码位置:frameworks/base/core/java/android/app/ActivityThread.javaapp
public static void main(String[] args) { ...... Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
Looper的建立能够经过Looper.prepare()来完成,上面的代码中prepareMainLooper()是给主线程建立Looper使用的,本质也是调用的prepare()方法。建立Looper之后就能够调用Looper.loop()开启循环了。main方法很简单,很少说了,下面看看Looper被建立的时候作了什么,下面是Looper的prepare()方法和变量sThreadLocal:async
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 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)); }
很简单,new了一个Looper,并把new出来的Looper保存到ThreadLocal里面。ThreadLocal是什么?它是一个用来存储数据的类,相似HashMap、ArrayList等集合类。它的特色是能够在指定的线程中存储数据,而后取数据只能取到当前线程的数据,好比下面的代码:ide
ThreadLocal<Integer> mThreadLocal = new ThreadLocal<>(); private void testMethod() { mThreadLocal.set(0); Log.d(TAG, "main mThreadLocal=" + mThreadLocal.get()); new Thread("Thread1") { @Override public void run() { mThreadLocal.set(1); Log.d(TAG, "Thread1 mThreadLocal=" + mThreadLocal.get()); } }.start(); new Thread("Thread2") { @Override public void run() { mThreadLocal.set(2); Log.d(TAG, "Thread1 mThreadLocal=" + mThreadLocal.get()); } }.start(); Log.d(TAG, "main mThreadLocal=" + mThreadLocal.get()); }
输出的log是函数
main mThreadLocal=0 Thread1 mThreadLocal=1 Thread2 mThreadLocal=2 main mThreadLocal=0
经过上面的例子能够清晰的看到ThreadLocal存取数据的特色,只能取到当前所在线程存的数据,若是所在线程没存数据,取出来的就是null。其实这个效果能够经过HashMap<Thread, Object>来实现,考虑线程安全的话使用ConcurrentMap<Thread, Object>,不过使用Map会有一些麻烦的事要处理,好比当一个线程结束的时候咱们如何删除这个线程的对象副本呢?若是使用ThreadLocal就不用有这个担忧了,ThreadLocal保证每一个线程都保持对其线程局部变量副本的隐式引用,只要线程是活动的而且 ThreadLocal 实例是可访问的;在线程消失以后,其线程局部实例的全部副本都会被垃圾回收(除非存在对这些副本的其余引用)。更多ThreadLocal的讲解参考:Android线程管理之ThreadLocal理解及应用场景工具
好了回到正题,prepare()建立Looper的时候同时把建立的Looper存储到了ThreadLocal中,经过对ThreadLocal的介绍,获取Looper对象就很简单了,sThreadLocal.get()
便可,源码提供了一个public的静态方法能够在主线程的任何地方获取这个主线程的Looper(注意一下方法名myLooper(),多个地方会用到):oop
public static @Nullable Looper myLooper() { return sThreadLocal.get(); }
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; 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); } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } msg.recycleUnchecked(); } }
上面的代码,首先获取主线程的Looper对象,而后取得Looper中的消息队列final MessageQueue queue = me.mQueue;
,而后下面是一个死循环,不断的从消息队列里取消息Message msg = queue.next();
,能够看到取出的消息是一个Message对象,若是消息队列里没有消息,就会阻塞在这行代码,等到有消息来的时候会被唤醒。取到消息之后,经过msg.target.dispatchMessage(msg);
来处理消息,msg.target 是一个Handler对象,因此这个时候就调用到咱们重写的Hander的handleMessage()方法了。
msg.target 是在何时被赋值的呢?要找到这个答案很容易,msg.target是被封装在消息里面的,确定要从发送消息那里开始找,看看Message是如何封装的。那么就从Handler的sendMessage(msg)方法开始,过程以下:
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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
能够看到最后的enqueueMessage()方法中msg.target = this;
,这里就把发送消息的handler封装到了消息中。同时能够看到,发送消息其实就是往MessageQueue里面插入了一条消息,而后Looper里面的循环就能够处理消息了。Handler里面的消息队列是怎么来的呢?从上面的代码能够看到enqueueMessage()里面的queue是从sendMessageAtTime传来的,也就是mQueue。而后看mQueue是在哪初始化的,看Handler的构造方法以下:
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } 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的初始化很简单,首先取得Handler所在线程的Looper,而后取出Looper中的mQueue。这也是Handler为何必须在有Looper的线程中才能使用的缘由,拿到mQueue就能够很容易的往Looper的消息队列里插入消息了(配合Looper的循环+阻塞就实现了发送接收消息的效果)。
以上就是主线程中消息机制的原理。
那么,在任何线程下使用handler的以下作法的缘由、原理、内部流程等就很是清晰了:
new Thread() { @Override public void run() { Looper.prepare(); Handler handler = new Handler(); Looper.loop(); } }.start();
源码分析完了,下面看一下文章开头的两个问题:
这两个问题源码分析中已经给出答案,这里作一下总结,首先搞清楚如下对象在消息机制中的关系:
带着上面的一系列问题看源码就很清晰了,下面是知乎上的一个问答:
缘由很简单,循环里有阻塞,因此死循环并不会一直执行,相反的,大部分时间是没有消息的,因此主线程大多数时候都是处于休眠状态,也就不会消耗太多的CPU资源致使卡死。
这里说到binder线程,具体的实现细节没必要深究,考虑下面的问题:
主线程的死循环如何处理其它事务?
首先须要看懂这个问题,主线程进入Looper死循环后,如何处理其余事务,好比activity的各个生命周期的回调函数是如何被执行到的(注意这里是在同一个线程下,代码是按顺序执行的,若是在死循环这阻塞了,那么进入死循环后循环之外的代码是如何执行的)。
首先再看main函数的源码
Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } Looper.loop();
在Looper.prepare和Looper.loop之间new了一个ActivityThread并调用了它的attach方法,这个方法就是开启binder线程的,另外new ActivityThread()的时候同时会初始化它的一个H类型的成员,H是一个继承了Handler的类。此时的结果就是:在主线程开启loop死循环以前,已经启动binder线程,而且准备好了一个名为H的Handler,那么接下来在主线程死循环以外作一些事务处理就很简单了,只须要经过binder线程向H发送消息便可,好比发送 H.LAUNCH_ACTIVITY 消息就是通知主线程调用Activity.onCreate() ,固然不是直接调用,H收到消息后会进行一系列复杂的函数调用最终调用到Activity.onCreate()。
至于谁来控制binder线程来向H发消息就不深刻研究了,下面是《Android开发艺术探索》里面的一段话:
ActivityThread 经过 ApplicationThread 和 AMS 进行进程间通信,AMS 以进程间通讯的方式完成 ActivityThread 的请求后会回调 ApplicationThread 中的 Binder 方法,而后 ApplicationThread 会向 H 发送消息,H 收到消息后会将 ApplicationThread 中的逻辑切换到 ActivityThread 中去执行,即切换到主线程中去执行,这个过程就是主线程的消息循环模型。
这个问题就到这里,更多内容看知乎原文
和其余系统相同,Android应用程序也是依靠消息驱动来工做的。网上的这句话仍是颇有道理的。
《Android开发艺术探索》
Android中为何主线程不会由于Looper.loop()里的死循环卡死?
Android线程管理之ThreadLocal理解及应用场景
Android 消息机制——你真的了解Handler
Android Handler究竟是什么