zoukankan      html  css  js  c++  java
  • android学习-异步消息处理机制

    消息处理机制主要对象:Looper,Handler,Message(还有MessageQueue和Runnable)

    Looper不断从MessageQueue消息队列中取出一个Message,然后传给Handle,如此循环往复,如果队列为空,那么它会进入休眠。

    这些类的主要变量

    Looper.java

    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
        private static Looper sMainLooper;  // guarded by Looper.class
    
        final MessageQueue mQueue;
        final Thread mThread;

    Handler.java

    final MessageQueue mQueue;
        final Looper mLooper;
        final Callback mCallback;
        final boolean mAsynchronous;
        IMessenger mMessenger;

    Message.java

    Handler target;每个消息只能对应一个handler
    Runnable callback;回调接口 

    MessageQueue.java

    Message mMessages;

    Runnable是一个空接口类,没有变量

    上一个书上的图:

     

    Handler和Thread没有直接关系,但对应关系可以推理得到

    每个Thread只对应一个Looper;

    每个Looper只对应一个MessageQueue;

    每个MessageQueue对应N个Message,每个Message只对应一个Handler

    ==》每个Thread对应N个Handler。

    Handler是”真正处理事情“的地方,作用:处理消息,将Message压入MessageQueue中

    带着一个问题看源码:创建handler对象的线程(ui/主线程除外)为什么,必须先调用Looper.prepare() ?

     public Handler() {
            this(null, false);
        }
    public Handler(Callback callback) {
            this(callback, false);
        }
    public Handler(Looper looper) {
            this(looper, null, false);
        }
     public Handler(Looper looper, Callback callback) {
            this(looper, callback, false);
        }
     public Handler(boolean async) {
            this(null, async);
        }
    public Handler(Callback callback, boolean async) { 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()); } } 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 = callback; mAsynchronous = async; } public Handler(Looper looper, Callback callback, boolean async) { mLooper = looper; mQueue = looper.mQueue; mCallback = callback; mAsynchronous = async; }
    初始化handler对象时(构造方法是Handler(),Handler(Callback callback))都间接调用Handler(Callback callback, boolean async)构造方法
    主要代码是Looper.myLooper();
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();//这是在Looper类中的定义
    public
    static Looper myLooper() { return sThreadLocal.get();//从当前线程中获得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));//为当前线程设置looper对象
    }

    我们自己创建线程必须通过Looper.prepare()方法为当前线程设置looper对象才可以通过Looper.myLooper()方法返回looper对象,这样在非UI线程创建handler对象时才不会报错。"Can't create handler inside thread that has not called Looper.prepare()"

    ps:prepare(boolean quitAllowed)(这个不用我们关心,略过。。)

    这个quitAlowed参数是定义消息队列用了,看的源代码是android4.4

    Looper.java
    private
    Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); }
    MessageQueue.java

    // True if the message queue can be quit.
    private final boolean mQuitAllowed;//true消息队列可以被quit,false消息队列不能被quit。

    主线程/UI线程的MessageQueue不能被销毁掉。看源码(销毁调用Looper.quit())

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

    偏离太远了

    所以得出结论:创建handler对象的线程(ui/主线程除外),必须先调用Looper.prepare() 

    Handler作用1:处理消息

    在Looper类中处理消息是通过msg.target.dispatchMessage(msg);target就是handler对象(Message类的内部变量Handler target)将消息转发到处理消息的对应的handler对象上,然后这个target即handler对象会在处理消息前做一个检查

    public void dispatchMessage(Message msg) {
            if (msg.callback != null) {//如果msg有绑定callback回调接口Runaable不为空,则执行Runnable的run方法
                handleCallback(msg);
            } else {
                if (mCallback != null) {//如果handler的内置接口类Callback不为空,则执行boolean handleMessage(Message msg)这个方法
                    if (mCallback.handleMessage(msg)) {执行完成则return
                        return;
                    }
                }
                handleMessage(msg);//最后才执行handler本身的方法
            }
        }

    private static void handleCallback(Message message) {
    message.callback.run();
    }

    public interface Callback {//handler的内置接口类Callback

    public boolean handleMessage(Message msg);

    }

     

    Handler作用2:将Message压入MessageQueue中

    handler中提供的很多发送message的方法,除了sendMessageAtFrontOfQueue()方法(直接调用enqueueMessage(queue, msg, 0);)之外,其它的发送消息方法最终都会辗转调用到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);
        }

    sendMessageAtTime()方法也是调用Handler中的enqueueMessage(queue, msg, uptimeMillis)方法

    和sendMessageAtFrontOfQueue()方法两者最后都会调用enqueueMessage(queue, msg, uptimeMillis)方法

    区别是需要延迟uptimeMillis时间后才将Message压入MessageQueue中

     private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;//给msg的target赋值为handler自身然后加入MessageQueue中
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }

    最终所有的方法都是调用MessageQueue中的enqueueMessage(msg, uptimeMillis);方法,是不是感觉两个方法差不多啊,注意参数!!

    MessageQueue的使用是在Looper中

    Handler的作用整理完毕(好像我现在已经可以把Handler源码完整默写下来了。哈哈^.^记忆力真不行)

    Looper类

    作用:与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。

    对于Looper主要是prepare()和loop()两个方法

    prepare()将普通线程转化为looper线程,

    loop()方法,不断从MessageQueue中去取消息,交给消息的target属性的dispatchMessage去处理。

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

    27行就是上面提到了,handler进行消息处理的关键代码了

    看着上面的分析很复杂,总结下

    1、首先Looper.prepare()为在当前线程中保存一个Looper实例(sThreadLocal.set()),然后该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。

    2、Looper.loop()会让当前线程进入一个无限循环,不端从MessageQueue的实例中读取消息,然后回调msg.target.dispatchMessage(msg)方法。

    3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue想关联。

    4、Handler的sendMessage方法,会给msg的target赋值为handler自身,然后加入MessageQueue中。

    5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。

    好了,总结完成,大家可能还会问,那么在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢,这是因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。

    这篇博客很不错,借鉴了

    http://blog.csdn.net/lmj623565791/article/details/38476887

  • 相关阅读:
    中共中央办公厅的机构设置(局、室)
    清理winsxs文件夹(系统更新文件)的第三方工具
    通用的MIME类型:application/octet-stream
    “IIS7.5无法写入配置文件web.config”的解决方案
    刷新组策略的命令
    windows网络和共享中心“查看基本网络信息并设置连接”为“未知”的解决方案
    使 windows 无需输入开机密码自动进入系统
    windows server 2008 R2 的 FTP 防火墙的正确配置方法
    搜狗浏览器不能使用拖拽搜索的解决方案
    无法启动 Diagnostic Policy Service(服务错误 1079)的解决方案
  • 原文地址:https://www.cnblogs.com/gne-hwz/p/6737822.html
Copyright © 2011-2022 走看看