Android Handler(1)流程分析

125 阅读3分钟

最近正好也在复习一些知识,正好整理一下,handler的流程。mac上没有source insight,看源码还是不太方便,暂时没有好的工具。

先上一张流程图吧,图是随便画的,有时间在优化一下。

未命名绘图drawio.png

一般来说,很多人都会把handler的机制看传送带。这里我们直接从代码上开始看。分为两条线。 先看看如何使用:

class HandlerActivity : Activity() {

    private val handler = object : Handler(Looper.myLooper()!!) {
        //接收message
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            when (msg.what) {
                1 -> Toast.makeText(this@HandlerActivity, "handler msg", Toast.LENGTH_SHORT).show()
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_handler)
        Thread {
            //发送message
            val message = Message.obtain()
            message.what = 1
            handler.sendMessage(message)
        }.start()
    }
}

一、 先从发送开始: 从handler.sendMessage点进方法,回调到handler类中的

//跳到sendMessage
public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
    
//跳到sendMessageDelayed
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        //参数不能小于0  delayMillis代表延时
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
//创建messagequeue对象,并赋值 直接往下看调用了enqueueMessage
public boolean sendMessageAtTime(@NonNull 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);
}
  /**
   * 最终会调用queue.enqueueMessage 去看Message.enqueueMessage
  * */
  private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        //这句很重要,this代表handler,将handler作为参数传递,也是引起GC不可达的原因
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        //赋值为同步消息,跟同步屏障有关
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
}

这个方法我们来张图吧,我们直接看msg跑到哪里去了。可以看红色的框。代码里进行了一些判断,主要对延时参数进行了判断,如果when比取出的消息队列头的when小,就直接添加,否者会遍历找到一个合适的位置添加进去。 至此,消息已经被加入队列了。

image.png

二、 接下来我们就直接看handler是如何拿到消息的回调的。可以用反推。但是一般大家都知道是从ActivityThread中开始的,那我们就直接看代码。直接看main中两个最主要的方法

image.png

    //looper进行准备工作,调用了prepare(false),直接把代码拿过来
    @Deprecated
    public static void prepareMainLooper() {
        //这里通过sThreadLocal不为null就会报错。这里说明sthreadlocal只能存放一个对象。
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //set方法,将looper对象传入我们直接去看threadlocal的set方法
        sThreadLocal.set(new Looper(quitAllowed));
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
    
    public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        //同理,通过localthreadmap,去取线程为key的值
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

   public void set(T value) {
        //得到当前线程实例对象
        Thread t = Thread.currentThread();
        //将线程实例作为key,去查询map是否已经存值
        ThreadLocalMap map = getMap(t);
        if (map != null)
            //没有的话,将线程实例作为key,looper作为value存储
            //这里可以看出,一个线程对应一个looper对象
            map.set(this, value);
        else
            createMap(t, value);
    }

此时已经准备工作已经基本做完了。直接看looper。looper方法非常长,我就直接拿关键的代码说。

image.png

首先看loop方法中首先拿到了Messagequeue消息队列。然后第二个框,可以看到通过queue.next将消息取出来。 这里我们只看流程,不管休眠、唤醒、内存屏障之类的。可以看下面messagequeue的代码,for循环最后取出消息返回。那我们在看looper的代码,拿到msg之后做了什么。

image.png

可以在looper.loop中看到一句非常重要的代码,前面我们说过target就是handler对象,可以看出loop方法中,调用了handler.dispatchMessage将消息传递回去。

image.png 由下图可见,进入了handlermessage的回调。此时进入了接收消息。我们的流程就走完了。消息此时可以被接收方接收。

流程走完。能时间的尽量走一下流程,加深印象。