Android(安卓)性能优化(二)Handler运行机制原理,源码分析
目录
前言
一、Looper轮询器
二、Handler处理者
三、MessageQueue消息队列
四、Message消息载体
五、小结
面试题:
前言
Handler机制是Android中非常重要的技术点,几乎所有的面试都会涉及到这个问题,那么为什么说它这么重要呢?因为Handler机制几乎贯穿了整个应用从启动到结束,包括Throwable抛出异常、View绘制、事件分发、Activity启动、Activity生命周期等都涉及到Handler机制。
我们知道,主线程中不建议耗时操作,子线程中不允许更新UI,所以一般我们在开发中都会Handler机制来保证线程安全。Handle运行依赖于Looper、MessageQueue和Message ,整体流程如下:
在应用启动时会开启一个主线程,默认会创建一个Looper对象关联当前线程,且系统规定一个线程只允许创建一个Looper实例对象。另外,创建Looper对象同时实例化了一个MessageQueue对象,因此一个线程中对应唯一的Looper对象和MessageQueue对象。主线程中完成Looper对象创建后会调用Looper.loop()方法,进行消息循环,不断地获取
MessageQueue
中的Message对象
,然后调用与Message
绑定的Handler
对象的dispatchMessage
方法实现接口回调,从而更新handle所在的主线程的UI。
流程图如下:
一、Looper轮询器
Looper是Handler机制运行工作的核心,所以我们先搞懂Looper是怎么回事很关键。我们知道应用的启动通过main方法作为入口,这个main方法在主线程中,查看这个类的main方法可以看到有两个方法:
Looper.prepareMainLooper()、Looper.loop()。
public static final void main(String[] args) { ... //主线程中调用Looper.prepareMainLooper()方法创建Looper Looper.prepareMainLooper(); if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } //主线程中调用Looper.loop(),开始轮询,取消息 Looper.loop(); ... }}
首先,ActivityThread首先调用的Looper.prepareMainLooper()方法,我们来看一下这个方法源码都做了些什么?
在Looper类中public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; }}
原来,该方法中首先调用本类中的prepare()方法创建Looper对象,并且把该对象setMainLooper()绑定到当前线程中。在这里既然是主线程调用的,那么该Looper对象自然是在主线程当中。继续看里面的prepare()这个方法:
在Looper类中public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } //把Looper绑定到当前线程 sThreadLocal.set(new Looper());}
这个方法只做了一个事情,就是保证当前线程只能存在一个Looper对象,如果已经存在就会抛出异常(Android规定一个线程只允许关联一个Looper)。
继续看new Looper()这个构造方法,我们可以看到:
private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread();}
Looper这个类的构造方法是private私有的,不允许外界直接new出来Looper对象,而最重要的是:在创建Looper的时候,同时创建了一个消息队列MessageQueue。所以请注意!MessageQueue是依赖Looper一起创建的。
然后,看ActivityThread调用Looper.loop()方法,开始轮询消息:
public static final void loop() { Looper me = myLooper(); //通过Looper对象,获取MessageQueue MessageQueue queue = me.mQueue; ... //死循环获取消息 while (true) { //调用MessageQueue的next()方法取消息,这个过程也是死循环 Message msg = queue.next(); if (msg != null) { ... //取到消息之后,交给发送该消息的Handler取处理消息 msg.target.dispatchMessage(msg); ... //回收消息,在Message中维护的有一个消息池 msg.recycle(); } } }
从上述源码中简单的注解,我们可以直观明白主线程的Looper和对应的MessageQueue之间最直接的关系了。但是我们带着一个问题如何把Message消息从子线程交给Handler在主线程中处理的?继续分析msg.target.dispatchMessage()源码:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) {//注意这个方法调用 return; } } handleMessage(msg); }}
我们可以知道Looper.loop()方法里的死循环是不断地获取MessageQueue
中的Message
,然后调用与Message
绑定的Handler
对象的dispatchMessage
方法。最后,我们终于明白原来Handler的handleMessage
就在这里调用的,其实就是一个接口回调啊。
二、Handler处理者
我们知道Handler的工作主要就是发送和处理消息。其实Handler的构造方法有多种,但都会获取当前线程的Looper对象和MessageQueue对象。这个其实也没什么,看看就好:
//Handler的构造方法1public Handler() { ... //获取当前线程的Looper对象 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 = null;}//Handler的构造方法2public Handler(Callback callback) { ... 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;}//Handler的构造方法3public Handler(Looper looper) { mLooper = looper; mQueue = looper.mQueue; mCallback = null;}//Handler的构造方法4public Handler(Looper looper, Callback callback) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback;}
发送消息可以通过一系列send方法,也可以通过一系列post方法,不过post方法最终还是通过send方法去实现。
用send方法,最终也会调用一个方法如下:
//在Handler中public boolean sendMessageAtTime(Message msg, long uptimeMillis){ boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { //调用msg.target的方法把Message和发送它的Handler绑定,所以Looper能够把正确的Message交给发送它的Handler处理 msg.target = this; //调用MessageQueue的enqueueMessage方法把消息加入消息队列 sent = queue.enqueueMessage(msg, uptimeMillis); } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent;}
可以看到,Handler发送消息的过程就是向消息队列中插入一条消息。前面已经讲到,MessageQueue调用next方法去轮询消息,那么当MessageQueue拿到消息之后,把消息传递给Looper,最终交给Handler去处理,即dispatchMessage方法会被调用。此时,Handler开始处理消息。值得一提的是,在消息队列中可能有不同Handler发送的多个消息,通过在发送消息的时候把Message和发送它的Handler绑定,Looper就会把消息正确的交给发送它的Handler来处理。dispatchMessage方法如下:
//Handler中public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); }}
Handler处理消息的过程:
第一步,查看Message的callback是否为null,不为null就通过handleCallback(msg)方法处理消息。这里的callback实际上就是一个Runnable对象。如果以post方式去发送消息,最终就会调用handleCallback(msg)方法去处理,这个方法内容为:
private final void handleCallback(Message message) { message.callback.run(); }
第二步,检查mCallBack是否为null,如果不为null就调用mCallBack的handleMessage(msg)方法。这个mCallBack是CallBack接口的子类对象,前面已经说过Handler的构造方法中有两个可以用到这个CallBack。如果mCallBack为null,最终就会调用Handler的handleMessage(msg)方法,这个方法通常是在创建Handler时被使用者重写的方法。
需要说明:在主线程当前我们创建Handler时并没有自己创建Looper对象,这是因为主线程已经为我们创建好了;如果要在子线程当前创建Handler,一定要在之前创建Looper对象,即调用Looper.prepare()方法。
三、MessageQueue消息队列
前面的内容已经讲了很多关于MessageQueue的东西,这里就总结下了。MessageQueue主要包含两个操作:插入消息(enqueueMessage方法)和读取消息(next方法)。读取的过程也伴随着删除操作。MessageQueue的的内部实际上是通过一个单链表的数据结构来维护消息列表,这主要也是因为单链表在插入和删除上比较有优势。
final boolean enqueueMessage(Message msg, long when) { ... Message p = mMessages; if (p == null || when == 0 || when < p.when) { // 当前发送的message需要马上被处理调,needWake唤醒状态置true msg.next = p; mMessages = msg; needWake = mBlocked; // new head, might need to wake up } else { // 当前发送的message被排队到其他message的后面,needWake唤醒状态置为false Message prev = null; while (p != null && p.when <= when) { prev = p; p = p.next; } msg.next = prev.next; prev.next = msg; needWake = false; // still waiting on head, no need to wake up } } // 是否唤醒主线程 if (needWake) { nativeWake(mPtr); } return true;}
enqueueMessage的主要操作其实就是单链表的插入操作,根据时间看当前发送的Message是否需要马上处理。这个enqueueMessage方法是Handler发送消息的时候调用。
下面来看next方法:
final Message next() { int pendingIdleHandlerCount = -1; // -1 only during first iteration int nextPollTimeoutMillis = 0; for (;;) { if (nextPollTimeoutMillis != 0) { Binder.flushPendingCommands(); } nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); final Message msg = mMessages; if (msg != null) { final long when = msg.when; if (now >= when) { mBlocked = false; mMessages = msg.next; msg.next = null; if (Config.LOGV) Log.v("MessageQueue", "Returning message: " + msg); return msg; } else { nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); } } else { nextPollTimeoutMillis = -1; } ...}
注意"return msg",是next方法返回一个Message对象。next方法中也是采用阻塞的方式去获取消息队列中的消息,一旦有消息立即返回并且将它从单链表中移除。如果没有消息就一直阻塞。前面已经提到,这个取消息的next方法是在Looper的loop()方法中调用。
四、Message消息载体
Message只有一个无参构造方法,但是Message有多个obtain静态方法来返回Message对象。
采用哪种方式创建Message对象都可以,但是建议采用obtain方法来创建。这是因为Message通过在内部构建一个链表来维护一个被回收的Message对象池。当用户调用obtain方法时会优先从池中获取,如果池中没有则创建新的Message对象。同时在使用完毕之后,进入池中以便于复用。这个在Looper.loop()方法可以看到一点端倪,在使用完毕时候调用了Message的recycle()方法。
下面是obtain方法创建Message对象的流程:
public static Message obtain() { synchronized (sPoolSync) { if (sPool != null) { Message m = sPool; sPool = m.next; m.next = null; sPoolSize--; return m; } } return new Message();}
五、小结
- 在应用启动时会开启一个主线程,默认创建一个Looper对象绑定当前线程,且不同线程间的Looper不能共享。
- 一个Thread线程对应一个Looper对象、对应一个MessageQueue对象。
- 主线程中调用Looper的loop()方法,会开启消息循环,不断的从消息队列中拿到消息。
- Looper拿到消息之后调用Handler的dispatchMessage方法来处理消息。
- 一个Thread线程对应多个Handler。
- Handler通过send、post方法,把消息加入消息队列MessageQueue中。
- 子线程中创建Handler需先创建Looper对象,Thread与Handler共享一个Looper与MessageQueue。
面试题:
Looper.loop()为什么不会阻塞主线程?
IdHandler(闲时机制)?
postDelay()的具体实现?
post()与sendMessage()区别?
使用Handler时怎么解决内存泄漏的问题的?
更多相关文章
- 关于Handler的理解
- Android(安卓)数据存储和文件使用案例分析
- ExifInterface使用,Android(安卓)2.0新增类
- Android透明无边框圆形进度条之最简单实现
- android 使用AsyncTask实现异步下载文件
- android通过更改hosts免优酷广告方法
- 如何优雅的避免android(安卓)运行时崩溃
- android 热修复之类加载机制
- Android中Intent概述及使用