国内最全IT社区平台 联系我们 | 收藏本站
华晨云阿里云优惠2
您当前位置:首页 > php开源 > 综合技术 > Android中异步消息处理机制

Android中异步消息处理机制

来源:程序员人生   发布时间:2015-04-03 08:33:23 阅读次数:2691次


1. Thread Local Storage (线程局部存储)

     我们通过位于android.os包下的Looper.class源码可以看到成员变量区有1个线程局部变量sThreadLocal,该类的作用是线程局部存储?那末是线程局部存储TLS?这个问题可以从变量作用域的角度来理解。
     
     变量的常见作用域1般包括以下几种。
  • 函数内部变量。其作用区域是该函数,即每次调用该函数,该变量都会重新回到初始值。
  • 类内部的变量。其作用就是该类所产生的对象,即只要该对象没有被烧毁,则对象内部的变量则1直保持。
  • 类内部的静态变量。其作用是全部进程,即只要在该进程中,该变量的值就会1直保持,不管使用多少类来构造这个对象,该变量只有1个赋值,且1直保持。
对类内部的静态变量而言,不管是从进程中那个线程援用该变量,其值总是相同的,由于编译器内部为静态变量分配了单独的内存空间。但有时我们却希望,当从同1个线程中援用该变量时,其值总是相同的, 而从不同的线程援用该变量,其值应当不同,即我们需要1种作用域为线程的变量定义,这就是线程局部存储。

ThreadLocal内部实际上是使用弱援用WeakReference来存储当前线程对象,但是不同的线程有不同的ThreadLocal对象,因此会有1个用于区分的id,由此内部封装了1个静态内部类Values,这个类就相当于1个map,存储了每个线程的实例的值。

sThreadLocal在prepare()函数被调用的时候就进行了1次赋值,即构造1个新的Looper对象存储到本地线程局部变量中,固然1个线程只能有1个Looper对象,否则会抛出以下图所示的异常。

          

Looper中有2个用于构造Looper对象的方法。1个是prepare(),另外一个是prepareMainLooper()。
prepare()我们已说过了,下面说下prepareMainLooper()。
prepareMainLooper(),由方法注释我们可以知道,这个利用程序的mainlooper对象是由android环境创建的,所以你自己永久没必要调用这个函数。
               

2. Looper

我们可以从类注释来了解Looper类的作用究竟是干甚么的。

Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare in the thread that is to run the loop, and then loop to have it process messages until the loop is stopped.

Most interaction with a message loop is through the Handler class.

This is a typical example of the implementation of a Looper thread, using the separation of prepare and loop to create an initial Handler to communicate with the Looper.

  class LooperThread extends Thread {
      public Handler mHandler;

      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }
Looper类的作用就是给在线程内部创建1个消息循环,固然线程本身内部是没有1个消息循环机制;在run()函数首行调用Looper.prepare(),即便创建了1个消息循环队列,然后在run函数尾行调用Looper.loop()则开始处理消息直到消息循环停止。
大多数消息循环的交互是通过Handler类进行的。


3. Handler

这里我们只从消息循环机制角度来分析这个Handler。首先看Handler的构造函数
/**
     * Default constructor associates this handler with the queue for the
     * current thread.
     *
     * If there isn't one, this handler won't be able to receive messages.
     */
    public Handler() {
        if (FIND_POTENTIAL_LEAKS) {
            final Class<? extends Handler> klass = getClass();
            if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                    (klass.getModifiers() & Modifier.STATIC) == 0) {
                Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                    klass.getCanonicalName());
            }
        }
         //从TLS(局部线程存储)中取出已寄存好的Looper对象
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        //将Looper对象中的MessageQueue赋值给Handler中的对象
        mQueue = mLooper.mQueue;
        mCallback = null;
    }

然后我们在从使用的角度分析,这里我只从异步处理UI界面分析。
我们(程序员)通过 Handler调用sendMessage()函数在线程内部向UI主线程发送1个Message对象,该Message对象会顺次通过Handler中的函数
sendMessage(...) -> sendMessageDelayed(...) -> sendMessageAtTime(...) 
终究会通过sendMessageAtTime发送消息对象。
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
    {
        boolean sent = false;
        MessageQueue queue = mQueue;
        if (queue != null) {
            msg.target = this;
       
            //将消息对象加入到消息队列

            sent = queue.enqueueMessage(msg, uptimeMillis);
        }
        else {
            RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
        }
        return sent;
    }

然后我们在来看看enqueueMessage进行了甚么操作。

    final  boolean enqueueMessage(Message msg, long when) {
                      ...

        if (needWake) {
            nativeWake(mPtr);
        }

                      ...
   }

nativeWake是1个java本地方法,这里触及了消息机制中的Sleep-Wakeup机制,关于如何唤醒Looper线程的动作,这里不做赘述,其终究就是调用了 native层的Looper的wake函数,唤醒了这个函数以后,就开始进行消息循环
 

生活不易,码农辛苦
如果您觉得本网站对您的学习有所帮助,可以手机扫描二维码进行捐赠
程序员人生
------分隔线----------------------------
分享到:
------分隔线----------------------------
关闭
程序员人生