zoukankan      html  css  js  c++  java
  • Android进阶-消息机制

    Handler的主要作用是将一个任务切换到某个指定的线程去执行。

    Android的消息机制主要涉及三个类:Handler, Looper, MessageQueue:

    现在假设一个情景:

    有两个线程,线程1和线程2,在线程1中调用Looper.prepare(), 创建一个Handler对象handler,调用Looper.loop()。

    在线程2中调用handler.sendMessage()发送消息,那最终这个消息会在线程1中被handler.handleMassage()处理。我们画出内存模型帮助理解:

    从根据这个调用顺序来看看这个模型:

    1.Looper在线程1中调用prepare()方法:

    1 private static void prepare(boolean quitAllowed) {
    2         if (sThreadLocal.get() != null) {
    3             throw new RuntimeException("Only one Looper may be created per thread");
    4         }
    5         sThreadLocal.set(new Looper(quitAllowed));
    6     }

    这里的sTreadLocal是Looper的static对象:

    1     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    ThreadLocal对象在不同的线程中会指向不同的Looper对象(java编程思想中有介绍)或者说在不同线程调用sThreadLocal.get()返回的是个该线程关联的Looper对象,使用get(), set()访问。prepare()为线程1创建一个Looper对象,来看看构造方法:

    1 private Looper(boolean quitAllowed) {
    2         mQueue = new MessageQueue(quitAllowed);
    3         mThread = Thread.currentThread();
    4     }

    Looper内部封装了一个MessageQueue对象。

    2.在线程1中创建handler对象:

     1     public Handler(Callback callback, boolean async) {
     2         if (FIND_POTENTIAL_LEAKS) {
     3             final Class<? extends Handler> klass = getClass();
     4             if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
     5                     (klass.getModifiers() & Modifier.STATIC) == 0) {
     6                 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
     7                     klass.getCanonicalName());
     8             }
     9         }
    10 
    11         mLooper = Looper.myLooper();
    12         if (mLooper == null) {
    13             throw new RuntimeException(
    14                 "Can't create handler inside thread that has not called Looper.prepare()");
    15         }
    16         mQueue = mLooper.mQueue;
    17         mCallback = callback;
    18         mAsynchronous = async;
    19     }

    从第11行看出Handler中封装了线程1的Looper对象,和消息队列,也就是说handler对象可以在线程2中访问线程1的消息队列,从而向里面添加新消息(后面将看到),我们从内存模型也可以理解这点。但是这个Handler可以被任意线程访问,这就是关键所在。

    3.在线程1中调用Looper.loop():

     1 /**
     2      * Run the message queue in this thread. Be sure to call
     3      * {@link #quit()} to end the loop.
     4      */
     5     public static void loop() {
     6         final Looper me = myLooper();
     7         if (me == null) {
     8             throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
     9         }
    10         final MessageQueue queue = me.mQueue;
    11 
    12         // Make sure the identity of this thread is that of the local process,
    13         // and keep track of what that identity token actually is.
    14         Binder.clearCallingIdentity();
    15         final long ident = Binder.clearCallingIdentity();
    16 
    17         for (;;) {
    18             Message msg = queue.next(); // might block
    19             if (msg == null) {
    20                 // No message indicates that the message queue is quitting.
    21                 return;
    22             }
    23 
    24             // This must be in a local variable, in case a UI event sets the logger
    25             Printer logging = me.mLogging;
    26             if (logging != null) {
    27                 logging.println(">>>>> Dispatching to " + msg.target + " " +
    28                         msg.callback + ": " + msg.what);
    29             }
    30 
    31             msg.target.dispatchMessage(msg);
    32 
    33             if (logging != null) {
    34                 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
    35             }
    36 
    37             // Make sure that during the course of dispatching the
    38             // identity of the thread wasn't corrupted.
    39             final long newIdent = Binder.clearCallingIdentity();
    40             if (ident != newIdent) {
    41                 Log.wtf(TAG, "Thread identity changed from 0x"
    42                         + Long.toHexString(ident) + " to 0x"
    43                         + Long.toHexString(newIdent) + " while dispatching to "
    44                         + msg.target.getClass().getName() + " "
    45                         + msg.callback + " what=" + msg.what);
    46             }
    47 
    48             msg.recycleUnchecked();
    49         }
    50     }

    loop()获取当前线程的Looper对象,开启无限循环调用MessageQueue.next不断获取Message,msg.target.dispatchMessage(msg);msg.target就是handler对象(下面将看到),我们看看该方法:

     1     public void dispatchMessage(Message msg) {
     2         if (msg.callback != null) {
     3             handleCallback(msg);
     4         } else {
     5             if (mCallback != null) {
     6                 if (mCallback.handleMessage(msg)) {
     7                     return;
     8                 }
     9             }
    10             handleMessage(msg);
    11         }
    12     }

    第10行调用handlerMessage()方法,一定是在线程1中执行。

    4.在线程2中调用handler.sendMessage():

    1 public final boolean sendMessage(Message msg)
    2     {
    3         return sendMessageDelayed(msg, 0);
    4     }

    该方法最终调用:

     1     public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
     2         MessageQueue queue = mQueue;
     3         if (queue == null) {
     4             RuntimeException e = new RuntimeException(
     5                     this + " sendMessageAtTime() called with no mQueue");
     6             Log.w("Looper", e.getMessage(), e);
     7             return false;
     8         }
     9         return enqueueMessage(queue, msg, uptimeMillis);
    10     }

    第9行方法:

    1     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    2         msg.target = this;
    3         if (mAsynchronous) {
    4             msg.setAsynchronous(true);
    5         }
    6         return queue.enqueueMessage(msg, uptimeMillis);
    7     }

    第2行将handler对象保存到msg.target;

    第6行,因为handler有线程1MessageQueue对象的引用,这里向该消息队列增加消息。

    5.当线程切换到线程1时,loop循环不断获取message对象,调用msg.target.dispatchMessage(msg);在线程1中处理该消息。这样就实现了,在线程2中调用sendMessage(),在线程1中处理该消息的效果了,是不是很巧妙。

  • 相关阅读:
    关于SEL数据类型的简单知识点
    小结RunLoop
    iOS-静态库的创建与使用
    MRC 下block 小结
    Native与H5交互的一些解决方法
    iOS UIPickerView 显示全国省市
    iOS开发 首次启动显示用户引导,第二次启动直接进入App,UIScrollView,UIPageControl,NSUserDefaults
    去掉tableView的header view的粘黏性
    黑苹果-IOS学习的开始
    IOS中程序如何进行推送消息(本地推送,远程推送)
  • 原文地址:https://www.cnblogs.com/gatsbydhn/p/5337499.html
Copyright © 2011-2022 走看看