Android Handler原理源码浅析

开发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

参考资料

  • 你真应该再多了解些Handler机制
  • 深入理解MessageQueue
  • Linux中pthread线程使用详解
  • 源码//滑稽

你可能感兴趣的:(Android Handler原理源码浅析)