美文网首页
Android Handler机制

Android Handler机制

作者: Bfmall | 来源:发表于2023-02-20 17:26 被阅读0次

    前言

    在android开发中,经常会在子线程中进行一些操作,当操作完毕后会通过handler发送一些数据给主线程,通知主线程做相应的操作。 探索其背后的原理:子线程 handler 主线程 其实构成了线程模型中的经典问题 生产者-消费者模型。 生产者-消费者模型:生产者和消费者在同一时间段内共用同一个存储空间,生产者往存储空间中添加数据,消费者从存储空间中取走数据。

    image.png

    好处: - 保证数据生产消费的顺序(通过MessageQueue,先进先出) - 不管是生产者(子线程)还是消费者(主线程)都只依赖缓冲区(handler),生产者消费者之间不会相互持有,使他们之间没有任何耦合。

    Handler机制相关类:

    Hanlder:发送和接收消息。
    Looper:用于轮询消息队列,一个线程只能有一个Looper。
    Message: 消息实体。
    MessageQueue: 消息队列用于存储消息和管理消息。

    一、创建Looper

    文件位置:ActivityThread.java,在main方法中已经调用prepare了。

    public static void main(String[] args) {
            //其他代码省略.....
            //创建主线程Looper
            Looper.prepareMainLooper();
    
            // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
            // It will be in the format "seq=114"
            long startSeq = 0;
            if (args != null) {
                for (int i = args.length - 1; i >= 0; --i) {
                    if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                        startSeq = Long.parseLong(
                                args[i].substring(PROC_START_SEQ_IDENT.length()));
                    }
                }
            }
            ActivityThread thread = new ActivityThread();
            thread.attach(false, startSeq);
    
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
    
            //省略代码...
    
            //开始轮循操作
            Looper.loop();
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }
    

    调用Looper的prepareMainLooper方法:

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

    prepare有两个重载的方法,主要看 prepare(boolean quitAllowed) quitAllowed的作用是在创建MessageQueue时标识消息队列是否可以销毁, 主线程不可被销毁 下面有介绍

    //静态不可变sThreadLocal 对象,每个Looper中只能有一个sThreadLocal对象
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
    public static void prepare() {
            //消息队列可以quit
            prepare(true);
        }
    
    //quitAllowed是否允许退出Looper循环
    private static void prepare(boolean quitAllowed) {
            //不为空表示当前线程已经创建了Looper,表明每个线程只能创建一个Looper
            if (sThreadLocal.get() != null) {
                throw new RuntimeException("Only one Looper may be created per thread");
            }
            //创建Looper对象,并保存在ThreadLocal中,这样get的时候就不会为null了
            sThreadLocal.set(new Looper(quitAllowed));
        }
    

    Looper构造方法:创建MessageQueue以及Looper与当前线程的绑定

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

    MessageQueue的构造方法

    MessageQueue(boolean quitAllowed) {
            //mQuitAllowed决定队列是否可以销毁,主线程的队列不可以被销毁需要传入false
            //在MessageQueue的quit()方法就不贴源码了
            mQuitAllowed = quitAllowed;
            mPtr = nativeInit();
        }
    

    Looper.loop()
    同时是在main方法中 Looper.prepareMainLooper() 后Looper.loop(); 开始轮询

    public static void loop() {
            //里面调用了sThreadLocal.get()获得刚才创建的Looper对象
            final Looper me = myLooper();
            //如果Looper为空则会抛出异常
            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
               //由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage enqueueMessage后
                //队列里才有消息
                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);
                }
    
                //msg.target就是绑定的Handler,详见后面Message的部分,Handler开始
                msg.target.dispatchMessage(msg);
                //后面代码省略.....
    
                msg.recycleUnchecked();
            }
        }
    

    看下ThreadLocal类,内部封装了Map集合,map中保存的<key,value>对应的就是<this, looper对象>

    public T get() {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null) {
                ThreadLocalMap.Entry e = map.getEntry(this);
                if (e != null) {
                    @SuppressWarnings("unchecked")
                    T result = (T)e.value;
                    return result;
                }
            }
            return setInitialValue();
        }
    
        /**
         * Variant of set() to establish initialValue. Used instead
         * of set() in case user has overridden the set() method.
         *
         * @return the initial value
         */
        private T setInitialValue() {
            T value = initialValue();
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                //将<当前ThreadLocal对象,Looper对象>键值对保存到map中
                map.set(this, value);
            else
                createMap(t, value);
            return value;
        }
    
        /**
         * Sets the current thread's copy of this thread-local variable
         * to the specified value.  Most subclasses will have no need to
         * override this method, relying solely on the {@link #initialValue}
         * method to set the values of thread-locals.
         *
         * @param value the value to be stored in the current thread's copy of
         *        this thread-local.
         */
        public void set(T value) {
            Thread t = Thread.currentThread();
            ThreadLocalMap map = getMap(t);
            if (map != null)
                //将<当前ThreadLocal对象,Looper对象>键值对保存到map中
                map.set(this, value);
            else
                createMap(t, value);
        }
    
        void createMap(Thread t, T firstValue) {
            t.threadLocals = new ThreadLocalMap(this, firstValue);
        }
    

    以上可以简单总结一下对应关系:
    主线程ActivityThead <--> ThreadLocal <--> Looper <--> MessageQueue为一对一关系

    二、创建Handler

    最常见的创建handler

    Handler handler=new Handler(){
         @Override
          public void handleMessage(Message msg) {
               super.handleMessage(msg);
          }
    };
    

    在内部调用 this(null, false);

    public Handler(Callback callback, boolean async) {
            //前面省略
            mLooper = Looper.myLooper();//获取Looper,**注意不是创建Looper**!
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
            mQueue = mLooper.mQueue;//创建消息队列MessageQueue
            mCallback = callback; //初始化了回调接口
            mAsynchronous = async;
        }
    

    Looper.myLooper()方法:

     //这是Handler中定义的ThreadLocal  ThreadLocal主要解多线程并发的问题
     //sThreadLocal.get() will return null unless you've called prepare().
     static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    
     public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
        }
    

    sThreadLocal.get() will return null unless you’ve called prepare(). 这句话告诉我们get可能返回null 除非先调用prepare()方法创建Looper。

    三、创建Message

    可以直接new Message 但是有更好的方式 Message.obtain。因为可以检查是否有可以复用的Message,用过复用避免过多的创建、销毁Message对象达到优化内存和性能的目地

    public static Message obtain(Handler h) {
            Message m = obtain();//调用重载的obtain方法
            m.target = h;//并绑定的创建Message对象的handler
    
            return m;
        }
    
    public static Message obtain() {
            synchronized (sPoolSync) {//sPoolSync是一个Object对象,用来同步保证线程安全
                if (sPool != null) {//sPool是就是handler dispatchMessage 后 通过recycleUnchecked 回收用以复用的Message 
                    Message m = sPool;
                    sPool = m.next;
                    m.next = null;
                    m.flags = 0; // clear in-use flag
                    sPoolSize--;
                    return m;
                }
            }
            return new Message();
        }
    

    Handler的enqueueMessage方法:Message和Handler的绑定
    创建Message的时候可以通过 Message.obtain(Handler h) 这个构造方法绑定。当然可以在 在Handler 中的 enqueueMessage()也绑定了,所有发送Message的方法都会调用此方法入队,所以在创建Message的时候是可以不绑定的

      private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this; //绑定
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    Handler发送消息
    Handler发送消息的重载方法很多,但是主要只有2种。 sendMessage(Message) sendMessage方法通过一系列重载方法的调用,sendMessage调用sendMessageDelayed,继续调用sendMessageAtTime,继续调用enqueueMessage,继续调用messageQueue的enqueueMessage方法,将消息保存在了消息队列中,而最终由Looper取出,交给Handler的dispatchMessage进行处理

    我们可以看到在dispatchMessage方法中,message中callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程通直接通过无参的构造方法new出来的为null,所以会直接执行后面的handleMessage()方法。
    Handler的dispatchMessage方法:

    public void dispatchMessage(Message msg) {
        //callback在message的构造方法中初始化或者使用handler.post(Runnable)时候才不为空
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            //mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,该属性为null,此段不执行
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);//最终执行handleMessage方法
        }
    }
    
     private static void handleCallback(Message message) {
            message.callback.run();
        }
    

    Handler处理消息
    在handleMessage(Message)方法中,我们可以拿到message对象,根据不同的需求进行处理,整个Handler机制的流程就结束了。

    小结
    handler.sendMessage 发送消息到消息队列MessageQueue,然后looper调用自己的loop()函数带动MessageQueue从而轮询messageQueue里面的每个Message,当Message达到了可以执行的时间的时候开始执行,执行后就会调用message绑定的Handler来处理消息。大致的过程如下图所示


    image.png

    handler机制就是一个传送带的运转机制。
    1)MessageQueue就像履带。
    2)Thread就像背后的动力,就是我们通信都是基于线程而来的。
    3)传送带的滚动需要一个开关给电机通电,那么就相当于我们的loop函数,而这个loop里面的for循环就会带着不断的滚动,去轮询messageQueue
    4)Message就是 我们的货物了。

    作者:我爱田Hebe
    链接:https://www.jianshu.com/p/ced11afb66f7
    来源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

    相关文章

      网友评论

          本文标题:Android Handler机制

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