Android 之 Looper、MessageQueue、Handler 与消息循环

在android的activity中有各种各样的事件,而这些事件最终是转换为消息来处理的。android中的消息系统涉及到:
 *  消息发送
 *  消息队列
 *  消息循环
 *  消息分发
 *  消息读取
  消息对应的重要类有MessageQueue、Looper、Handler,它们分别对应着消息队列、消息循环和消息处理。

 

Handler类:
Handler主要用来发送消息和处理消息。每个handler实例都对应着一个线程和该线程的消息队列。
当你创建一个handler对象时,该handler对象就属于创建它的线程,并和该线程的消息队列绑定,比如在主线程中创建handler对象,那么该handler就只属于主线程,并且和主线程的消息队列绑定。(当然,每个线程都有自己的消息队列,在android中,消息队列是针对与线程的)。这样,该handler就可以发送消息到该消息队列并且处理该消息队列的消息了。
当执行一些费时的操作时,比如需要远程网络访问、下载等操作时,一般情况下都会启动一个新的线程去操作。而不会放在ui线程去做,这样可以防止android的5秒无相应导致的ANR异常。子线程中返回的结果怎样更新到ui线程呢,这时就可以通过handler来处理了。可以在主线程中定义handler,然后通过主线程handler把子线程中的消息发送到主线程对应的消息队列。在主线程中通过handler.handlerMessage就可以处理消息了,并更新到ui了。
我们刚才说过,Handler主要是用来发送、处理消息的。那么消息循环、队列是在哪里管理的。答案是:Looper、MessageQueue中。

Looper类:
looper类主要用来开启线程的消息循环。默认情况下,系统在启动的时候会为主线程创建消息循环。其他新创建的线程则没有,
如果需要,可以在该线程内调用Looper.prepare()来启用looper对象,然后调用Looper.loop()进入消息循环。
这样该线程就具有消息循环机制了,比如:

  *  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中创建的,看下Looper的loop()方法,这个方法就是用来做消息循环用的。
   *     public static final void loop() {
   *      Looper me = myLooper(); //获得当前线程的Looper对象
   *      MessageQueue queue = me.mQueue;  //获得当前线程的消息队列
   *     while (true) { //条件为true、一直循环执行。消息循环
   *         Message msg = queue.next(); // might block
   *           //if (!me.mRun) {
   *         //    break;
   *          //}
   *         if (msg != null) {
   *             if (msg.target == null) {
   *                  // No target is a magic identifier for the quit message.
   *                 return;
   *            }
   *               if (me.mLogging!= null) me.mLogging.println(
   *                     ">>>>> Dispatching to " + msg.target + " "
   *                     + msg.callback + ": " + msg.whclearForRecycleat
   *                      );
   *              msg.target.dispatchMessage(msg); //消息分发
   *             if (me.mLogging!= null) me.mLogging.println(
   *                      "<<<<< Finished to    " + msg.target + " "
   *                      + msg.callback);
   *              msg.recycle(); //消息已经分发出去,对消息进行回收处理
   *          }
   *      }
   *  }

我们再来看Handler的构造函数,在Handler的构造函数中获得当前线程的Looper对象、和消息队列。消息队列也是从looper中获得的,刚才我们说过。
  *   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());
  *           }
  *       } 
  *        //在Handler的构造函数中获得当前线程的Looper对象、和消息队列。消息队列也是从looper中获得的
  *       mLooper = Looper.myLooper();
  *       if (mLooper == null) {
  *           throw new RuntimeException(
  *               "Can't create handler inside thread that has not called Looper.prepare()");
  *       }
  *       mQueue = mLooper.mQueue;
  *       mCallback = null;
  *   }

在handler中还有一些重要的方法:

 

handleMessage(Message)   //处理消息

dispatchMessage(Message)  //分发消息

sendMessage(Message)   //发送消息

 

到这里我们大致可以了解android中的消息流程大概是这样的:
Handler获得当前线程的Looper、MessageQueue,并且发送消息到MessageQueue中。 Looper对消息做循环,并通过 msg.target.dispatchMessage来分发消息(target应该就是handler)。然后Handler.handlerMessage处理消息

android的looper,handler消息小结

 在android开发中,为了UI线程能及时响应需要避免在其中执行耗时操作,以防止界面假死甚至ANR。我们一般把耗时操作如下载,查询放在一个单独的线程中。这之后再将结果更新到UI界面。android平台在非UI线程中更新界面大致有以下几种方式:

Activity.runOnUiThread(Runnable)
View.post(Runnable)
AsyncTask<Params, Progress, Result>
Handler.post(Runnable)
其中后面两种方式我们用的比较多,而AsyncTask也是基于Handler进行封装的,可以看出Handler是我们更新UI线程的利器。看下常用的Handler构造方法:

public Handler() {
   this(null, false);
}
public Handler(Callback callback, boolean async) {
    .....
    mLooper = Looper.myLooper();
    .....
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
从第二个方法里面可以看出,新建一个handler对象,主要是使其final MessageQueue mQueue作用域指向一个对象,其它的作用域用默认的都行。mQueue来自mLooper = Looper.myLooper();看下myLooper()方法的实现:

public static Looper myLooper() {
    return sThreadLocal.get();
}
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
        到这里是时候说明下ThreadLocal了,ThreadLocal是java中为每个线程都提供一个变量值的副本,是Java中一种较为特殊的线程绑定机制。这里我们只要记住以下几点:1.每个线程中获得的ThreadLocal的值都是线程独立的,和其它线程没有关联。2.对于ThreadLocal的理解在记住第一点的基础上只需要记住public void set(T)和public T get()两个接口就可以。
        事实上android利用ThreadLocal,为每个UI线程建立一个looper对象,每个looper对象有一个消息队列MessageQueue。每次声明一个handler,其主要步骤就是绑定当前线程的消息队列,这之后就可以利用handler将需要的操作派发到消息队列中。一般在每个线程的开始执行Looper.prepare();这是为当前线程创建线程私有的looper对象,这之后就可以声明handler;一般在每个线程的最后执行Looper.loop();前面说明handler绑定了当前线程的消息队列,loop()方法就是监听消息队列,并且执行操作,因此这个方法是一个无限循环,其之后的代码都不会执行,所以在线程的最后执行。
        这种模式有点像大学操作系统中的生产者和消费者模式,looper就像消费者,handler就像生产者,比较特殊的是在一个线程中消费者looper只能有一个,生产者handler可以有多个。接下来带着上面的知识看看looper中的主要代码:

public static void prepare() {
    prepare(true);
}
 
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;
    for (;;) {
        Message msg = queue.next(); // might block
        .....
        msg.target.dispatchMessage(msg);
    }
}
       在程序中如果某一个子方法里面涉及到了handler而当前线程没有绑定looper(比如在某个自己新建的线程中新分配的一个对象就可能需要handler),运行时就会抛出异常:

        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
而如果只在线程的开头执行Looper.prepare();而没有执行Looper.loop();就会出现很多操作不会执行的现象。:)

      最后再总结下:用了很久的Handler.post(Runnable),一直没去深入看下原理。android封装的真的很好,涉及到更新UI的东东就上面说的已经够用,如果有一些特殊的需要HandlerThread,AsyncTask都已经帮我们封装好了。
--------------------- 
作者:AndrewBS 
来源:CSDN 
原文:https://blog.csdn.net/maybe_windleave/article/details/8887980 
版权声明:本文为博主原创文章,转载请附上博文链接!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值