您的位置:首页 > 产品设计 > UI/UE

android异步消息处理机制 handler MessageQueue Looper 类 学习

2015-03-04 09:32 633 查看
一直以来对android的异步消息处理机制都弄不太明白,handler类也只能依样画葫芦使用,内部如何实现完全不了解 

今天翻看 android内核剖析 这本书, 总算稍微弄明白了点 ,所以写下来,一来加深自己的理解 ,二来方便以后再研究  .

异步消息处理线程 

一般的普通线程 ,执行完run()之后该线程就会结束 。 而对于异步消息处理线程,run()方法内部会启动一个无限循环,每次循环都会从其内部的消息队列中取出一条消息,并回调相应的消息处理函数。执行完一条消息后继续循环 。如果消息队列中没有消息,该线程将被挂起 ,等待消息队列中有新的消息 。

实现异步线程要解决的问题具体包括:

1.每个异步线程内部有且仅有一个消息队列(MessageQueue),队列采用先进先出的排队机制。
2.线程执行体run()方法中使用 while(true) 无限循环 。循环体从消息队列(MessageQueue) 中依次取出消息,并根据消息来源,回调相应的消息函数处理。
3.其他外部线程可以向异步线程发送消息,消息队列内部必须对读写操作加锁。

andriod异步线程实现方法



在线程内部,有一个或者多个handler对象,其他线程可通过handler对象向异步线程发送消息。消息经过handler对象传递到MessageQueue对象中。线程内部只能包含一个

MessageQueue对象,线程主执行函数中从MessageQueue中读取消息,并回调Handler对象中的回调函数handleMessageO。

Looper对象

Looper的作用有两点,第一是为调用该类中静态函数prepare()的线程创建一个消息队列;第二是提供静态函数loop(),使调用该函数的线程进行无限循环,并从消息队列中读取消息。

首先看调用静态函数prepare(),为线程创建消息队列MessageQueue对象

线程内部只能包含一个MessageQueue对象 ,该MessageQueue对象通过调用Looper类的静态方法prepare()来创建。该静态方法代码:

public static final void prepare(){
if (sThreadLocal.get() != null)
throw new RuntimeException("Only one Looper may be created per thread");
sThreadLocal.set(new Looper());
}


sThreadLocal变量是一个ThreadLocl类型。该类的作用是提供“线程局部存储”。通俗来说就是,此类的作用域为线程,同一个线程引用该变量时,其值总是相同的,而从不同的线程引用该变量,其值不同。

ThreadLocl类 的get()方法返回此线程局部变量的当前线程副本中的值 。set()方法将此线程局部变量的当前线程副本中的值设置为指定值。

perpare()方法首先判断当前线程中是否包含MessageQueue对象,因为线程局部变量sThreadLocal保存的是一个Looper对象,而该Looper对象中保存了一个MessageQueue对象。如果不为空,则报异常,否则给当前线程局部变量设置为一个Looper对象。 而Looper对象构造函数中会创建一个MessageQueue对象,如下:
private Looper(){
mQueue = new MessageQueue();
mRun = true;
mThread =Thread.currentThread();
}

对程序员来讲,当需要把一个线程变为异步消息处理线程时;应该在Thread类的run()函数中先调用Looper.repare()为该线程创建一个MessageQueue对象,然后再调用Looper.loop()函数,使当前线程进入消息处理循环。loop()函数的代码如下:
public static final void loop(){
Looper me = myLooper();
MessageQueue queue =me.Mqueue;
while (true){
Message mag = queue.next();
//if (!me.mRun){
//    break;
//}
if(msg!=null){
if(msg.target == null ){
return;
}
if(me.mLogging!=null){
//日志输出
}
msg.target.dispatchMessage(msg);
if(me.mLogging!=null){
//日志输出
}
msg.recycle();
}
}
}


该段代码的执行流程如下:
调用myLooper()函数返回当前线程的Looper对象,该函数内部仅仅通过调用sThreadLocal.get()方法返回当前线程id 对应的Looper对象。

进入while(tme)无限循环。

     调用MessageQueue对象的next()函数取出队列中的消息。注意,如果当前队列为空,当前线程会被挂起,也就是说,next()函数内部会暂停当前线程。

     回调msg.target.dispatchMessage()函数,完成对该消息的处理,也就是说,消息的具体处理实际上是由程序指定的。msg变量的类型是Message,msg.target的类型是            Handler。

    每处理完该消息后,需要调用mSg.reCyCleO回收该Message对象占用的系统资源。因为Message类内部使用了一个数据池保存Message对象,从而避免不停地创建和删除       Message类对象,因此,每次处理完该消息后,需要将该Message对象表明为空闲状态,以便使该Message对象可以被重用

MessageQueue 

消息队列釆用排队方式对消息进行处理,即先到的消息会先得到处理,但如果消息本身指定了被处理的时刻,则必须等到该时刻才能处理该消息。消息在MessagQueue中使用Message类表示,队列中的消息以链表的结构进行保存,Message对象内部包含一个next变量,该变量指向下一个消息。

MessageQueue对象内部主要是JNI函数 ,用于取出消息和添加消息

Handler

我们一般使用handler类向消息队列中发送消息,并重载Handler类的handleMessage()方法添加消息处理代码 

Handler对象只能添加到有消息队列MessageQueue对象 的线程中 ,否则发生异常,以下代码来自Handler类的构造函数:

mLooper = Looper.myLoopre();
if(mLooper==null)
throw new RuntimeException("Can`t create handler inside thread that has not call Looper.prepare()")


因此,在构造Handler对象前,必须已经执行过Looper.prepare(),但prepare()不能被执行两次

我们一般在Activity对象的初始化代码中直接添加Handler对象,事实上,在Activity对象被构造前,Activity对象所在的线程已经执行了Looper.prepare()方法。

Activity对象所在的UI线程是从ActivityThread运行的,在该类中的main()方法中,已经使用Looper.prepareMainLooper()为该线程添加了 Looper对象,即已经为该线程创建了消息队列(MessageQueue)

一个线程中可以包含多个Handler对象。在Looper. loop()函数中,不同的Message对应不同的Handler对象,从而回调不同的handleMessage()函数。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐