美文网首页
Android 消息处理机制(读后心得)

Android 消息处理机制(读后心得)

作者: 被风扬起的沙 | 来源:发表于2016-11-18 15:11 被阅读31次

    原文借鉴自:http://www.jianshu.com/p/02962454adf7

    搞安卓的消息处理机制应该是最基础的了,但是其中原理的话还是很少有人能深入下去,最低拜读了大神文章,文章地址在开头。偶有心得在此记录下来。

    从App启动开始,一开始找ActivityThread类,我用的是Mac版studio,在项目里面打上这个单词,竟然没找到,Looper,handler能找到。找了一个大神问了问,双击shift键出现搜索,然后打上这个单词才找到。据大神的说,默认的只是一部分常用的framework源码,双击搜索的是sdk中的android源码。
    代码如下:

    public final class ActivityThread { 
      public static final void main(String[] args) {
       ...... 
      Looper.prepareMainLooper();
      ...... 
      ActivityThread thread = new ActivityThread(); 
      thread.attach(false); 
      ...... 
      Looper.loop();
     ...... 
      }
     }
    

    吐槽一下,这个main函数还是挺难找的,源码里面是放到了类的最底部,5300多行了。。。

    首先执行Looper.prepareMainLooper(),这里面有个ThreadLocal 线程本地存储区(Thread Local Storage,简称为TLS),每个线程都有自己的私有的本地存储区域,不同线程之间彼此不能访问对方的TLS区域。这里线程自己的本地存储区域存放是线程自己的Looper。
    可以先简单理解它相当于map,key是线程,value是Looper,那么你只要用当前的线程就能通过sThreadLocal获取当前线程所属的Looper。

    Looper.prepareMainLooper()做的事件就是new了一个Looper实例并放入Looper类下面一个static的ThreadLocal<Looper> sThreadLocal静态变量中,同时给sMainLooper赋值,给sMainLooper赋值是为了方便通过Looper.getMainLooper()快速获取主线程的Looper,sMainLooper是主线程的Looper可能获取会比较频繁,避免每次都到 sThreadLocal 去查找获取。

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

    在Looper的构造函数中创建了消息队列MessageQueue,并且让Looper持有MessageQueue的引用。执行完Looper.prepareMainLooper()
    之后,主线程从普通线程转成一个Looper线程。

    ActivityThread的构造函数并没有做什么事只是初始化了资源管理器。

     ActivityThread() {
         mResourcesManager = ResourcesManager.getInstance();
     }
    

    thread.attach(false);会创建一个Binder线程,他是在主线程进入无限循环之前创建,接收系统服务发送的消息如LAUNCH_ACTIVITY,PAUSE_ACTIVITY等等通过handler传递给主线程。

    我们知道一个线程是一段可执行的代码,当可执行代码执行完成后,线程生命周期便会终止,线程就会退出,那么做为App的主线程,如果代码段执行完了会怎样?,那么就会出现App启动后执行一段代码后就自动退出了,这是很不合理的。所以为了防止代码段被执行完,只能在代码中插入一个死循环,那么代码就不会被执行完,然后自动退出,怎么在在代码中插入一个死循环呢?那么Looper出现了,在主线程中调用Looper.prepare()...Looper.loop()就会变当前线程变成Looper线程(可以先简单理解:无限循环不退出的线程),Looper.loop()方法里面有一段死循环的代码,重要的就是这句:

    Message msg = queue.next(); //获取队列中的下一条消息,可能会线程阻塞
    

    简单说如果在主线程的MessageQueue没有消息时,就会阻塞在loop的queue.next()方法里,时候主线程会释放CPU资源进入休眠状态,直到有下个消息进来时候就会唤醒主线程,在2.2 版本以前,这套机制是用我们熟悉的线程的wait和notify 来实现的,之后的版本涉及到Linux pipe/epoll机制,通过往pipe管道写端写入数据来唤醒主线程工作。原理类似于I/O,读写是堵塞的,不占用CPU资源。

    分析到这里,从应用启动创建Looper,创建消息队列,到进入loop方法执行无限循环中,那么这一块就告一段落了,主线程已经在死循环里轮询等待消息了,接下来我们就要再看看,系统是怎么发消息给主线程的,主线程是怎么处理这些个消息的?

    在准备启动一个Activity的时候,系统服务进程下的ActivityManagerService(简称AMS)线程会通过Binder发送IPC调用给APP进程,App进程接到到调用后,通过App进程下的Binder线程最终调用ActivityThread类下面的scheduleLaunchActivity方法来准备启动Activity,在Binder线程执行mH.sendMessage(msg);,由主线程创建的Handler mH实例发送消息到主线程的消息队列里,消息队列从无到有,主线程堵塞被唤醒,主线程loop拿到消息,并回调mH的handleMessage 方法处理LAUNCH_ACTIVITY 等消息。从而实现Activity的启动。

    1、Handler 对象在哪个线程下构建(Handler的构造函数在哪个线程下调用),那么Handler 就会持有这个线程的Looper引用和这个线程的消息队列的引用。因为持有这个线程的消息队列的引用,意味着这个Handler对象可以在任意其他线程给该线程的消息队列添加消息,也意味着Handler的handlerMessage 肯定也是在该线程执行的。
    2、如果该线程不是Looper线程,在这个线程new Handler 就会报错!

    Handler 有很多sendXXXX开头的方法sendMessageAtTime、sendEmptyMessageDelayed、sendEmptyMessage等等,都是用来给消息队列添加消息的,那么这些方法最终都会调用enqueueMessage来实现消息进入队列。
    最后我们再看下Handler 的dispatchMessage方法,这个方法在Looper线程从消息队列拿出来的时候,通过msg.target.dispatchMessage(msg)
    调用的。

    相关文章

      网友评论

          本文标题:Android 消息处理机制(读后心得)

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