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

一直以来对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()函数。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值