快上网专注成都网站设计 成都网站制作 成都网站建设
成都网站建设公司服务热线:028-86922220

网站建设知识

十年网站开发经验 + 多家企业客户 + 靠谱的建站团队

量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决

Android的HandlerLooperMessage机制应用实例与详解(二)

    上一篇博文给出了Android中基于Handler Looper机制实现线程间通信的两个典型实例。本文将对该机制的基本原理进行较深入的研究。个人认为,学好Android编程最好的老师就是Android的源代码,下面将基于Android-19的源码进行分析,重点阐述分析思路。

成都创新互联公司主要从事成都做网站、网站建设、网页设计、企业做网站、公司建网站等业务。立足成都服务威宁,十年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:13518219792

    要分析Handler Looper机制,自然想到去看Handler类和Looper类的源码(分别位于Handler.java和Looper.java两个文件中)。简单阅读两个类的描述后,在Looper类的描述中能找到以下一段示例代码。

This is a typical example of the implementation of a Looper thread, * using the separation of {@link #prepare} and {@link #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();
*      }
*  }
*/

    这段代码给出了Handler Looper机制实现进程间通信的三大基本步骤,包括Looper的两个函数prepare()和loop(),以及Handler的handleMessage函数。上一篇博文中实例二模拟子线程向UI主线程传递信息的程序就基本上是直接copy这段示例代码实现的。

    先看第一个步骤:调用Looper.prepare()函数,猜测应该是创建Looper对象,做些初始化工作。代码如下:

/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/
public static void prepare() {
    prepare(true);
}

    直接调用了重载函数prepare(true);

//ThreadLocal实例为多个线程共享,但保证每个线程的存储空间相互独立
static final ThreadLocal sThreadLocal = new ThreadLocal(); 
//消息队列
final MessageQueue mQueue;
//当前线程引用
final Thread mThread;
private static void prepare(boolean quitAllowed) {
    //保证一个线程最多只能创建一个Looper对象。
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //创建Looper对象并存储到当前线程独立的存储空间中
    sThreadLocal.set(new Looper(quitAllowed));
}

private Looper(boolean quitAllowed) {
        //创建消息队列(线程间即通过该消息队列实现通信)
        mQueue = new MessageQueue(quitAllowed);
        //获得当前线程的引用
        mThread = Thread.currentThread();
    }

    看到这里明白了,Looper的prepare函数实际上创建了Looper对象并把对象保存到当前线程独立的存储空间中。这里,Looper的构造函数是私有的,所以外部无法直接通过new Looper()随意创建Looper对象。而只能通过Looper的prepare()函数创建。这样做能够保证对于某一个线程,最多只会创建一个Looper对象的实例,这实际上就是设计模拟中的单体模式。此外,在Looper的私有构造函数中还创建了消息队列并获得当前线程(即创建Looper的线程)的引用。

    先跳过Handler.handleMessage(Message msg),直接看Looper.loop()的实现。

/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/
public static void loop() {ui
    //myLooper()函数通过sThreadLocal.get()判断当前线程是否已经创建了Looper的实例
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;

    // Make sure the identity of this thread is that of the local process,
    // and keep track of what that identity token actually is.
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    //这里开始无限循环
    for (;;) {
        //从消息队列中取出一条消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger
        Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                      msg.callback + ": " + msg.what);
        }
        //这里是关键,调用了dispatchMessage函数对从消息队列取出的msg进行分派
        msg.target.dispatchMessage(msg);

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the
        // identity of the thread wasn't corrupted.
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }
        //消息使命完成,将其放回消息池
        msg.recycle();
    }
}

    到这里,Looper扮演的角色已经明朗了,主要就是通过loop()函数中的那个无限循环不断从消息队列中取出消息,并通过dispatchMessage()方法将消息派送出去。那么消息队列中的消息从哪里来,又会被派送到哪里呢?

    先来分析第一个问题,消息从哪里来。上一篇博文的实例中,消息源线程均通过调用Handler的sendMessage()函数来发送消息。进入Handler.java文件看其中的sendMessage()函数。  

public final boolean sendMessage(Message msg)
{
    return sendMessageDelayed(msg, 0);
}

    这里调用了sendMessageDelayed,延时0毫秒。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
   if (delayMillis < 0) {
       delayMillis = 0;
   }
   return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

    进一步调用了sendMessageAtTime,在当前时刻发出。

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
   //获得消息队列的引用
   MessageQueue queue = mQueue;
   if (queue == null) {
       RuntimeException e = new RuntimeException(
                 this + " sendMessageAtTime() called with no mQueue");
       Log.w("Looper", e.getMessage(), e);
       return false;
   }
   return enqueueMessage(queue, msg, uptimeMillis);
 }

    再看enqueueMessage函数。

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
   //设置target handler
   msg.target = this;
   if (mAsynchronous) {
        msg.setAsynchronous(true);
   }
   //将消息插入到消息队列中
   return queue.enqueueMessage(msg, uptimeMillis);
}

    看到这里已经明朗了,消息源线程通过Handler.sendMessage发送消息,实际上就是把消息插入了与之关联的消息队列中。在enqueueMessage函数中有一条关键语句msg.target = this,通过这条语句就把Handler和Looper关联起来了(在Looper.loop()的循环中就是通过msg.target属性找到发送消息的Handler并调用其dispatchMessage()函数派发消息的).

    搞清楚了消息从哪里来,接下来分析消息被派发到哪里,接着看dispatchMessage()函数的实现。

/**
* Handle system messages here.
*/
public void dispatchMessage(Message msg) {
    //若消息对象实现了其中的Runnable接口,调用对应的回调函数,即为message.callback.run())
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        //若实现了Handler类的Callback接口,调用接口的回调函数
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //将消息返回Handler的消息处理回调函数(是Handler的成员函数,示例代码中回调的就是该函数)
        handleMessage(msg);
    }
}

    可见,dispatchMessage函数中根据msg,handler对象的设置情况调用相应的消息处理回调函数,我们只需要在这个回调函数中添加代码,就可以进行消息处理。示例代码的第二个步骤中的handleMessage函数就是在这里被回调的。

    下面回到示例代码中的第二个步骤:

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

   这段代码创建了Handler的对象,并覆盖了其中的HandleMessage方法,用户可以添加自己的消息处理函数。 handleMessage回调函数上面已经分析过了,下面主要看看创建handler对象时都做了哪些事情。转入Handler.java文件,看Handler的构造函数(找不带参数那个)。

/**
* Default constructor associates this handler with the {@link Looper} for the
* current thread.
*
* If this thread does not have a looper, this handler won't be able to receive messages
* so an exception is thrown.
*/
public Handler() {
    this(null, false);
}

    调用了下面的双参数的构造函数。

final Looper mLooper;
final MessageQueue mQueue;
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class 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());
        }
    }
    //获得当前线程Looper对象的引用
    mLooper = Looper.myLooper();
    if (mLooper == null) {
         throw new RuntimeException(
             "Can't create handler inside thread that has not called Looper.prepare()");
    }
    //获得Looper关联的消息队列    
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

    到这里发现Handler的构造函数主要做了两件事:1)获得当前线程Looper对象的应用.2)获得与Looper关联的消息队列的引用。到这里,Handler、Looper、消息队列三者就已经关联起来了。

    下面通过一个示意图对上面的分析进行总结。

Android的Handler Looper Message机制应用实例与详解(二)

    由图可见,基于Handler Looper机制传递消息主要包括以下几个步骤。

    (1)目标线程调用Looper.prepare()创建Looper对象和消息队列。

    (2)目标线程通过new Handler()创建handler对象,将Handler,Looper,消息队列三者关联起来。并覆盖其handleMessage函数。

    (3)目标线程调用Looper.loop()监听消息队列。

    (4)消息源线程调用Handler.sendMessage发送消息。

    (5)消息源线程调用MessageQueue.enqueueMessage将待发消息插入消息队列。

    (6)目标线程的loop()检测到消息队列有消息插入,将其取出。

    (7)目标线程将取出的消息通过Handler.dispatchMessage派发给Handler.handleMessage进行消息处理。

    到这里整个Android的Handler Looper机制传递消息原理就分析完毕了。还有一个问题值得一提,回顾一下上一篇博文的示例1模拟从网络上下载数据的程序,UI主线程只通过new Handler()创建了Handler对象的实例并覆盖了其handleMessage函数。在代码中并没有看到调用Looper.prepare和Looper.loop(),那么UI主线程中没有创建Looper对象吗?下面就来分析这个问题,既然是UI主线程,那么自然是在启动应用时候由系统自动创建的,创建过程中是否已经创建了Looper对象并调用loop()进行监听了呢?转到ActivityThread.java,找到其中的main函数,这里即为Android程序的入口。在其中能看到以下两行代码。

Looper.prepareMainLooper();
    ...... ...... 
Looper.loop();

    再看prepareMainLooper函数的实现:

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
       if (sMainLooper != null) {
           throw new IllegalStateException("The main Looper has already been prepared.");
       }
       sMainLooper = myLooper();
    }
}

    在这里调用了prepare创建Looper对象。所以说,对于UI主线程而言,其Looper对象是由系统创建好的,用户就无需自行创建了。  

    


网页名称:Android的HandlerLooperMessage机制应用实例与详解(二)
网站路径:http://6mz.cn/article/gcegpc.html

其他资讯