activity 和 生命周期: 消息通信

时间:2023-03-08 17:43:25

实际上关于activity大概流程已经了解了,在深入的话方向应该是ams的处理操作和界面创建和view绘制。这些话题之后再谈,activity是一个gui程序,其中离不开的就是消息通讯,也就是在消息循环中不断的处理消息,比如用户交互消息,系统提醒消息等。

所以一定要把消息通信作为一个核心的组件,其中涉及到的类有Handler,Looper,Message,MessageQueue,HandlerThread。

首先介绍的就是1.Message了,表示一个消息,关键的几个属性为what:消息的类型。arg1,arg2这个可以传递两个简单的整数。data一个bundle可以传递多个数据。obj可以传递一个对象。when用来表示时间的和消息队列有关,callback是一个runnable也就是说消息除了可以用来传递数据之外还可以处理runnable。这个都是在Handler中做的处理。target就是处理本消息的handler。实际上Message的重点并不是在于这个几个对象的封装,更加关键的是消息队列的链表维护。一直以来都说消息队列是一个链表结构的,但是你看一下MessageQueue并没有使用list或者给是collection。那这个链表到底在哪里?在Jni层?当然jni层实现了一个底层的looper和queue。但是在java层数据结构和操作都是健全的。实际上这个链表结构就是MessageQueue的一个Message对象mMesssages。这真的只是一个消息对象吗?是一个链表。其中的关键就在于Message的一个字段next。

tip:假如熟悉用数据结构,都知道链表的下一个节点的指针维护在本节点中,也就是Message的next实际是维护的下一个Message对象。十分简单的一个实现,

Message有这样一个方法Obtain是获取一个缓冲池中的Message。这个缓冲池是怎么维护的?就是4个static final的对象,sPoolSync是一个锁对象,由于缓冲池可能多线程操作,所以要同步。spool就是一个缓冲池,也是一个Message对象。spoolSize是缓冲池的大小。还有一个代表缓冲池的最大值MAX_POOL_SIZE = 10。也就是当有10个缓冲对象,这些对象实际上就是之前用过的Message对象,为了避免大量的创建,增加复用,减少垃圾回收。缓冲池的原理就是利用一个Message的链表来进行对象缓冲。

tip:所以Message的关键其实在于维护链表,为了更加明白。先说一下缓冲池利用和释放的机制:

synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                sPoolSize--;
                return m;
            }
        }

首先spool不为空,就说明有缓冲对象。然后m就是我们要使用的消息,先把spool引用给m。然后把m的next变为null。也就是把刚才的spool从链表中断开。spool = m.next在断开之前把缓冲池的操作根节点向后移了一位。其实都是这个原理,就是链表操作。这个是obtain的获取Message消息。再看一下如何缓冲已废弃的Message

clearForRecycle();

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

这个是Message的recycle函数,clearForRecycle就是把所有数据都置为空,然后当缓冲池没有10的时候,把要缓存的消息,实际就是笨消息实例,next节点变为spool,同时把spool根节点向前移一位,spool = this,容量++。

Message中并没有什么特殊的函数调用,注意sendToTarget这个方法内部是调用target里面的Handler的sendMessage发送给本身处理。假如是new的Message或者是复用的obtain里面的,这个方法不会起作用。

2.Handler:这个是Message的处理器兼职发送器,管理的就是Message的发送和处理,在使用的时候一般是复写Handler的handleMessage方法然后把Handler的对象传递出去发送消息。Handler中会有Looper,MessageQueue,Callback这几个主要的对象,这几个对象会在创建Handler的时候初始化。

消息发送,首先要获取消息,其中基本上都是调用的Message的Obtain的各种重载函数。所以有获取缓冲池中的Message,也可以自己创建。然后就要发送了基本所有的发送都是调用了sendMessqeAtTime,其中调用了MessageQueue的enqueueMessage把消息插入到消息队列中。其中不管Attime还是delayed,delayed就是当前时间+delayed的时间。所以都是调用attime的方法。还有一个sendEmptyMessage,实际上就是从Message缓冲池中去一个对象发送出去。

消息接收和处理:消息的接收实际是looper从MessageQueue中获取了消息之后,交给了Handler的dispatchMessage,在这个函数里首先查看Handler有没有Callback回调,假如有就执行,然后检测Message中有无Callback回调,有就执行。然后会交给HandleMessage,也就是我们复写的方法中执行。这也就是为什么Handler可以执行Message和runnable的原因。注意这些操作是在Looper线程中,假如是UI线程,必须注意执行的时间了。

3Looper:开始看looper的时候,会有人这么说线程一共分两种,一种就是带有Looper的,比如UI线程。一种没有looper假如你要在没有looper的线程中使用Handler就要自己构造looper或者是使用HandlerThread。这个实际上看了流程篇的介绍后就明白了。activity的ui线程实际上在创建的时候就创建了Looper,并且一直进行的消息循环。所有的UI线程的动作都是发消息到Looper中执行的。彻底明白了消息循环,就彻底的领悟了actvity运行的机制,包括生命周期都是在消息循环的规范下的,不能让我们*的定义程序流程。

每一个线程都只能有一个looper,为什么?从原理角度讲,looper作用是从队列中取消息,队列中一旦出现消息就要取出来,这就使得Looper必须做死循环,这也就是消息循环。那么死循环就会阻塞线程执行。一旦有两个looper,一个运行起来,另一个就没办法执行。从代码角度来看,looper是私有的构造,创建只能靠prepare,而创建出来的会保存到ThreadLocal中,也就是说线程唯一性。

looper循环,从prepare中初始化了之后,就可以执行loop方法了,一旦执行了loop之后就会不断的调用Queue的next方法尝试取消息,一旦取到就会dispatchMessage给各个Handler。处理消息。

tip:looper每个线程只有一个,但是可以有多个Handler,我们的activity都是在同一个线程中运行的,但是可以创建多了Handler,那么不会乱吗?不会这就是为什么每一个Message都有一个target,Message只能由创建他的线程处理。也就是目标targetHandler处理。

这有一个有趣的东西,假如没有target怎么办?这时候就要looper退出了,而且Looper的quit中也确实就是这么定义的,注意的是UI线程的looper是不能退出的,在检测target是否为空的时候,还检测了一个Boolean额变量mQuitAllowed.UI线程中的这个变量为false,所以不可以退出

4.MessageQueue:这个就是消息队列了,这个是和jni交互的类,在jni层也有一队列和loop,我觉得是为了优化消息循环。我们要注意的就是next和enqueueMessager方法了。next是为了获取消息队列的下一个Message,enqueueMessage是为了向队列中添加一个Message。具体的过程就不分析了,实际上就是他维护的Message队列mMessages的增加和删除操作。

5HandlerThread,实际上更加简单就是为我们添加了Looper的线程。有一个回调函数onLooperPrepared这个就是还没有执行loop之前的调用,你可以创建一个Handler。也就是说其实自己也可以做一个有looper的线程,你可以调用Looper.preapre然后在做一切的准备操作,调用Loop方法就进入了阻塞的循环,等待消息到来了。

总体来说Handler机制是很简单的,了解清楚概念之后就很容易了。