Android异步消息处理

来源:互联网 发布:it职业学校 编辑:程序博客网 时间:2024/06/05 03:17
异步消息简介

异步消息和线程的区别在于,线程执行完run()方法后,线程就结束了,而异步消息是在线程内部有一个消息队列,写一个死循环,
一直去消息队列里去取消息,然后根据消息类型处理相应的操作,如果取不到消息就一直在等待。

异步认为一般用于:任务需要常驻,比如处理用户交互的屏幕触摸事件处理;根据不同的消息类型处理不同的操作。

实现上就是:
1.每个异步任务要有一个消息队列;
2使用while(true)无限循环,读取消息,处理消息,执行回调函数等;
3,外部可以向队列发消息,消息队列注意线程安全。

Android中的异步消息

看下图,这个就是android中的实现图,
可以这样描述:
(1)在线程内部有一个或者多个Handler对象,外部程序通过Handler对象向线程发送异步消息,
消息由Handler对象传递到MessageQueue对象中。
(2)线程的主执行程序中从MessageQueue中读取消息,并回调Handler对象中的回到函数handleMessage()

注意:MessageQueue在线程内部只能包含一个;每个消息对应一个Handler对象


Looper,MessageQueue,Handler的源码介绍

先看下使用
-----------------------------------------------------------
Looper.java

Looper的作用有两点:
第一个是创建消息队列;
第二个就是无限循环读取消息队列里的消息。

创建消息队列

当创建消息队列的时候,需要首先调用Looper.prepare()静态函数。


第73行代码,在sThreadLocal里面检查一下当前线程是否已经调用过prepare()方法了,即检查一下和当前线程相关的Looper对象是否
已经创建。这里就是保证一个线程只能产生一个Looper对象,如果多次调用会报错。Looper对象被存储到了线程本地存储里(ThreadLocal),只存和当前线程相关的内容。


对于不了解ThreadLocal的,自行到网上查询一下,简单说下,就是保存线程范围的数据。

接着看下Looper的构造函数


Looper的构造函数时私有的,同事在构造函数里,创建了一个消息队列mQueue(第182行),并且把调用次Looper的线程赋值给mThread(第184行),代表这个Looper从属的线程。

这样队列就准备好了。

读取消息队列

从调用Looper.loop();的时候开始读取消息。loop()方法的源码如下:


108行:获得Looper对象,就是简单的从sThreadLocal里面把和当前线程相关的Looper对象找出来,如下:

109行:从Looper对象里找出当前线程的消息队列MessageQueue对象。

113和114行先别管,以后再讲解。

直接进入while循环,开始读取消息了
117行:从消息队列里读取消息,返回的是Message,Message代表一个消息。如果没有消息,阻塞在这里,直到有消息返回为止。
121行:只有消息不为null才去处理消息
122行:如果消息的target为null的话,就结束这次循环,继续循环下次,target就是消息对应的Handler对象。
130行:msg.target.dispatchMessage(msg),把消息通过Handler分发下去,就是把消息传递给Handler的回调函数
146行:msg.recycle();当处理完一次消息之后,要对消息进行回收处理,因为在Message内部有一个消息池,用来避免不停的
          创建删除消息对象,内部只是把消息设置成空闲状态,以便重复利用。

MessageQueue

其实上面介绍Looper的时候,异步消息基本上大体就是这样处理的。
这里分析下MessageQueue的源码

消息队列采用排队的方式进行消息处理的,先到的消息最先得到处理(还有一种情况,消息被指定某个时刻进行处理,如果时刻不到也不会
去处理)。消息队列中的消息用Message类表示,消息是以链表的形式存储,Message对象内部包含一个next变量,该变量指向下一个消息。

消息队列的主要功能有两个:
1.取出消息
2.放进消息

取出消息

从Looper的源码中可以知道,是调用的消息队列的next()方法取出的消息。看MessageQueue的源码:



从115行看起,这里是循环取消息。其实再MessageQueue.java的对象是没有这个消息队列,真正的消息队列在C代码中的,
119行:private native void nativePollOnce(int ptr, int timeoutMillis)是个native函数,从C的消息队列中取消息,C中有个
            MessageQueue.cpp文件,里面定义了一个MessageQueue的C++类。调用C++代码,取出消息,把取出的消息赋值古java中
            mMessages变量,如果没有就会挂起等待。
121行开始,就是读取消息
123行:获得当前时间
124行:把取出的消息mMessages赋值为final修饰的msg变量,便于后面的处理。
125行:判断取出的消息为空
127-133行:如果到了执行时间,就会把消息返回给Looper。
134行,如果没到执行时间,则继续循环,去取消息。消息队列的读写不能同时进行,所以用synchronized关键字包围起来。
从上面的代码可以看出,当时间未到或者消息为空的时候,队列会处于空闲状态,如果没有空闲时的任务的话,会继续到队列里
去取消息。其中mIdleHandlers存储的是空闲任务,如果有空闲任务的话,空闲的时候会去执行。
看下面的代码:158-174行

放进消息队列

对应该的方法是

主要看方法被synchronized包围的地方


205行之前都是做一些数据检查
206-221行主要是看看这个消息是不是队列的头部,如果不是头部,needWake为false如果不是的话,就需要把这个消息
放在队尾,如果是队头或者将会变成 对头,那么needWake就会是true,会调用navtiveWake,把消息放到C++的那个消息队列里去,
最后函数返回true,表示插入队列成功。

MesageQueue的构造函数,调用了nativeInit()函数,调用的是native的,在C代码中初始化一个消息队列。

Handler

先看下构造函数

119行:获得当前线程的Looper对象,赋值给mLooper
120-123:检查Looper对象是否为空,为空的话,肯定就没有消息队列了
124行:获得当前线程对应的消息队列
125行:mCallback是个回调对象

再一个就是大家比较关注用handler发送消息

接着调用:

第二个参数delayMillis代表消息延迟的时间,没有延迟就是0
接着调用sendMessageAtTime
第二个参数是消息的发送时间
456行:把msg的target设置成当前的Handler对象,这样每个消息就可以关联一个自己的Handler了,所以最后就能回调到这个Handler的回调函数。
457行:把消息放进消息队列,然后返回成功放进去与否。

回调Handler的函数:
其实是在Looper的loop()方法中执行的,请看
调用的handler对象的dispatchMessage(msg)方法,方法体如下:

handleCallback(msg)先不讲。
99行:调用了handleMesssage(msg)函数
handleMessage(msg)函数如下,就是空函数,需要程序员去实现:
解释一个callback,这个是定义在Handler里的一个内部接口,如果这个接口对象存在的话,就不会回调handler的handleMessage(msg)方法,从94-98行代码可以看出。
同理当msg的callback存在的时候,也不调用handler的handleMessage(msg)回调方法了,而是调用handleCallback(msg)方法。
回调的Message中的callback.run()方法。其实Message中的callback就是一个Runable实现


Message解释

Handler中有一个obtainMessage()方法,来获取可以回收利用的Message对象。
其实调用的是Message.obtain(handler)方法
下面是Message类中的方法调用
138行调用了obtain()方法返回一个Message
139行:返回的Message对象的target赋值为传过来的Handler
mPoolSync就是对象锁,防止同时被访问而设置的
mPool就是Message对象,Message的next变量也是Message对象,是链表形式的,可以组成一个消息链条,只要有消息空闲就可以取出来使用。
前面的Looper执行完成之后,会有一个Message回收的调用,就是这个方法
clearForRecycle()把消息还原到初始状态,代码入下:


源码分析完了,如果感觉不错,给个好评吧,哈哈
原创粉丝点击