您的当前位置:首页正文

观Android Handler源码有感

2024-11-10 来源:个人技术集锦

观Android Handler源码有感

闲来无事,所以一观Android Handler源码

构建Handler

首先我们要构建一个Handler,在这里我们就使用最简单的 new Handler()来构建。

Handler 内部构建流程

我们调用了new Handler()以后。handler会调用下面的代码

    public Handler() {
        this(null, false);
    }
    public Handler(Callback callback, boolean async) {
		 //...省略部分无关代码
		mLooper = Looper.myLooper();	//构建Looper
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue; //从Looper中取出消息队列
        mCallback = callback;
        mAsynchronous = async;
    }

观察上面代码。我们可以看到,其中最核心的代码就是构建Looper的代码。而我们在子线程中构建Handler报错
“Can’t create handler inside thread that has not called Looper.prepare()”);
其中出问题的地方我们观察上面代码就是“ Looper.myLooper();” ,所以我们接下来仔细观察一下Looper的构建。

    // sThreadLocal.get() will return null unless you've called prepare().
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

    /**
     * Return the Looper object associated with the current thread.  Returns
     * null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

从上面我们可以看到" Looper.myLooper();" ,最终是从sThreadLocal中取出了一个Looper对象,而sThreadLocal是一个ThreadLocal对象。如果不了解ThreadLocal的话,可以看下这篇文章

简单来说,我们可以把它理解为一个只有当前线程能访问的变量池。那么既然是变量池,而我们上面的方法是取值,所以我们就必须找到存值的地方。
而我们找遍Looper类,也只有一个地方调用了存值的方法

    public static void prepare() {
        prepare(true);
    }

    private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) { //保证每个线程只有一个Looper
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

看到这里你是不是有点明白了,为啥每次在子线程中创建Handler报错 “Can’t create handler inside thread that has not called Looper.prepare()”); ,我们只需要在Handler创建前调用一下Looper.prepare()就可以解决这个错误了。

那为啥我们在子线程中新建Handler必须要手动调用Looper.prepare(),而在主线程中,则不需要调用了。聪明的你一定想到了,在主线程中一定有一位“雷锋”帮我们做了这件事。那么接下来我们就是找出这位“雷锋”。

其中上面这两个prepare方法在系统多处都有调用,但是我们找到一个最像帮我们在activity干活的方法。

    /**
     * Initialize the current thread as a looper, marking it as an
     * application's main looper. The main looper for your application
     * is created by the Android environment, so you should never need
     * to call this function yourself.  See also: {@link #prepare()}
     */
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

在这里,我特意把方法的说明也贴上来了。最后一句话,意思就是,我们永远不需要自己手动去调用它。那么接下来,我们看下程序哪里自动的调用了它。
我们使用一下As的自动跳转功能,发现一共有两个地方调用了,一个是SystemServer,一个是ActivityThread。根据名字,我们应该找的是ActivityThread。

 public static void main(String[] args) {
		//...省略前面无关代码
        Looper.prepareMainLooper();

        ActivityThread thread = new ActivityThread();
        //在attach方法中会完成Application对象的初始化,然后调用Application的onCreate()方法
        thread.attach(false);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

        throw new RuntimeException("Main thread loop unexpectedly exited");
    }

而ActivityThread的main方法是整个APP的入口,没想到我们一找就找到了传世纪的源头了。从这里我们就可以理解原来我们的App在创建的时候就已经 调用了Looper.myLooper()了。

消息入列

入口(Activity)

MainActivity

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.i(TAG, "handleMessage: ");
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler.sendEmptyMessage(0);
    }

}

首先我们要在activity中新建一个Handler,以便我们好找点一个切入点。这次我们的切入点就选择了较为简单的

 handler.sendEmptyMessage(0);

同时,我们也在上面调用了handleMessage方法来接受我们的信息

初探幽径(Handler 源码)

    public final boolean sendEmptyMessage(int what)
    {	
    	//调用sendEmptyMessageDelayed方法,延迟0秒
        return sendEmptyMessageDelayed(what, 0);
    }
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain(); //从Message池中取出一个Message,避免构建多个Message
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);  //调用sendMessageDelayed
    }
    public final boolean sendMessageDelayed(Message msg, long delayMillis)
    {
        if (delayMillis < 0) {
            delayMillis = 0; //防止延迟时间小于0秒
        }
       //将方法转换成sendMessageAtTime
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis); 
    }
  public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue; //取出Handler中的消息队列
        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);
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //调用消息队列的方法,将消息塞入消息队列
        return queue.enqueueMessage(msg, uptimeMillis);
    }

此处需要留意的一个点就是下面这句代码

      msg.target = this;

这句代码是把handler绑定到了每个Message上面,这样后面每个Message就知道使用哪个handler去处理message。

好了handler中的方法我们已经看完了,其实没啥技术含量,就是跟着每个方法走就行了

再进一步(MessageQueue)

刚刚我们在handler中调用了enqueueMessage方法,所以接下来我们瞅瞅enqueueMessage方法。

    boolean enqueueMessage(Message msg, long when) {
		··· //略去部分代码
        synchronized (this) {
	        ···//略去部分代码
            msg.markInUse();
            msg.when = when; // 将消息发送时间保存到message中
            Message p = mMessages; //取出当前队列的第一个message,也就是最先被发送的消息
            boolean needWake;
            //如果当前队列中为空,或者新添加的消息发送时间要早于,目前队列中第一个消息的时间,则将当前消息排在队列第一个消息的前面
            if (p == null || when == 0 || when < p.when) { 
                // New head, wake up the event queue if blocked.
                msg.next = p; //记录后面一个消息的地址,类似链表
                mMessages = msg; //将当前消息置位第一个消息
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) { //死循环,类似while(true)
                    prev = p; //保存列表中的上个Message
                    p = p.next; //取出下个Message
                    //如果已经到达了队列的尾端,或者上面新添加的Message的时间,小于当前的Message,则跳出死循环
                    if (p == null || when < p.when) { 
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
               // 将当前消息,插入到两个Message之间,如果是当前的消息所在的位置为队末的话,则当前消息的next为空
                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;
    }

其实,如果大家知道链表的话。上面的代码应该是秒懂的。

上面代码的可能有一点点难度的就是死循环那里。
那么我们假设队列中有这样的四个消息

[
A:{
when:0;
next:B},
B:{
when:100;
next:C},
C:{
when:200;
next:D}.
D:{
when:300;
next:null}
]

本来一切都很和谐,然后我们忽然来了一条Message

[
	E:{
	when:250;
	next:null
	}
]

因为很明显E.when是大于A.when的,所以程序会走入那个死循环的中。
那么循环第一次的时候

prev=A;
p=B;
when=E.when

那么E.when=250<B.when=100 么?很明显不是,所以循环继续下一步

prev=B;
p=C;
when=E.when

那么这时候 E.when=250<C.when=200 么?很明显还不是,所以循环继续下一步

prev=C;
p=D;
when=E.when

那么这时候 E.when=250<C.when=300 么?这就毋庸置疑了,所以跳出循环。
这是执行

    msg.next = p; // invariant: p == prev.next
    prev.next = msg;

也就等价于

    E.next = D; // invariant: p == prev.next
    C.next = E;

然后这时候,我们的整个队列就变成了

[
A:{
when:0;
next:B},
B:{
when:100;
next:C},
C:{
when:200;
next:E}.
E:{
when:250;
next:D}.
D:{
when:300;
next:null}
]

我们也就成功把新的消息插入了消息队列

消息出列

前面我们查看源码的时候是从Activity出发,这次我们也一样,继续从activity中出发。

    private Handler handler=new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.i(TAG, "handleMessage: ");
        }
    };

handler源码

我们查看Handler中哪个地方调用了handleMessage。

	
	//在handler中该handleMessage 方法默认没有做任何实现
    public void handleMessage(Message msg) {
    }

    /**
     * Handle system messages here.
     */
    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

可以看到,handleMessage方法,就是由dispatchMessage方法调用。而且在这里我们还可以看到,如果该Handler如果设置了hanldeCallback,那么就不在走handlerMessage方法。而handlerCallback的代码如下

  new Handler(new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
 });

好了,我们继续看handler在哪里调用了dispatchMessage方法。于是我们在handler相关几个类中搜索一番,功夫不负有心人,我们在Looper类中发现了调用的地方

 public static void loop() {
   //...其他的代码
        for (;;) {
          Message msg = queue.next(); // might block 从消息队列中取出Message
			//...其他的代码
            try {
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
          //...其他的代码
        }
    }

此处我省略了无关的代码,只看主流程,可以看到,其实Looper内部其实是个死循环,不断的从queue中取出Message.然后调用Message的dispatchMessage方法。而“ queue.next(); ”方法是一个阻塞方法。这样也就是实现了整个handler流程的分析。

至于“ queue.next(); ”的具体细节,太复杂了,本宝宝看不懂,等看懂了再继续写!

结语

这算是android源码中最简单的源码了。不由的感叹到,自己还是太菜了!

最后送大家一张我的总结图。有不足之处,欢迎大家指出!

Top