美文网首页
Handler,MessageQueue,Runnable与Lo

Handler,MessageQueue,Runnable与Lo

作者: 小庄bb | 来源:发表于2018-07-25 17:07 被阅读28次

    前言

    前篇对MessageQueue、Handler等几个概念进行了概述,相信大家一定也有了一定的理解。接下来将对上篇遗留的问题进行研究。由于上面中LooperThread例子只是一个壳,没有可真正运行的”内容“。所以要回答剩余的问题,ActivityThread是一个很好的示例。从名称上看ActivityThread就是我们所熟悉的主线程。

    示例

    public static void main(String[] args) {
             ...
             Looper.prepareMainLooper() ;
             ActivityThread thread = new ActivityThread();
             thread.attach(false);
             if ( sMainThreadHandler == null){
                     sMainThreadHandler = thread.getHandler();
             }
             AsyncTask.init();
             Looper,loop();
             throw new RuntimeException("Main thread loop unexpectedly exited");
    }
    

    如果比较上面这段代码与LooperThread.run()的实现,就可以发现它们在整体架构上是一样的,区别主要体现在:

    • prepareMainLooper和prepare
      普通线程只要prepare就可以了;而主线程使用的是prepareMainLooper。
    • Handler 不同
      普通线程生成一个与Looper绑定的Handler对象的就行;而主线程是从当前当前线程中获得的Handler(thread.getHandler());
    1. 那么,prepareMainLooper有什么特殊之处?
    public static void prepareMainLooper() {
              prepare (false); //先调用prepare
              synchronized (Looper.class) {
                    if (sMainLooper != null) {
                           throw new IllegalStateException("The main Looper has already been prepared.");
                   }
                   sMainLooper = myLooper ();
               }
    }
    

    我们可以看到,在prepareMainLooper也是需要用到prepare.参数false表示该线程不允许退出,这和前面的LooperThread不一样,经过prepare后,myLooper就可以得到一个本地线程<ThreadLocal>的Looper对象,然后将其赋给sMainLooper。从这个角度来讲,主线程的sMainLooper其实和其他线程的Looper对象并没有本质的区别。


    Looper揭秘.png

    这个图描述的是一个进程和它内部两个线程的Looper情况,其中线程1是主线程,线程2是普通线程。方框表示它们能访问的范围,如线程1就不能直接访问到线程2中的Looper对象,但二者都可以接触到进程中的各元素。
    线程1:因为是Main Thread,它使用的是prepareMainLooper(),这个函数将通过prepare()为线程1生成一个ThreadLocal的Looper对象,并让sMainLooper指向它。这样做的目的就是其他线程如果要获得主线程的Looper,只需调用getMainLooper()即可。
    线程2:作为普通线程,它调用的是prepare();同时也生成一个ThreadLocal的Looper对象,只不过这个对象只能在线程内通过myLooper()访问。当然,主线程内部也可以通过这个函数访问它的Looper对象。
    由此可见,Google玩了一个技巧,从而巧妙的区分开各线程的Looper,并界定了它们的访问权限。

    1. sMainThreadHandler。当ActivityThread对象创建时,会在内部同时生成一个继承自Handler的H对象:
      final H mH = new H();
      ActivityThread.main中调用的tread.getHandler()返回的就是mH。
      也就是说,ActivityThread提供了一个“事件管家”,以处理主线程中的各种消息。
      接下来我们分析下loop()函数。
    public static void loop() {
             final Looper me = myLooper () ;
             /*loop函数也是静态的,所以它只能访问静态的数据。函数myLooper则调用sThreadLocal.get()来获取与之匹配的Looper实例(其实就是取出之前prepare中创建那个Looper对象)*/
             ...
             final MessageQueue queue = me.mQueue;
             /*正如我们所说,Looper中自带一个MessageQueue*/
             for( ; ; ){//消息循环开始
                     Message msg = queue.next();/*从MessageQueue中取出一个消息,可能会阻塞*/
                     if(msg == null){
                      /*如果当前消息队列中没有msg,说明线程要退出了。类比于上面Windows伪代码
                      例子中while判断条件为0,这样就会结束循环*/
               return;/*消息处理完毕,进行回收*/
                      }
              ...
              msg.target.dispatchMessage(msg);
                      /*终于开始分派消息,重心就在这里。变量target其实是一个Handler,所以
                      dispatchMessage最终调用的是Handler中的处理函数*/
              ...
                      msg.recycle () ;/*消息处理完毕,进行回收*/
             }
    }
    

    可以看到,loop()函数的主要工作就是不断地从消息队列中取出需要处理的事件,然后分发给相应的负责人。如果消息队列为空,它很可能会进入睡眠以让出cpu资源。而在具体事件的处理过程中,程序会post新的事件到队列中。另外,其他进程也可能投递新的事件到这个队列中。APK应用程序就会不停地执行“处理队列事件”的工作,直到它退出运行。
    以上我们看到了Looper.loop的处理流程,从而知道它和前面讨论的Windows消息处理机制是类似的,最后再来解决一个问题:MessageQueue是怎样创建出来的?
    我们有提到过,Looper中带有唯一一个MessageQueue,是不是这样?

    /*
    以下代码还是Looper.java中的,不过只提取出MessageQueue相关的部分
    */
    final MessageQueue mQueue ; /*注意它不是static的*/
    private Looper(boolean quitAllowed) {
         mQueue = new MessageQueue( quitAllowed );
         /*new了一个MessageQueue,就是它了。也就是说,当Looper创建时,消息队列也同时会被创建出来*/
         mRun = true;
         mThread = Thread.currentThread();//Looper与当前线程建立对应关系
         }
    

    事实证明Looper内部的确管理了一个MessageQueue,它将作为线程的消息存储仓库,配合Handler,Looper一起完成一系列操作。

    相关文章

      网友评论

          本文标题:Handler,MessageQueue,Runnable与Lo

          本文链接:https://www.haomeiwen.com/subject/jkipmftx.html