Android更新Ui进阶精解(二)

《代码里的世界》

用文字札记描绘自己 android学习之路

转载请保留出处 by Qiao
http://blog.csdn.net/qiaoidea/article/details/45115047

【导航】
Android更新Ui的几种方法和见解 android更新ui基本常用方法
Android更新Ui进阶精解(一) android ui线程检查机制
Android更新Ui进阶精解(二) android 线程更新UI机制


1.回顾

第一篇讲了对Ui线程更新的方法和见解,然后接着讲了线程检查机制,这里来详细分析下更新Ui的核心——Android中消息系统模型。当然,这里要讲的其实也已经不再简简单单地是更新Ui的范畴了。不过还是很值得学习和分析一下。另外,其实网上关于这方面的讲解也有很多了,本篇也是综合整理并用自己的理解加以描述和概括。同时也感谢有更高造诣的大大能予以批评指正。

提炼

Android中的消息机制主要有如下几个要点,这里也着重围绕这些内容来讲解:
  
1. Handler 调度消息和runnable对象在不同线程中执行相应动作。
2. Looper消息泵,用来为一个线程开启一个消息循环
3. MessageQueue 遵循FIFO先进先出规则的消息队列,以链表形式存取Message,供looper提取

(为了深入了解,已从源码从提取这几个类
  Handler/Looper/MessageQueue/Message .java 方便新手下载查看)


2.分析

为了方便分析,借用一下找到的模型图综合看一下:
  

消息系统模型
消息系统模型

  首先在一个线程中初始化一个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();
    }

以上便是整个消息系统的过程,后边我们会逐个分析精讲。


3.回到我们更新UI讲解

在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部分也算讲完了,那么作为补充部分,还是从源码上完整细致的过一下整个消息系统模型吧。


4.精解

这里通过剖析源码来理解各部分的具体实现,再结合前面讲的内容加以融会贯通,便于深入理解最终达到在不同场景的熟练使用。
  我们将按照顺序来逐个查看。
  首先说说消息对象,毕竟其他类操作的最基本元素也都是它。

4.1 Message

public final class Message implements Parcelable {
    //继承Parcelable 用于数据传递

    /**几种数据类型**/
    public int arg1; 
    public int arg2; 
    public Object obj;
    Bundle data;
    
    public int what;//供handler处理的消息识别标识身份
    long when;//什么时候处理该消息

    Handler target;//处理该消息的目标handler
    Runnable callback;  //回调方法

    int flags;//标签标识
    static final int FLAG_IN_USE = 1 << 0;//是否可用(回收利用)
    static final int FLAG_ASYNCHRONOUS = 1 << 1;
    static final int FLAGS_TO_CLEAR_ON_COPY_FROM = FLAG_IN_USE;
    
    public Messenger replyTo;//可选对象,可以用来记录发送方或接收者

    Message next;//这条消息的下一条消息

    /**
    *开一个消息池,便于循环利用消息,避免生成新对象并分配内存
    */
    private static final Object sPoolSync = new Object();
    private static Message sPool;
    private static int sPoolSize = 0;
    private static final int MAX_POOL_SIZE = 50;
}

既然提到消息池又在前面讲了利用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 的读写。

4.2 Looper

先看他所定义的属性:

public class Looper {
    private static final String TAG = "Looper";

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    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和打印和异常捕获相关的了,有兴趣的可以自己去看一下。

4.3 MessageQueue

类的描述简要翻译一下:

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<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
    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;
            }
        }
    }

4.4 Handler

Handler是和线程的MessageQueue相关联的Runable对象,用于发送和处理消息。Handlerg和线程及线程的MessageQueue是一一对应的,即每个Handler实例关联一个单一线程和该线程的messagequeue。当您创建一个Handler时,它就绑定到创建它的线程以及对应的消息队列。使用该handler将发送消息到对应消息队列,并由Handler处理取出的消息。
  简单来说,handler主要做了两件事:

  • 将要处理的数据消息或runnable以消息形式放入消息队列,在指定时间处理;
  • 保证运行在多个线程中得消息对象能够在指定线程中被有序处理。
1.先看属性变量
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;

}
2.再看构造方法
    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) {
        this(looper, callback, false);
    }
    
    public Handler(boolean async) {
        this(null, async);
    }

最后都执行的是这两段构造方法:

  1. Handler(Callback callback, boolean async)
    • 当标志位为true,检测到可能造成内存泄露的类时抛出异常
    • 得到当前线程的looper初始化其属性变量
  2. Handler(Looper looper, Callback callback, boolean async)
    • 由传入的参数初始化属性变量
    
    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;
    }

    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
3.接着看它的生成Message

产生消息的方法obtainMessage()是间接调用Message的obtain()方法,尝试从消息池中取已有message实例,便于高效和重复利用。

    //基本类似,不再一一列举
    public final Message obtainMessage()
    public final Message obtainMessage(int what)
    public final Message obtainMessage(int what, Object obj);
    //利用Message的obtain方法构造消息
    public final Message obtainMessage(int what, int arg1, int arg2, Object obj){
        return Message.obtain(this, what, arg1, arg2, obj);
    }

    //将runnable封装成消息
    private static Message getPostMessage(Runnable r) 
    private static Message getPostMessage(Runnable r, Object token) {
        Message m = Message.obtain();
        m.obj = token;
        m.callback = r;
        return m;
    }
4.然后看它的send消息的过程

handler有提供post(runnable)和sendMessage(message)两种方法。post其实就是通过上边的getPostMessage方法将runnable对象封装成消息发送至消息队列。这些发送消息的方法有:

    public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }
    public final boolean sendEmptyMessage(int what)
    {
        return sendEmptyMessageDelayed(what, 0);
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }
    //上述方法都调用sendMessageDelayed()方法
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

这些方法都转入了sendEmptyMessageAtTime这个方法中去

    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);
    }
    //还有这个方法
    public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageAtTime(msg, uptimeMillis);
    }

sendMessageAtTime在指定时间发送消息,对消息进行有序排队。调用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);
    }

发送消息还有一个方法,将消息放在队列首并立即取消息。使用方法sendMessageAtFrontOfQueue

    public final boolean sendMessageAtFrontOfQueue(Message msg) {
        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, 0);
    }

最后looper循环取消息并调用handler的处理方法dispatchMessage 和handleMessage

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }
    
    //当message的callback不为空的时候,调用handleCallback
    private final void handleCallback(Message message) {  
        message.callback.run();  
    }  

    //这里是个空方法需要子类定义处理逻辑
    public void handleMessage(Message msg) {
    }   

当我们实例化一个handler时候,handler会通过mCallback接口来回调我们的handleMessage方法,看一下这个接口:

    public interface Callback {  
        public boolean handleMessage(Message msg);  
    }  

同时handler也提供了移除回调removeCallbacks 和 移除消息removeMessages方法

    //移除回调
    public final void removeCallbacks(Runnable r)
    {
        mQueue.removeMessages(this, r, null);
    }
    
    public final void removeCallbacks(Runnable r, Object token)
    {
        mQueue.removeMessages(this, r, token);
    }

    //移除消息
    public final void removeMessages(int what) {
        mQueue.removeMessages(this, what, null);
    }

    public final void removeMessages(int what, Object object) {
        mQueue.removeMessages(this, what, object);
    }

    //移除消息和回调
    public final void removeCallbacksAndMessages(Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

5.综述

以上便是Android中的消息机制的几个核心部分内容和源码概括完了。详细读到这里多少也对整个体系有了点深入了解吧。当然,个人叙述相对有些混乱,建议感兴趣的朋友可以在sdk\sources目录下找到相应源码查看具体详细。
  作为补充还是把对应的Handler Looper MessageQueue 和 Message 的.java文件放上来,方便不会关联源码的朋友查看。
  Handler/Looper/MessageQueue/Message .java 文件


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342

推荐阅读更多精彩内容