android 异步消息处理线程

       异步消息处理线程是指,线程启动后会进入一个无限循环体之中,每循环一次,从其内部的消息队列(MessageQueue)中取出一个消息,并且回调相应的消息处理函数,执行完一个消息后则继续循环。   如果消息队列为空,则线程会暂停,一直到消息队列中有了新的消息。

      实现异步线程的一般思路

               如图1:

                       

                                           

                                                                                                                                图1


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

                                  1.每个异步线程内部包含一个消息队列(MessageQueue),队列中的消息一般采用排队机制,即先到达的消息会先得到处理。

                                   2.线程的执行体中使用while(true)进行无限循环,循环体中从消息队列中取出消息,并根据消息的来源,回调相应的消息处理函数。

                                   3.其他外部线程可以向本线程的消息队列中发送消息,消息队列内部的读/写操作必须进行加锁, 即消息队列不能同时进行读/写操作。



     Android中异步线程的实现方法

                                 在异步消息线程内部可以有一个和多个Handler对象,外部程序(外部线程)通过该Handler对象向线程发送异步消息, 消息经由Handler传递到MessageQueue对象中。异步消息线程内部只会有一个MessageQueue对象,在异步消息线程主执行函数中从MessageQueue中读取消息,并回调Handler对象中的回调函数handleMessage()。如图2

            

  

                                          

                                                                         图2



下面分析的是android异步消息线程内部的Handler,MessageQueue,Looper类的调用过程


线程局部存储(Thread Local Storage)

                      通过调用Looper类的静态方法prepare()为线程创建MessageQueue对象,该函数的代码如下:

                                          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的类型是ThreadLocal,该类的作用是提供“线程局部存储”,那么什么是线程局部存储呢(TLS)?

     ThreadLocal类的对象的作用域是当前线程。 当同一个线程引用该变量时,其值总是相同的,而从不同的线程中引用该变量时,其值应该不同。

      ThreadLocal就是能够提供这种功能的类,Looper内部的sThreadLocal变量是当该进程第一次调用Looper.prepare()是被复制的,之后该进程中的其他线程调用prepare()函数时,sThreadLocal变量就已经被赋值了。sThreadLocal对象内部会根据调用prepare()线程的id保存一个数据对象,这个数据对象就是所谓的“线程局部存储"对象,该对象是通过sThreadLocal的set()方法设置进去的,Looper类中保存的这个对象是一个Looper对象。

        prepare()函数中首先调用sThreadLcoal.get()函数获取该线程对应的Looper对象,如果该线程已经存在Looper对象,则提示出错,否则,为该线程创建一个新的Looper对象。为什么一个线程只能又一个Looper对象呢?这仅仅是异步线程所需要的,因为每个Looper对象都会定义一个MessageQueue对象,一个异步线程中只能有一个消息队列,所以只能有一个Looper对象,这与“线程局部存储"本身没有什么关系。

                      注意:线程局部存储(TLS)变量           在本线程内的任何对象内保持一致

                                  静态变量                                        在本进程内的任何对象保持一致



Looper类

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

            在Looper的静态函数prepare()中,会给线程局部存储变量中添加一个新的Looper对象,Looper的构造函数中则会创建一个MessageQueue对象,如以下代码所示:

                                   private Looper(){

                                       mQueue = new MessageQueue();

                                       mRun = true;

                                       mThread = Thread.currentThread();

                                   }


             对于一个程序员来说,当需要把一个线程变为异步消息处理线程时;应该在Thread类的run()函数中先调用Looper.prepare()为该线程创建一个MessageQueue对象,然后在调用Looper.loop()函数,使当前线程进入消息处理循环。loop()函数的代码如下:

            public  static final void loop(){

                   Looper me = myLooper();

                   MessageQueue queue = me.mQueue;

                   while(true){

                           Message msg = queue.next();//might block

                            if(msg != null){


                            if(msg.target == null){

                                         return ;

                     }

                 if(me.mLogging != null)

                            me.mLogging.println(">>>> Dispatching to " +  msg.target + " " +msg.callback + ": " + msg.what);

                 msg.target.dispatchMessage(msg);

                  if(me.mLogging != null)

                            me.mLogging.println("<<<< Finished to    " + msg.target + "   " + msg.callback);

                    msg.recycle();

                     }

        }


}

          

 这段代码的执行流程如下:

         1.调用myLooper()函数返回当前线程的Looper对象,该函数内部仅仅通过调用sThreadLocal.get()方法返回当前线程id对应的Looper对象。

          2.进入while(true)无限循环。

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

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

的类型是Handler。

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



MessageQueue

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

           MessageQueue中的两个主要函数是“取出消息"和”添加消息",分别为函数next()和enquenceMessage()。

       

 

Handler          

           尽管MessageQueue提供了直接 读/写 的函数接口,但对于应用程序员而言,一般不直接 读/写 消息队列。 前面讲过,在Looper.loop()函数中,当取出消息后,会回馈msg.target对象的handlerMessage()函数,而msg.target的类型正事Handler.

          程序员一般使用Handler类向消息队列中发送消息,并重载Handler类的handleMessage()函数添加消息处理代码。

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

                        mLooper = Looper.myLooper();

                           if(mLooper == null){

                      throw new RuntimeException("can't create handler inside thread that has not call"

                 } 

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

 创建Handler对象可以在执行Looper.loop()函数之前,也可以在执行之后。在以往的应用程序开发中,程序员一般在Activity对象的初始化代码中添加Handler对象,事实上,在Activity对象被构造前,Activity所在的线程已经执行了Looper.prepare()函数。

        一个线程中可以有多个Handler对象。在Looper.loop()函数中,不同的Message对应不同的Handler对象,从而回调不同的handleMessage()函数。

          

          

                                 





                                                                   

       

     

     

  

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值