美文网首页
Handler机制(Android消息机制)

Handler机制(Android消息机制)

作者: Zcurry | 来源:发表于2018-12-27 17:58 被阅读0次

    自我理解链接

    handler机制大致流程图.png
    1. 首先有一个Looper对象已经存在,并处于轮询的状态;
    相对应的代码,Looper.prepare()-->Looper.loop()
    
    2. Handler发送Message,加入到MQ中
    各种发送消息的方法-->equeueMessage()-->MessageQueue.equeueMessage()
    
    3. 轮询取出MQ的队头Message,通过Handler进行回调
    MessageQueue.next()-->(handler)msg.target.dispatchMessage()-->handleMessage()[自定义TODO]
    

    Message 【继承Parcelable】

    封装了任务携带的参数和处理该任务的Handler

    /**
     * User-defined message code so that the recipient can identify
     * what this message is about. Each {@link Handler} has its own name-space
     * for message codes, so you do not need to worry about yours conflicting
     * with other handlers.
     */
    public int what; //标识位,用来区别消息
    
    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg1; //用来存储一些整数值,替代setData()存储
    
    /**
     * arg1 and arg2 are lower-cost alternatives to using
     * {@link #setData(Bundle) setData()} if you only need to store a
     * few integer values.
     */
    public int arg2; //用来存储一些整数值,替代setData()存储
    
    /**
     * An arbitrary object to send to the recipient.  When using
     * {@link Messenger} to send the message across processes this can only
     * be non-null if it contains a Parcelable of a framework class (not one
     * implemented by the application).   For other data transfer use
     * {@link #setData}.
     *
     * <p>Note that Parcelable objects here are not supported prior to
     * the {@link android.os.Build.VERSION_CODES#FROYO} release.
     */
    //发送给收件人的任意对象。当使用{@link Messenger}跨进程发送消息时,
    //如果它包含框架类的Parcelable(不是应用程序实现的框架类),则它只能是非null。
    //对于其他数据传输,请使用{@link #setData}
    //2.2版本之前不支持实现Parceable的实体类
    public Object obj;
    
    • Message的创建(2种方法)

      1. 直接实例化一个Message,然后设置其参数
        Message msg = new Message;
        msg.what = 0;
        msg.arg0 = 1; 
        ...
        
      2. Message.obtain() --【推荐】
        在许多情况下可以避免分配新的对象;避免重复创建Message
    • Message注意点:

      1. 尽量通过Message.obtain()的方式构建Message对象,防止Message的多次创建;
      2. 仅有int型参数时 最好使用arg1和arg2,减少内存的使用;

    Looper:消息通道(使普通线程变成Looper线程)

    在Activity中,系统会自动启动Looper对象;而在自定义类中,需要自己手动调用;
    下面是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.prepare()系统源码:

    /** 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()}.
      */
    "//将当前线程初始化为looper;确保在调用prepare方法之后,调用loop方法;然后结束时调用quit方法;"
    public static void prepare() {
        prepare(true);
    }
    
    '//一个Thread只能有一个Looper对象'
    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));
    }
    //初始化时会创建一个MessageQueue对象和线程
    private Looper(boolean quitAllowed) {
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
    }
    

    关于Looper.loop()系统源码:

    /**
     * Run the message queue in this thread. Be sure to call
     * {@link #quit()} to end the loop.
     */
    //在此线程中运行消息队列,确保在结束loop后调用quit
    public static void loop() {
        //获取当前looper线程
        final Looper me = myLooper();
        //判断是否调用了prepare
        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(;;)和while(true)  for (;;)指令少,不占用寄存器,而且没有判断跳转'
        for (;;) {
            // 获取消息队列中的message
            Message msg = queue.next(); // might block 
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ... log日志[省略]...
            try {
                '//交给Message对应的Handler处理消息'
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
             ... [省略]...
            msg.recycleUnchecked();
        }
    }
    

    总结:
    1)一个Thread只能有一个Looper对象;
    2)Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行;
    3)Looper使一个线程变成Looper线程。

    MessageQueue:消息队列

    根据源码中的注释翻译如下:
    保存由{@link Looper}分派的消息列表的低级类。消息不会直接添加到MessageQueue,而是通过与Looper关联的{@link Handler}对象添加。
    您可以使用{@link Looper#myQueue()Looper.myQueue()}检索当前线程的MessageQueue。

    /**
     Low-level class holding the list of messages to be dispatched by a
     {@link Looper}.  Messages are not added directly to a MessageQueue,
     but rather through {@link Handler} objects associated with the Looper.
    
     <p>You can retrieve the MessageQueue for the current thread with
     {@link Looper#myQueue() Looper.myQueue()}.
     */
    public final class MessageQueue {
        ...
        //获取下一个message方法
        Message next(){
            ...
        }
        //message添加到消息队列的方法
        boolean enqueueMessage(Message msg, long when) {
            ...
        }
    }
    

    Handler:消息操作类【重点】

    官方注释(抽象的翻译):
    Handler允许您发送和处理与线程{@link MessageQueue}关联的{@link Message}和Runnable对象。
    每个Handler实例都与一个线程和该线程的消息队列相关联。
    当您创建一个新的Handler时,它被绑定到正在创建它的线程的线程/消息队列 - 从那时起,它将消息和runnables传递给该消息队列并在消息出来时执行它们队列。

    Handler有两个主要用途:
    (1)将消息和runnable安排在将来的某个点上执行;
    (2)将要在不同于自己的线程上执行的动作排入队列。

    源码分析:
    /**
      *默认的构造方法会 关联一个looper
      */
    public Handler(Callback callback, boolean async) {
        ...
        //关联looper,不能为null
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //直接获取looper的消息队列,
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
    handler发送消息:

    可传递参数包括Message和Runnable,但即使传递Runnable对象,最终也被处理为Message对象,然后执行sendMessageAtTime()方法

    /**
     * 在所有待处理消息之后将消息排入消息队列。
     *  如果消息成功的加入到MQ中,就返回true
     */
    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);
    }
    
    handler消息的处理:

    dispatchMessage(msg);此方法在Looper.loop()中调用msg.target.dispatchMessage();这里的msg.traget就是指向当前的handler;
    然后Handler中调用handleMessage(msg)方法,来触发我们需要实现的具体逻辑。

    延伸:

    1. Handler内部如何获取到当前线程的Looper?

    答:是通过 ThreadLocal

    Handler内部.png myLooper方法.png
    2. 系统为什么不允许子线程中访问UI?

    答:Android中的UI控件不是线程安全的,如果在多线程中并发的访问,UI显示状态不可预计。

    3. 为何系统不对UI的访问加上锁机制?

    答:上锁会让UI访问的逻辑变得复杂,会降低UI访问的效率,也会阻塞某些线程的执行,体现在界面上会显得卡顿运行不畅。

    相关文章

      网友评论

          本文标题:Handler机制(Android消息机制)

          本文链接:https://www.haomeiwen.com/subject/jkjdlqtx.html