用文字札记描绘自己 android 学习之路
【导航】
android 更新 ui 基本常用方法
android ui 线程检查机制
android 线程更新 UI 机制
讲了对 Ui 线程更新的方法和见解,然后接着讲了,这里来详细分析下更新 Ui 的核心——Android 中消息系统模型。当然,这里要讲的其实也已经不再简简单单地是更新 Ui 的范畴了。不过还是很值得学习和分析一下。另外,其实网上关于这方面的讲解也有很多了,本篇也是综合整理并用自己的理解加以描述和概括。同时也感谢有更高造诣的大大能予以批评指正。
Android 中的消息机制主要有如下几个要点,这里也着重围绕这些内容来讲解:
1. Handler 调度消息和 runnable 对象在不同线程中执行相应动作。
2. Looper 消息泵,用来为一个线程开启一个消息循环
3. MessageQueue 遵循 FIFO 先进先出规则的消息队列,以链表形式存取 Message, 供 looper 提取
(为了深入了解,已从源码从提取这几个类
方便新手下载查看)
为了方便分析,借用一下找到的模型图综合看一下:
首先在一个线程中初始化一个 looper 并 prepare(准备),然后创建该 looper 对象的处理对象 Handler,接着当需要交互变更时,可以在其他线程(或自身线程)中使用 handler 发消息至该消息队列 MessageQueue,最后 looper 会自动有序抽取消息(没有消息则挂起),交给 handler 执行消息处理逻辑。
Orz, 我的概念描述还是一塌糊涂,还是转代码说明吧:
比如我们有个线程专门负责一类处理逻辑, 并且只允许该线程来处理这类逻辑, 那么我们怎么做到呢?
1. 在一个线程里边定义一个 Looper
- Looper.prepare(); //稍微有点儿多,详细见下文
2. 定义一个处理消息的 Handler
- handler = new Handler() {@Override public void handleMessage(Message msg) {
- super.handleMessage(msg);
- //处理逻辑
- }
- };
3. 启动 looper,并开始工作,轮询消息
- Looper.loop(); //详细见下文
- //要停止的话,使用Looper.quit();
4. 在其他线程将要处理的数据 data 或回调对象 callback 以消息 Message 模式通过 Handler 发至该消息队列 MessageQueue
- handler.sendMessage(msg)
5.Looper 的 loop() 方法会从消息队列中取到该消息并唤醒处理逻辑
- //即loop()方法中的代码
- for (;;) { //显然这个死循环一直在等待消息到来并处理
- Message msg = queue.next(); // 取一条消息
- if (msg == null) {
- return;
- }
- msg.target.dispatchMessage(msg); //调用消息绑定的handler执行处理逻辑
- //other code....
- }
6.handler 跳转到执行处理逻辑的过程
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) { //如果有回调,则调用
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
- //调用回调方法
- private static void handleCallback(Message message) {
- message.callback.run();
- }
以上便是整个消息系统的过程,后边我们会逐个分析精讲。
在 ActivityManagerService 为 Android 应用程序创建新的进程并启动 activity 时候,主线程 ActivityThread 首先被创建。该进程 Process.java@start("android.app.ActivityThread",…) 会加载执行 ActivityThread 的静态成员函数 main, 打开该方法:
- public static void main(String[] args) {
- //other code.. 我们只看有用的部分,其他暂略过
- Looper.prepareMainLooper(); //准备looper,注,绑定的为当前主线程
- ActivityThread thread = new ActivityThread(); //开启一个新ActivityThread线程
- thread.attach(false); //最后执行到activity
- //other code..
- Looper.loop(); //启动looper
这个静态函数做了两件事情,一是在主线程中创建了一个 ActivityThread 实例,二是通过 Looper 类使主线程进入消息循环。
然后,代码经过一系列逻辑 (ActivityThread.attach->IActivityManager. attachApplication -> attachApplicationApplicationThread.scheduleLaunchActivity ->… ->ActivityThread.performLaunchActivity ),最终会调用 activity 的 attach 方法。
我们打开 activity 类。可以看到,它定义了 uiThread 和 Handler 参数
- ActivityThread mMainThread; //对应上边的ActivityThread线程
- private Thread mUiThread; //主Ui线程
- final Handler mHandler = new Handler(); //这个handler就是activity用来处理Ui的了。我们自己定义的handler其实等于重新定义了这个mHandler;
我们来看 activity 的 attach 方法:
- final void attach(Context context, ActivityThread aThread, Instrumentation instr, IBinder token, int ident, Application application, Intent intent, ActivityInfo info, CharSequence title, Activity parent, String id, NonConfigurationInstances lastNonConfigurationInstances, Configuration config, IVoiceInteractor voiceInteractor) {
- mUiThread = Thread.currentThread(); //当前主线程Ui线程
- mMainThread = aThread; //对应上边的ActivityThread线程
- }
所以,当我们要更新 UI 的时候,都会用到 sendMessage, 比如使用 runOnUiThread,来看下这个方法
- public final void runOnUiThread(Runnable action) {
- /**
- *如果当前线程不为Ui主线程则使用定义好的mHandler
- */
- if (Thread.currentThread() != mUiThread) {
- mHandler.post(action);
- } else {
- action.run();
- }
- }
打开 post 方法:
- public final boolean post(Runnable r) {
- return sendMessageDelayed(getPostMessage(r), 0);
- }
还是熟悉的配方,还是熟悉的味道。。封装成消息,然后发送出去。好,我们再回头看看最初第一篇文章里的四种方法:
1.new 一个 handler 来 sendMessage();
2. 利用 new handler 来 post
不过是把上边已经定义好 Activity 的 mHandler 重新定义了一遍,然后封装成消息发送出去;
3.runOnUiThread
同样不过是用了 Activity 的 mHandler 发送消息;
4.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;
- }
对于 AttachInfo 应该不算陌生吧,附加 view 到 Activity 的时候会把 activity 的一些属性附加给 AttachInfo,这里同样调用取得 mHandler 然后再 post。。绕了一圈又回来了。
综上看来,整个 UI 更新机制其实就是 Android 消息系统模型的一个简单实现。至此,我们的更新 UI 部分也算讲完了,那么作为补充部分,还是从源码上完整细致的过一下整个消息系统模型吧。
这里通过剖析源码来理解各部分的具体实现,再结合前面讲的内容加以融会贯通,便于深入理解最终达到在不同场景的熟练使用。
我们将按照顺序来逐个查看。
首先说说消息对象,毕竟其他类操作的最基本元素也都是它。
既然提到消息池又在前面讲了利用 obtain() 节省内存资源,那么我们就看下这个 obtain()
- /**
- *从消息池中返回一个新的消息实例,避免我们通常情况下分配新对象。
- */
- public static Message obtain() {
- synchronized(sPoolSync) {
- if (sPool != null) {
- Message m = sPool;
- sPool = m.next;
- m.next = null;
- sPoolSize--;
- return m;
- }
- }
- return new Message();
- }
然后就是基于此方法的一系列运用:先调用 obtain() 方法,然后把获取的 Message 实例的 各种参数赋值传参。
- //取一个消息对象,把已存在的消息内容赋值过去
- public static Message obtain(Message orig) {
- Message m = obtain();
- m.what = orig.what;
- m.arg1 = orig.arg1;
- m.arg2 = orig.arg2;
- m.obj = orig.obj;
- m.replyTo = orig.replyTo;
- if (orig.data != null) {
- m.data = new Bundle(orig.data);
- }
- m.target = orig.target;
- m.callback = orig.callback;
- return m;
- }
- public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {
- Message m = obtain();
- m.target = h;
- m.what = what;
- m.arg1 = arg1;
- m.arg2 = arg2;
- m.obj = obj;
- return m;
- }
- //调用obtain并赋值,不再一一列出
- public static Message obtain(Handler h) { //..}
- public static Message obtain(Handler h, Runnable callback) { //..}
- public static Message obtain(Handler h, int what) { //...}
- public static Message obtain(Handler h, int what, Object obj) { //...}
- public static Message obtain(Handler h, int what, int arg1, int arg2) { //...}
然后就是回收释放 recycle, 它返回一个消息池实例。释放之后将不能再使用此方法。
- public void recycle() {
- clearForRecycle();
- synchronized(sPoolSync) {
- if (sPoolSize < MAX_POOL_SIZE) {
- next = sPool;
- sPool = this;
- sPoolSize++;
- }
- }
- }
- //清空所有数据
- void clearForRecycle() {
- flags = 0;
- what = 0;
- arg1 = 0;
- arg2 = 0;
- obj = null;
- replyTo = null;
- when = 0;
- target = null;
- callback = null;
- data = null;
- }
- //拷贝消息内容
- public void copyFrom(Message o) {
- this.flags = o.flags & ~FLAGS_TO_CLEAR_ON_COPY_FROM;
- this.what = o.what;
- this.arg1 = o.arg1;
- this.arg2 = o.arg2;
- this.obj = o.obj;
- this.replyTo = o.replyTo;
- if (o.data != null) {
- this.data = (Bundle) o.data.clone();
- } else {
- this.data = null;
- }
- }
后边就是 get 和 set 方法以及 Parcelable 的读写。
先看他所定义的属性:
- public class Looper {
- private static final String TAG = "Looper";
- static final ThreadLocal sThreadLocal = new ThreadLocal();
- private static Looper sMainLooper; //唯一对应一个主线程的looper静态实例
- final MessageQueue mQueue; //消息队列
- final Thread mThread; //当前绑定线程
- volatile boolean mRun; //是否允许退出
- private Printer mLogging; //日志打印
- //....各种方法体....
- }
通常操作系统都为线程提供了内部存储空间,一个线程对应一个内存空间,因此这里很方便的为一个线程定义唯一对应的 looper 实例:ThreadLocal<Looper> 这个有点类似 C 中申请内存大小 *malloc(sizeof Looper),或者我们可以简单理解为只作用于当前线程的 new Looper.
而 sMainLooper 是当前应用程序的主线程 looper,区别是适用于主线程。
我们再看他的构造方法:
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- mRun = true;
- mThread = Thread.currentThread();
- }
此构造方法是私有的,即不允许在外部实例化,这样通过单例模式保证外部从该线程取得 looper 唯一。另外它主要初始化了 mQueue 、mRun 和 mThread 几个属性,并绑定了当前线程。找一下它调用实例化的部分:
- //重载,主要看下边的prepare(boolean quitAllowed)方法
- public static void prepare() {
- prepare(true);
- }
- /**
- *初始化当前线程作为Looper并存为本地变量,
- *并由此来创建handler和处理程序
- *
- *@quitAllowed 是否允许退出(循环取消息)
- *通过调用loop()和quit()来开启和结束循环
- */
- private static void prepare(boolean quitAllowed) {
- if (sThreadLocal.get() != null) { //保证一个线程唯一对应一个Looper
- throw new RuntimeException("Only one Looper may be created per thread");
- }
- sThreadLocal.set(new Looper(quitAllowed)); //在线程中初始化一个实例
- }
sThreadLocal 的 get 和 set,负责在内存中存取与线程唯一对应的 looper。
同时我们会注意到有两个方法 prepareMainLooper 和 getMainLooper:
- /**
- *初始化当前线程作为Looper并作为android应用的取消息逻辑,
- *是由当前运行环境来创建,不需要手动调用
- */
- public static void prepareMainLooper() {
- prepare(false);
- synchronized(Looper.class) { //加锁,保证实例化唯一一个looper
- if (sMainLooper != null) {
- throw new IllegalStateException("The main Looper has already been prepared.");
- }
- sMainLooper = myLooper();
- }
- }
- /**
- * 返回当前应用程序主线程的looper实例
- */
- public static Looper getMainLooper() {
- synchronized(Looper.class) {
- return sMainLooper;
- }
- }
这部分是共应用程序初始化的时候调用的,我们一般用不到,不过也可以看出只是初始化了主线程的 looper。
好的,基本的初始化工作也已经完成了,来看该类中的核心部分,循环取消息的 loop()
- /**
- * 启动队列的循环取消息操作,直到调用quit()退出
- */
- 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;
- // 确保当前线程是本地进程的唯一标示
- Binder.clearCallingIdentity();
- final long ident = Binder.clearCallingIdentity();
- //开始循环取消息操作
- for (;;) {
- Message msg = queue.next(); //取下一条消息
- if (msg == null) {
- // 如果消息队列没有消息则挂起
- return;
- }
- // 打印日志部分
- Printer logging = me.mLogging;
- if (logging != null) {
- logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);
- }
- //调用消息处理逻辑(回调和执行handler处理)
- msg.target.dispatchMessage(msg);
- if (logging != null) {
- logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
- }
- // 确保在处理消息逻辑时当前线程并没有被打断
- 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.recycle();
- }
- }
基本都有备注,不用过多解释了。msg.target.dispatchMessage 前面已经讲过,后便可能会在拉出来遛遛。
其他再就是基本的 get 和打印和异常捕获相关的了,有兴趣的可以自己去看一下。
类的描述简要翻译一下:
MessageQueue 是一个低级类,负责维护一个需要被 Looper 派发处理的消息列表。其消息对象是通过 hanlder 绑定到 looper 上的,而不是直接添加到消息队列中去的。
其实即 MessageQueue 只是一个消息队列,提供给 handler 加入和 Looper 取出消息操作,这两个接口分别是 enqueueMessage(Message msg, long when) 和 next()。
先看属性:
- public class MessageQueue {
- // 消息队列是否可以退出
- private final boolean mQuitAllowed;
- Message mMessages; //message实例(类似链表)
- //存放 IHandler的list和数组
- private final ArrayList mIdleHandlers = new ArrayList();
- private IdleHandler[] mPendingIdleHandlers;
- private boolean mQuiting; //Thread是否退出
- // 判断next()是否因一个非零的超时pollOnce()而处于阻塞等待
- private boolean mBlocked;
- //标志位,表示阻碍是否是由于消息的空target(handler)引起
- private int mNextBarrierToken;
- //native code部分略过
- @SuppressWarnings("unused") private int mPtr; // used by native code
- private native void nativeInit();
- private native void nativeDestroy();
- private native void nativePollOnce(int ptr, int timeoutMillis);
- private native void nativeWake(int ptr);
我们来了解一下 MessageQueue 内部定义的 IdleHanlder 接口:
这是一个提供给线程,用来阻塞等待更多消息的回调接口。
- public static interface IdleHandler {
- boolean queueIdle();
- }
queueIdle() 方法会在该消息队列处理完所有消息并且不会有新消息时候调用,返回 true 则该闲置 idlehandler 保持活跃,否则(false)移除该 idleHandler。当然,如果还有消息在队列中等待,并且这些消息是在接下来的时间才被处理,那么 queueIdle() 也会被调用。
对于添加和移除 idlehandler 我们简要略过.
- public final void addIdleHandler(IdleHandler handler) {
- if (handler == null) {
- throw new NullPointerException("Can't add a null IdleHandler");
- }
- synchronized(this) {
- mIdleHandlers.add(handler);
- }
- }
- public final void removeIdleHandler(IdleHandler handler) {
- synchronized(this) {
- mIdleHandlers.remove(handler);
- }
- }
取消息 next()
- final Message next() {
- int pendingIdleHandlerCount = -1; //空闲的idleHandler个数
- int nextPollTimeoutMillis = 0; //下次取消息的时间
- for (;;) {
- if (nextPollTimeoutMillis != 0) {
- Binder.flushPendingCommands(); //刷新等待命令
- }
- nativePollOnce(mPtr, nextPollTimeoutMillis); //更新下次取消息时间
- //取消息锁
- synchronized(this) {
- if (mQuiting) { //线程正退出
- return null;
- }
- // 尝试取下一条消息并返回
- final long now = SystemClock.uptimeMillis();
- Message prevMsg = null;
- Message msg = mMessages;
- if (msg != null && msg.target == null) {
- //查找下一个不为空且不是异步的消息
- do {
- prevMsg = msg;
- msg = msg.next;
- } while ( msg != null && ! msg . isAsynchronous ());
- }
- if (msg != null) {
- if (now < msg.when) {
- // 如果当前消息并没到指定时间,则等待nextPollTimeoutMillis 后执行取操作
- nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
- } else {
- // 取一条消息
- mBlocked = false;
- if (prevMsg != null) {
- prevMsg.next = msg.next;
- } else {
- mMessages = msg.next;
- }
- msg.next = null;
- if (false) Log.v("MessageQueue", "Returning message: " + msg);
- msg.markInUse();
- return msg;
- }
- } else {
- // 当没有消息时,下次取操作的时间间隔设置为-1
- nextPollTimeoutMillis = -1;
- }
- // 如果是首次闲置,则获取需要运行的空闲hanlder数量。闲置的hanlder只有在消息队列为空或者当前时间没有消息被处理的时候等待
- if (pendingIdleHandlerCount < 0 && (mMessages == null || now < mMessages.when)) {
- pendingIdleHandlerCount = mIdleHandlers.size();
- }
- if (pendingIdleHandlerCount <= 0) {
- // 如果没有闲置handler等待,则消息队列进入阻塞等待
- mBlocked = true;
- continue;
- }
- if (mPendingIdleHandlers == null) {
- mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
- }
- mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
- }
- // 运行闲置handler,我们只有在首次迭代时运行下边这段代码
- for (int i = 0; i < pendingIdleHandlerCount; i++) {
- final IdleHandler idler = mPendingIdleHandlers[i];
- mPendingIdleHandlers[i] = null; //释放闲置hanlder
- boolean keep = false;
- try {
- keep = idler.queueIdle();
- } catch(Throwable t) {
- Log.wtf("MessageQueue", "IdleHandler threw exception", t);
- }
- if (!keep) {
- synchronized(this) {
- mIdleHandlers.remove(idler);
- }
- }
- }
- //重置闲置handler数量
- pendingIdleHandlerCount = 0;
- //当回收闲置handler时候可能有新消息被放进来,所以更新下次取消息时间重新执行
- nextPollTimeoutMillis = 0;
- }
- }
插入消息 enqueueMessage(Message msg, long when)
- final boolean enqueueMessage(Message msg, long when) {
- //如果消息正在被使用或者消息的处理handler为空,都会抛异常
- if (msg.isInUse()) {
- throw new AndroidRuntimeException(msg + " This message is already in use.");
- }
- if (msg.target == null) {
- throw new AndroidRuntimeException("Message must have a target.");
- }
- boolean needWake;
- synchronized(this) {
- if (mQuiting) { //如果线程已退出,则抛出异常
- RuntimeException e = new RuntimeException(msg.target + " sending message to a Handler on a dead thread");
- Log.w("MessageQueue", e.getMessage(), e);
- return false;
- }
- msg.when = when;
- Message p = mMessages;
- if (p == null || when == 0 || when < p.when) {
- // 如果消息处理时间等于0或者小雨队列头的处理时间,则将该消息插至消息队列头
- msg.next = p;
- mMessages = msg;
- needWake = mBlocked;
- } else {
- //将消息插入对位,通常我们不需要唤醒事件,除非消息队列处在阻塞状态并且这条消息是队列中最早的异步消息
- 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;
- }
- }
- if (needWake) {
- nativeWake(mPtr);
- }
- return true;
- }
删除消息 removeMessages()
- final void removeMessages(Handler h, int what, Object object) {
- if (h == null) {
- return;
- }
- synchronized(this) {
- Message p = mMessages;
- // Remove all messages at front.
- while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) {
- Message n = p.next;
- mMessages = n;
- p.recycle();
- p = n;
- }
- // Remove all messages after front.
- while (p != null) {
- Message n = p.next;
- if (n != null) {
- if (n.target == h && n.what == what && (object == null || n.obj == object)) {
- Message nn = n.next;
- n.recycle();
- p.next = nn;
- continue;
- }
- }
- p = n;
- }
- }
- }
- final void removeMessages(Handler h, Runnable r, Object object) {
- if (h == null || r == null) {
- return;
- }
- synchronized(this) {
- Message p = mMessages;
- // Remove all messages at front.
- while (p != null && p.target == h && p.callback == r && (object == null || p.obj == object)) {
- Message n = p.next;
- mMessages = n;
- p.recycle();
- p = n;
- }
- // Remove all messages after front.
- while (p != null) {
- Message n = p.next;
- if (n != null) {
- if (n.target == h && n.callback == r && (object == null || n.obj == object)) {
- Message nn = n.next;
- n.recycle();
- p.next = nn;
- continue;
- }
- }
- p = n;
- }
- }
- }
- final void removeCallbacksAndMessages(Handler h, Object object) {
- if (h == null) {
- return;
- }
- synchronized(this) {
- Message p = mMessages;
- // Remove all messages at front.
- while (p != null && p.target == h && (object == null || p.obj == object)) {
- Message n = p.next;
- mMessages = n;
- p.recycle();
- p = n;
- }
- // Remove all messages after front.
- while (p != null) {
- Message n = p.next;
- if (n != null) {
- if (n.target == h && (object == null || n.obj == object)) {
- Message nn = n.next;
- n.recycle();
- p.next = nn;
- continue;
- }
- }
- p = n;
- }
- }
- }
Handler 是和线程的 MessageQueue 相关联的 Runable 对象,用于发送和处理消息。Handlerg 和线程及线程的 MessageQueue 是一一对应的,即每个 Handler 实例关联一个单一线程和该线程的 messagequeue。当您创建一个 Handler 时,它就绑定到创建它的线程以及对应的消息队列。使用该 handler 将发送消息到对应消息队列,并由 Handler 处理取出的消息。
简单来说,handler 主要做了两件事:
- public class Handler {
- /*
- * 标志位,用来检测那些继承于它但不是静态的匿名类、本地类或成员类,这些类可能会导致内存泄露。
- */
- private static final boolean FIND_POTENTIAL_LEAKS = false;
- private static final String TAG = "Handler";
- final MessageQueue mQueue;
- final Looper mLooper;
- final Callback mCallback;
- final boolean mAsynchronous;
- IMessenger mMessenger;
- }
- public Handler() {
- this(null, false);
- }
- public Handler(Callback callback) {
- this(callback, false);
- }
- public Handler(Looper looper) {
- this(looper, null, false);
- public Handler(Looper looper, Callback callback) {
来源: http://lib.csdn.net/article/android/41946