• Android消息机制解析


    我们知道在编程时许多操作(如更新UI)需要在主线程中完成,而且,耗时操作(如网络连接)需要放在子线程中,否则会引起ANR。所以我们常使用Handler来实现线程间的消息传递,这里讨论的也就是Handler的运行机制。

    Handler的运行主要由两个类来支撑:Looper与MessageQueue。熟悉开发的朋友都知道在子线程中默认是无法创建Handler的,这是因为子线程中不存在消息队列。当需要创建一个与子线程绑定的Handler时,标准代码如下:

    class LooperThread extends Thread {
        public Handler mHandler;
        public void run() {
            Looper.prepare();
            mHandler = new Handler() {
                public void handleMessage(Message msg) {
                    // process incoming messages here
                }
            };
    Looper.loop(); } }

    在创建Handler前,需要先调用Looper.prepare()方法,之后再调用Looper.loop()方法。也就是说Handler的功能实现建立在Looper之上。

     1 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
     2 final MessageQueue mQueue;
     3 final Thread mThread;
     4 
     5 private static void prepare(boolean quitAllowed) {
     6     if (sThreadLocal.get() != null) {
     7         throw new RuntimeException("Only one Looper may be created per thread");
     8     }
     9     sThreadLocal.set(new Looper(quitAllowed));
    10 }
    11 
    12 private Looper(boolean quitAllowed) {
    13     mQueue = new MessageQueue(quitAllowed);
    14     mThread = Thread.currentThread();
    15 }

    由于Looper的消息循环是一个死循环,一个线程最多只能有一个Looper,所以Looper.prepare()函数首先检查该线程是否已经拥有一个Looper,如果有则抛出异常。Looper通过ThreadLocal类为每个线程储存独立的Looper实例,简单说一下ThreadLocal的实现原理:

    Java并发编程:深入剖析ThreadLocal

    首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本。

    初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。

    然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

     1 public static void loop() {
     2     final Looper me = myLooper();
     3     if (me == null) {
     4         throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
     5     }
     6     final MessageQueue queue = me.mQueue;
     7     for (;;) {
     8         Message msg = queue.next(); // might block
     9         if (msg == null) {
    10             // No message indicates that the message queue is quitting.
    11             return;
    12         }
    13         msg.target.dispatchMessage(msg);
    14         msg.recycleUnchecked();
    15     }
    16 }

    在4行可以看到一个我们熟悉的异常信息,说明并没有Looper与当前线程相关联,也就无法进行消息传递。Looper.loop()方法本身是一个死循环,不断在MessageQueue中取出Message对象进行处理,然后调用Message.recycleUnchecked()方法对其回收,这也是为什么官方推荐使用Message.obtain()方法来获取Message实例,而不是直接新建对象。当没有消息可处理时,MessageQueue.next()方法将阻塞,直到新的消息到来。

    对于MessageQueue,我们只需要关注两个函数即可,一个是MessageQueue.enqueueMessage()另一个是MessageQueue.next(),它们分别对应着队列的插入与取出操作。MessageQueue中队列是使用单链表实现的,由Message.next属性指向其下一个元素。

     1 boolean enqueueMessage(Message msg, long when) {
     2     synchronized (this) {
     3         msg.markInUse();
     4         msg.when = when;
     5         Message p = mMessages;
     6         boolean needWake;
     7         if (p == null || when == 0 || when < p.when) {
     8             msg.next = p;
     9             mMessages = msg;
    10         } else {
    11             Message prev;
    12             for (;;) {
    13                 prev = p;
    14                 p = p.next;
    15                 if (p == null || when < p.when) {
    16                     break;
    17                 }
    18             }
    19             msg.next = p; // invariant: p == prev.next
    20             prev.next = msg;
    21         }
    22     }
    23     return true;
    24 }

    向MessageQueue中插入元素时,需要根据Message.when属性的大小决定插入的位置,它代表了Meesage需要被处理的时间,拿Handler.sendMessage()函数为例。

    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;
        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);
    }

    从调用流程来看,Handler.sendMessage()函数其实就是向MessageQueue的消息队列中插入了一个Message.when属性为当前时间的元素。

    对于MessageQueue.next()函数,简单来说它的作用就是在MessageQueue的头部取出元素,然后执行Handler.dispatchMessage()函数。

    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            handleMessage(msg);
        }
    }
    
    private static void handleCallback(Message message) {
        message.callback.run();
    }

    如果我们使用Handler.post()函数发送一个Runnable对象,那么最终Runnable对象会在Handler.handleCallback()函数中执行。如果是一个普通Message,那么它会被分发到一个我们熟悉的函数中,Handler.handleMessage(),这就是为什么一般我们都需要重写这个函数对消息进行处理。

  • 相关阅读:
    matlab 自定义函数及调用
    matlab 逻辑数组及其应用
    matlab 嵌套循环的学习
    matlab和C语言的break及continue测试
    matlab 向量法建数组(推荐)
    matlab for循环应用(阶乘及the day of year)
    matlab for循环的三种类型
    ORACLE创建用户,建表空间,授予权限
    Linux计划任务
    android开发环境搭建
  • 原文地址:https://www.cnblogs.com/mmmmar/p/8595723.html
Copyright © 2020-2023  润新知