Android消息机制探究

Looper Handler 之间有什么关系:

首先看一个handler的使用例子:

  Handler handler = new Handler(){            @Override            public void dispatchMessage(Message msg) {                super.dispatchMessage(msg);            }            @Override            public void handleMessage(Message msg) {                super.handleMessage(msg);                            }        };                handler.sendEmptyMessage(1);

貌似主动创建就可以使用,但是如果是在子线程此方法是行不通的,具体原因是因为在ActivityThread中(app启动的的第一步ActivityThread的main方法)在启动前已经为其做好了充足的准备:

        Looper.prepareMainLooper();        ActivityThread thread = new ActivityThread();        thread.attach(false);        if (sMainThreadHandler == null) {            sMainThreadHandler = thread.getHandler();        }        //省略 无用代码        Looper.loop();

至于为什么要在这里创建 一句话,activity的生命周期大部分都是由handler来触发和控制的,handler在使用前就需要为其准备好looper

因而在子线程创建并使用handler需要进行如下准备:

 new Thread(new Runnable() {            @Override            public void run() {                Looper.prepare();                Handler handler = new Handler() {                    @Override                    public void dispatchMessage(Message msg) {                        super.dispatchMessage(msg);                    }                    @Override                    public void handleMessage(Message msg) {                        super.handleMessage(msg);                    }                };                handler.sendEmptyMessage(1);                Looper.loop();            }        }).start();

由此我们可以确定一个对应关系: 一个 thread(一开始创建的线程默认为主线程) 对应 多个Handler 至于对应几个Looper我们向下看

//子线程中使用的方法     /** Initialize the current thread as a looper.      * This gives you a chance to create handlers that then reference      * this looper, before actually starting the loop. Be sure to call      * {@link #loop()} after calling this method, and end it by calling      * {@link #quit()}.      */     public static void prepare() {        prepare(true);    }    //在activityThread中创建的方法       /**     * Initialize the current thread as a looper, marking it as an     * application's main looper. The main looper for your application     * is created by the Android environment, so you should never need     * to call this function yourself.  See also: {@link #prepare()}     */    public static void prepareMainLooper() {        prepare(false);        synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();        }    }

共同点 都调用了prepare(boolean quitAllowed) 只是入参不同,但是根据入参名称可以看出 这个入参之时决定是否允许退出。
然而 prepare(boolean quitAllowed);这个方法仿佛开启了一个异界的大门

 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));//重点 看这里    }

从这里可以看到原来这个prepare()只是干了一件事,创建一个looper然后放在sThreadLocal里。
至于sThreadLocal是个啥东西 看这里:ThreadLocal sThreadLocal = new ThreadLocal();
看到了吗?

ThreadLocal

ThreadLocal!!! 就是我们经常听说但是并没有什么实际使用场景的ThreadLocal
但是这里就出现了ThreadLocal的使用场景。

惯例先给ThreadLocal进行下说明:
1 是一个线程内部的数据存储类
2 通过它可以在制定的线程中存储数据
3 只有在指定的线程中可以获取到存储的数据

由此 我明白了一些事情:

1 虽然每次都是new 一个新的 但是面对主线程的looper在代码中还是进行的防范重复创建:

  synchronized (Looper.class) {            if (sMainLooper != null) {                throw new IllegalStateException("The main Looper has already been prepared.");            }            sMainLooper = myLooper();//myLooper()这个方法稍后研究        }

2 对应关系可以继续补充了: Thread ——> N*Handler Thread->Looper 一个线程里只有一个looper(在threadlocal中保存着)虽然可以有多个handler实例但是都是在一个looper中。

看到这里 我又有了新的问题:Thread是怎么区分线程的,毕竟只是一个set方法难道就ok了?让我们看看set方法:

    public void set(T value) {        Thread t = Thread.currentThread();        ThreadLocalMap map = getMap(t);        if (map != null)            map.set(this, value);        else            createMap(t, value);    }

根据变量名就能发现 原来是ThreadLocal内部维护着一个map --- ThreadLocalMap,这个map的key就是当前的线程,而且还是懒加载呢,看来这些小技巧也没有被这些大牛们ingore

我又有问题了这个ThreadLocalMap 是什么map? hashMap还是?

  ThreadLocalMap(ThreadLocal firstKey, Object firstValue) {            table = new Entry[INITIAL_CAPACITY];            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);            table[i] = new Entry(firstKey, firstValue);            size = 1;            setThreshold(INITIAL_CAPACITY);        }

于是乎我就顺手看了其创建方法,key的生成方式还是依据hash值,原来是放在一个初始长度是16的数组中,就是一个简易的hashmap

回归正题,想知道 handler和looper的关系,还得深入looper来看:new Looper(quitAllowed)
···
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);
mThread = Thread.currentThread();
}
···
哈哈哈 ,看到这里我笑了 本来我们是要找looper 和 handler的关系 怎么就冒出来一个MessageQueue,呵呵 看来 关系图要加上一笔,简直是意外收获啊,确认关系:thread ——> looper -> MessageQueue
(handler就先不考虑的)

MessageQueue

它就是Android中大名鼎鼎的消息队列,
1 它主要工作只有两部:插入和读取 enqueueMessage,next
2 虽然叫消息队列但实际上是通过单向链表维护数据结构(单向链表在插入和删除上比较有优势)

说起它是单向链表 我很好奇啊 这个单向链表是怎么实现的?

 boolean enqueueMessage(Message msg, long when) {   //省略             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;            }    }

删掉一些辅助类代码和注释 也不长就是一个判断 但是根据判断就明白了 原来 MessageQueue所谓的链表原来是 这样的结果 Message 对象,但是Message对象有个next属性用来记录下一个对象的引用
如图所示:


图片发自App

再看看如果获取消息:虽然是精简过的但是也不少,因为它还要考虑到延时消息怎么搞的问题

  final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // Next message is not ready.  Set a timeout to wake up when it is ready.                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // Got a message.                        mBlocked = false;                        if (prevMsg != null) {                            prevMsg.next = msg.next;                        } else {                            mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    // No more messages.                    nextPollTimeoutMillis = -1;                }

耐心看完,其实你可以发现:message的排列顺序 有多少个 messagequeue都不知道或者说不关心,也没有提供向上追溯的方法,而且看了代码才理解了“消费”这个词,当message被消费掉了,mMessage就会被替换成下一个message。简直是精妙!
上面介绍了这么多其实消息队列还没有跑起来呢,回到问题,即然是想要让消息机制运转起来,还有一段很重要的话:Looper.loop()

Looper

loop() 第一句代码就是 final Looper me = myLooper();

呵呵 myLooper() 到你了吧,这句话干嘛呢?让我看一下:

    public static @Nullable Looper myLooper() {        return sThreadLocal.get();    }

实现很简单,就是从LocalThread中获取到当前的looper

接下来 就是惨无人道的一通遍历(死循环啊)

  for (;;) {            Message msg = queue.next(); // might block            if (msg == null) {                // No message indicates that the message queue is quitting.                return;            }            //省略                msg.target.dispatchMessage(msg);//省略        }

无脑从消息队列中获取 next直到next是null,但是如果next取不到东西 其实就说明消息队列没有内容,messagequeue这时候其实已经是阻塞的了(调用native方法终止循环)最终消息是怎么被处理的呢? 看到没 msg.target.dispatchMessage(msg)
呵呵 你知道target对象是谁吗?
没错就是 Handler

Handler

handler啊 说白了就是个生产者,整天造垃圾向传送带(messagequeue)上扔,怎么扔呢:

 public final boolean sendMessageAtFrontOfQueue(Message msg) {        MessageQueue queue = mQueue; //省略        return enqueueMessage(queue, msg, 0);    }    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

原来handler中有messagequeue的引用啊,什么时候有的?

  mLooper = Looper.myLooper();        if (mLooper == null) {            throw new RuntimeException(                "Can't create handler inside thread that has not called Looper.prepare()");        }        mQueue = mLooper.mQueue;

原来是创建的时候啊,其实什么时候都无所谓 因为想要looper 调用静态方法Looper.myLooper()就得到了。

哈哈 这下算是形成了一个闭环 ,handler(扔垃圾者)——》MessageQueue(垃圾车收垃圾)——》Looper(从垃圾车上捡垃圾,问垃圾是谁扔的你)——》handler(处理自己造的垃圾)

更多相关文章

  1. Unable to start activity ComponentInfo 解决方法
  2. android,总结,判断应用程序是否切换到后台
  3. Android(安卓)之 setContentView 源码阅读
  4. android ANR
  5. 从源码一次彻底理解Android的消息机制
  6. android 开发使用 kotlin 进行点击事件监听和界面跳转,直接传也方
  7. Android下载完文件打开
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. linux上mongodb的安装与卸载
  2. 少不更事爱前端,每天都想着转后端。
  3. Oracle的用户权限及其管理操作
  4. 带你一起了解PHP的错误类型和错误级别
  5. 为什么使用PreparedStatement?
  6. 使用面向对象方法实现用户信息增删改查
  7. 在服务器上排除问题的头 5 分钟
  8. 初探MySQL Innodb集群
  9. 关于数据库的一些常识
  10. 数据库--存储过程详解