美文网首页
Android Handler原理源码浅析

Android Handler原理源码浅析

作者: KIDNG_LGJ | 来源:发表于2018-08-21 18:00 被阅读0次

    开发Android一般都用遇到使用handler的情况,现在因为rxjava的时候可能就减少了handler的使用。
    使用handler需要注意内存泄漏问题(可以通过弱引用Context解决,或者在不需要使用后调用Handler.removeCallbacksAndMessages(null)),当然rxjava也是会出现该种情况(RxLifecycle和AutoDispose都是为了不打断rxjva链式调用设计的生命周期监听)
    说到handler总是离不开looper、message、messageQueue的。但是使用Handler的时候也总是离不开Thread之间的使用。

    Thread

    构建Thread最终会调起init方法。在init方法中仅仅是对thread一些成员变量的初始化而已,并没有任何线程创建的过程。

        private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
            Thread parent = currentThread();
            if (g == null) {
                g = parent.getThreadGroup();
            }
            //告诉ThreadGroup中就绪线程+1,但没调用g.add所以没添加进ThreadGroup中
            g.addUnstarted();
            this.group = g;
    
            this.target = target;
            this.priority = parent.getPriority();
            this.daemon = parent.isDaemon();
            setName(name);
    
            init2(parent);
    
            /* Stash the specified stack size in case the VM cares */
            this.stackSize = stackSize;
            tid = nextThreadID();
        }
    
        private void init2(Thread parent) {
            this.contextClassLoader = parent.getContextClassLoader();
            this.inheritedAccessControlContext = AccessController.getContext();
            if (parent.inheritableThreadLocals != null) {
                this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
                        parent.inheritableThreadLocals);
            }
        }
    

    线程的使用最后总离不开start()方法。

        public synchronized void start() {
            // threadStatus==0表示线程未start过
            if (threadStatus != 0 || started)
                throw new IllegalThreadStateException();
            //添加线程进ThreadGroup中,并通知start,减少就绪线程计数器
            group.add(this);
    
            started = false;
            try {
                //真正创建线程的地方
                nativeCreate(this, stackSize, daemon);
                started = true;
            } finally {
                try {
                    if (!started)  group.threadStartFailed(this);
                } catch (Throwable ignore) {
                }
            }
        }
    

    在native方法中去创建线程,最终通过linux层的pthread方式创建线程并调用Thread中的run的方法指针。这时候的run方法才真正在的子线程中运行。

    Looper

    使用looper时很简单,短短两句话就可以实现创建一个当前线程的Looper

    Looper.prepare();
    Looper.loop();
    

    调用prepare方法时,当前线程中只能有一个looper的存在。当多个时会导致RuntimeException。代码中的ThreadLocal是什么呢?最后会稍微说下的。

     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));
        }
    

    Handler的构造方法有几个重构方法。当传入Looper时,handler使用传入的looper去获取对应message。不传入Looper时使用当前线程Looper(Looper.myLooper())。而在UI线程中早已创建MainLooper(在ActivityThread中的main方法,涉及到activity创建流程,这里不详述了),所以可以直接通过空构建方法使用Handler(这时的Looper.myLooper()==Looper.getMainLooper())

    public static void main(String[] args) {
            Looper.prepareMainLooper();
            ActivityThread thread = new ActivityThread();
            thread.attach(false);
            if (sMainThreadHandler == null) {
                sMainThreadHandler = thread.getHandler();
            }
            Looper.loop();
        }
    

    OK,说完Looper.prepare()。这时说下Looper.loop(),该方法主要是开启轮询去获取MessageQueue中的Message

    public static void loop() {
            final Looper me = myLooper();
            //略。。。。
            final MessageQueue queue = me.mQueue;
            //略。。。。
            for (;;) {
                Message msg = queue.next(); // might block(无消息则阻塞)
                if (msg == null) {
                    // No message indicates that the message queue is quitting.
                    return;
                }
                //略。。。。
                try {
                    //通过Message.target(Handler)分发处理回调
                    msg.target.dispatchMessage(msg);
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
                //略。。。。
                msg.recycleUnchecked();
            }
        }
    

    上述代码可以看出,Looper主要是通过MessageQueue.next()去获取对应Message。获取到Message后,通过Handler.dispatchMessage方法下发处理Message。最后通过msg.recycleUnchecked回收Message,供Message.obtain复用。
    因为Looper.loop方法的循环等待,所以Looper.loop方法是会阻塞当前线程的。所以handler的创建应该房间prepare和loop方法之间。在ActivityThread中也能看到调用Looper.loop()之前有一段代码sMainThreadHandler = thread.getHandler()。其实在主线程中activity的生命周期处理及其他相关操作都是通过内部的H.class的Handler通讯实现的。

    MessageQueue

    大概逻辑就是当有新的Message插入队列时会唤醒队列,若同步消息处理时间未到则再休眠指定时候后唤醒。
    主要看下next方法是怎么获取对应message的。

    Message next() {
            //检查loop是否已经为退出状态。mPrt是Native层的MessageQueue的地址。
            final long ptr = mPtr;
            if (ptr == 0) {
                return null;
            }
    
            int pendingIdleHandlerCount = -1; // -1 only during first iteration
            int nextPollTimeoutMillis = 0;
            for (;;) {
                if (nextPollTimeoutMillis != 0) {
                     //如果不是第一次获取消息,调用Native的函数,让虚拟机刷新所有的IBinder命令,确保进程在执行可能阻塞的任务之前,释放之前的对象。
                    Binder.flushPendingCommands();
                }
                //休眠指定时间
                nativePollOnce(ptr, nextPollTimeoutMillis);
    
                synchronized (this) {
                    // Try to retrieve the next message.  Return if found.
                    final long now = SystemClock.uptimeMillis();
                    Message prevMsg = null;
                    Message msg = mMessages;
                    if (msg != null && msg.target == null) {//遇到消息屏障时
                        // Stalled by a barrier.  Find the next asynchronous message in the queue.
                        //忽略同步消息
                        do {
                            prevMsg = msg;
                            msg = msg.next;
                        } while (msg != null && !msg.isAsynchronous());
                    }
                    if (msg != null) {
                        if (now < msg.when) {
                            // Next message is not ready.  Set a timeout to wake up when it is ready.
                            //未到时间,继续休眠
                            nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                        } else {
                            // Got a message.
                            mBlocked = false;
                            if (prevMsg != null) {
                                prevMsg.next = msg.next;
                            } else {
                                mMessages = msg.next;
                            }
                            msg.next = null;
                            if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                            //标记使用并返回
                            msg.markInUse();
                            return msg;
                        }
                    } else {
                        // No more messages.
                        nextPollTimeoutMillis = -1;
                    }
    
                    // Process the quit message now that all pending messages have been handled.
                    //队列已退出返回空,looper也相应结束
                    if (mQuitting) {
                        dispose();
                        return null;
                    }
                    //略。。。。。。
                }
                //略。。。。。。
    
                // Reset the idle handler count to 0 so we do not run them again.
                pendingIdleHandlerCount = 0;
    
                // While calling an idle handler, a new message could have been delivered
                // so go back and look again for a pending message without waiting.
                nextPollTimeoutMillis = 0;
            }
        }
    

    上面看到,当msg.target为空(即消息屏障)是会忽略同步消息。那在系统中什么时候会创建一个taget为空的消息呢?ViewRootImpl.scheduleTraversals()方法,即是在绘图之前会插入一个消息屏障,绘制之后移除。

        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                //添加消息屏障
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
                pokeDrawLockIfNeeded();
            }
        }
    

    Message

    平时我们一般发送的Message一般为同步Message,可以通过Messasge.setAsynchronous(true)设为异步消息。

    Handler.obtain和Message.obtain就简单了,主要是Message内部维持着一个Message链表,获取时先在链表中获取对应缓存Message。Message使用完后,在Looper中通过Message.recycleUnchecked()回收

    Handler

    Looper的创建需要通过Looper.prepare()来调用,但是每个线程只能有一个Looper。那是不是就意味着每次创建子线程Handler都需要new Thread在Thread内使用Looper的prepare和loop后再创建Handler呢?Android其实还有HandlerThread可以了解一下(不需要子线程处理后,要自行调用quit方法释放资源哟~)。但是要记住的是Looper.loop是会阻塞当前进程的。
    剩下Handler就只有post、sendMessage、handleMessage需要说了
    post和sendMessage可以合并一块说

    public final boolean post(Runnable r){
           return  sendMessageDelayed(getPostMessage(r), 0);
    }
    //最终起调方法
        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
            msg.target = this;
            if (mAsynchronous) {
                msg.setAsynchronous(true);
            }
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    是不是很惊喜很意外,其实最终还是当成了Message处理。通过post方法传入的Runnable最后转为了Message.callback成员。所以Handler无论sendEmptyMessage还是post,最后还是回落到Message.obtain并对Message初始化发送的流程。
    但是前面MessageQueue在next方法中已经休眠了,所以在MessageQueue.enqueueMessage会根据添加Message判断是否需要立刻唤醒队列。

        boolean enqueueMessage(Message msg, long when) {
           //省略判断代码,message无依附Handler或者还在使用中直接抛出异常
            synchronized (this) {
                if (mQuitting) {
                    //队列已结束,返回
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    //队列为空,唤醒
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                    // 插在中间无需唤醒,插入队列头或者是异步消息则需要唤醒
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
    
                // We can assume mPtr != 0 because mQuitting is false.
                if (needWake) {
                    nativeWake(mPtr);//唤醒队列
                }
            }
            return true;
        }
    

    剩下Handler.dispatchMessage(Message msg)需要说明下Handler处理消息流程而已了。在Looper中可以看到dispatchMessage的起调是在Looper.loop方法里。Looper在获取到需要处理的Message之后,调用Message.target.dispatchMessage处起调的。

    public void dispatchMessage(Message msg) {
            if (msg.callback != null) {
                handleCallback(msg);
            } else {
                if (mCallback != null) {
                    if (mCallback.handleMessage(msg)) {
                        return;
                    }
                }
                handleMessage(msg);
            }
        }
    

    代码简单易懂。

    • Message有callback,回调message.callback(所以post方法Runable会优先执行)
    • Handler有callback,回调Handler.callback
    • 最后回落handleMessage

    最后在稍微说下Looper中说到的ThreadLocal

    ThreadLocal

    最后稍微介绍下ThreadLocal。这个类就是保证每个线程只有一个Looper的关键。
    这个类主要作用就是线程内数据共享,不同线程为不同副本。

    class Looper
    
     public static @Nullable Looper myLooper() {
            return sThreadLocal.get();
     }
    
     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));
     }
    

    ThreadLocal.get方法主要是通过当前线程的ThreadLocalMap去获取当前ThreadLocal的值。

    class ThreadLocal
    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();
        }
    

    ThreadLocalMap是ThreadLocal定义的一个内部类,以ThreadLocal为key值。

    • 每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals(存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量))。
    • 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
    • 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

    所以在不同线程中调用ThreadLocal.set()实际上调用的是当前线程中的ThreadLocalMap,从而保证线程安全
    而Looper只有通过静态的Looper.prepare()方法去创建Looper,从而保证每个线程只有一个Looper

    参考资料

    相关文章

      网友评论

          本文标题:Android Handler原理源码浅析

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