zoukankan      html  css  js  c++  java
  • android 消息系统Handler、MessageQueue、Looper源代码学习

    android消息系统

    总体框架如图所看到的
    这里写图片描写叙述
    在安卓的消息系统中,每一个线程有一个Looper,Looper中有一个MessageQueue,Handler向这个队列中投递Message,Looper循环拿出Message再交由Handler处理。总体是一个生产者消费者模式,这四部分也就构成了android的消息系统。


    先来看一个最简单的样例

            //这段代码在某个Activity的onCreate中
            Handler handler = new Handler(Looper.getMainLooper());
            Message msg = Message.obtain(handler, new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(getApplicationContext,"I am a message",Toast.LENGTH_SHORT).show();
                }
            });
            handler.sendMessage(msg);

    效果就是。在当前窗体弹出I am a message。当然就事实上现的效果而言全然多此一举。可是就分析android消息系统。却是非常easy有效的样例。


    源代码分析

    Message

    Message中封装了我们经常使用的what、arg1、arg2、obj等參数,除此之外还有target:一个Handler类型,由前文可知一个Message终于还是交给一个Handler运行的。这个target存放的就是消息的目的地、callback。一个消息的回调。我们通过handler.post(new Runnable{…})发送的消息。这个Runnable即被存为callback。
    首先来看消息的获取:

        public static Message obtain() {
            synchronized (sPoolSync) {
                if (sPool != null) {
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    
        public static Message obtain(Handler h, Runnable callback) {
            Message m = obtain();
            m.target = h;
            m.callback = callback;
    
            return m;
        }

    对比最開始的样例,Message.obtain(Handler h, Runnable callback)首先调用obtain获取了一个新的Message对象。然后为其设置了目的地Handler和回调函数callback。Message类中有非常多不同的obtain函数。实际上仅仅是为我们封装了一些赋值的操作。

    再看Message.obtain()方法。sPoolSync是一个给静态方法用的静态锁。sPool是一个静态的Message变量。在消息的获取这里,android使用了享元模式,对于会被反复使用的Message消息。没有对每一次请求都新建一个对象。而是通过维护一个Message链表,在有空暇消息的时候从链表中拿Message。没有时才新建Message。
    能够看到obtain中仅仅有从链表中去Message和新建Message。而没有向链表中存储的过程。

    存储这部分就要看Message.recycle()了:

        public void recycle() {
            clearForRecycle();
    
            synchronized (sPoolSync) {
                if (sPoolSize < MAX_POOL_SIZE) {
                    next = sPool;
                    sPool = this;
                    sPoolSize++;
                }
            }
        }

    回收过程。首先把原链表的头指向当前被回收消息的下一个节点。然后再把链表头指针知道当前节点就可以。整个操作也就是将Message加入到链表的首位。


    MessageQueue 消息队列

    MessageQueue是在Looper中的。这点从Looper的构造函数能够看出来:

    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }

    对于每一个MessageQueue,是链表实现的消息队列。

    首先是入队操作:

    boolean enqueueMessage(Message msg, long when) {
            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.");
            }
    
            synchronized (this) {
                if (mQuitting) {
                    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;
                //mMessages是链表的头指针
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    // 将消息插入到队列的首位
                    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;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }

    next操作。包括取出和删除一条消息。

    Message next() {
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                    Binder.flushPendingCommands();
                }
    
                //从native层消息队列取出消息
                nativePollOnce(mPtr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {
                        // 找到非异步的Message或者消息队列尾部的Message取出
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // 消息尚未到运行时间,下次循环挂起线程一段时间
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // 获取一个Message
                            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 {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                    // 检查退出标志位
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
    
                    ...
            }
        }

    Handler

    Handler的作用是放入消息和处理消息,承担了生产者的工作和部分消费者的工作。
    首先通过Handler发送一条消息:

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

    通过一层一层嵌套,真正的逻辑在sendMessageAtTime,能够看到仅仅是运行了一下入队操作。作为生产者的工作也就运行完毕,消费者部分后面要结合Looper分析。
    除了sendMessage方法,经常使用的handler.post方法也是封装为Message,主要过程和上面类似。

    public final boolean post(Runnable r)
        {
           return  sendMessageDelayed(getPostMessage(r), 0);
        }
    private static Message getPostMessage(Runnable r) {
            Message m = Message.obtain();
            m.callback = r;
            return m;
        }

    Looper

    Looper类中,Looper的实例获取是通过ThreadLocal的。ThreadLocal会为每一个线程提供一个副本,通过set和get方法每一个线程获取作用域仅属于该线程的变量值。对于UI线程而言,会运行Looper.prepareMainLooper()来完毕Looper的初始化:

    
    public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
        }
    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));
        }

    Looper.prepare()方法将当前线程的ThreadLocal设置了一个新的Looper对象。prepareMainLooper则是把当前线程的Looper对象赋值给类变量sMainLooper 。该方法在ActivityThread中调用,设置了一个全局的给UI线程使用的Looper。

    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;
    
    
            Binder.clearCallingIdentity();
            final long ident = Binder.clearCallingIdentity();
            //从Looper中获取MessageQueue,循环取出消息
            for (;;) {
                Message msg = queue.next();
    
                ...
    
                //将消息发送给目标处理。
                msg.target.dispatchMessage(msg);
    
                ...
    
                //回收消息,把消息放在消息池中
                msg.recycle();
            }
        }

    主要逻辑非常清晰,前面分析过msg.target是一个Handler,表示处理消息的目标,通过命令模式将消息交给相应Handler处理。

    以下是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();
        }
    public void handleMessage(Message msg) {
        }
    

    假设我们是通过handler.post的方法发送一条消息,那么直接运行callback中的逻辑。

    否则通过实现Callback接口回调,或者运行handleMessage,handleMessage也就是我们子类覆写的方法。

    能够看到,尽管逻辑部分是我们在Handler中实现的。可是调用的地方却是Looper的线程。由于一个Looper绑定一个线程,我们也能够通过比較Looper来比較线程。


    总结

    通过分析源代码,能够知道android中能够通过Looper为每一个线程创建一个消息队里,UI线程的Looper在Activity启动前就已经初始化。

    那么对于我们自己定义的线程。非常明显也能够绑定Looper。
    自己定义线程绑定Looper。最明显的优点就是能够实现线程间通信了,同一时候由于借助了消息队列,也将并行转为串行实现了线程安全。看一个简单的样例:

            new Thread(new Runnable() {
                @Override
                public void run() {
                    Looper.prepare();
                    handlerA = new Handler(Looper.myLooper()){
                        @Override
                        public void handleMessage(Message msg) {
                            Log.d("TAG", msg.obj.toString());
                        }
                    };
                    Looper.loop();
                }
            }).start();

    上述在线程中创建绑定了一个Looper,然后新建一个和当前Looper绑定的Handler,这样能够通过该Handler向Looper的MessageQueue中加入消息,然后由Looper.loop取出消息并运行。

            Message msg = new Message();
            msg.obj = "i am main thread";
            handlerA.sendMessage(msg);
    

    在主线程或者其他线程中获取handler然后发送消息,终于能够看到消息被线程接收并处理。

    这里msg的target也就是handlerA。

    注意假设线程工作结束,须要调用Looper.quit()。不然会由于Looper一直循环而导致线程无法结束。

    最后经过上面的分析,流程图能够画的更为仔细:

    这里写图片描写叙述

  • 相关阅读:
    Clipboard.GetImage() Clipboard获取粘贴板内容为null的解决办法
    vs开发 winform 设置winform 获取管理员权限启动
    sql server分配某个用户只对某一个数据库有权限 转载 http://blog.sina.com.cn/s/blog_13554ebc70102wi3h.html
    第一个 谷歌浏览器扩展插件 操作网页
    printDocument设置适应边框打印 特重要 找了半天 设置一个属性即可
    手机网络抓包 转载记录http://blog.csdn.net/skylin19840101/article/details/43485911
    C#合并文件夹图片列表 自定义排版顺序
    ROS nodelet 理解记录
    多线程操作控件属性
    Djianggo 在windows中安装出现报错的解决方案
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/8337941.html
Copyright © 2011-2022 走看看