抖音主线程消息调度优化 实现

3,993 阅读4分钟

抖音优化的思路

抖音原文

我们着重看 这一部分

image.png

虽然没有代码,但是文字描述的也够清楚了,我们可以总结归纳一下,抖音的优化思路 ,然后就可以尝试去实现它了

1.acitivity 跳转的 生命周期 消息 是可以 进行优化的 ,说白了 就是 让这些生命周期消息 的优先级提高 ,尽早的让他执行

  1. 要提升doFrame的消息优先级, 这里其实用消息屏障 就可以完成,这个我们后面再说, 这里就只要知道 这个优化 可以提高首帧渲染 速度即可

  2. 自定义Printer 是关键, 这个应该做过主线程消息卡顿监听的同学 都很熟悉了。

  3. 在这个自定义Printer中 遍历消息类型 就可以完成上述的优化思路

  4. 一定得在消息处理结束之后 再调整消息顺序, 这个其实仔细想想就明白为啥要这么做,

可以参考 得物的实现 ,该文章是拿Observer去实现的,有android版本的限制,尤其是一些hidden api,用起来比较麻烦,但是思路是很棒的,有兴趣的可以多看看。

我这里就是基于Printer这个接口来实现了,没什么hidden api 需要处理。

Activity 跳转消息流程

<=android8

在android的老版本中 activity的页面跳消息

image.png

主要关注下 message的what的值即可

image.png

记住这个100和101的值,后续我们会用到

>=android9

而从android9 开始 页面的跳转消息 就变成了 excute_transaction

image.png

继续跟一下,注意这个常量值

image.png

image.png

关于activity 生命周期的消息 其实就是封装在 这个activitylifecycleitem 这个类中

我们其实最关注的就是

image.png

到这里我们就搞清楚了 >=android9.0 的 页面跳转消息的 类型

joor 反射库

由于这次需要反复利用反射来做许多事情,为了简单 我们使用joor反射库 来做,具体就不介绍用法了,自行google

获取activityThread中的 handler

既然要调整消息的顺序,我们自然要用到handler,而且必须是activityThread中的 handler,去源码中找一下

image.png

image.png

image.png

找到以后 无非就是反射 拿到这个handler而已

这个其实没啥难度

mainHandler = Reflect.onClass("android.app.ActivityThread").field("sCurrentActivityThread").field("mH").get()

有了他,我们就可以发送各种消息 让ActivityThread 去处理啦

一些基本处理

首先要定义一组变量 其实就是为了判断是不是跳转类型的消息

class FakeActivityLifecycleItem {
    companion object {

        // public static final int EXECUTE_TRANSACTION = 159;
        const val EXECUTE_TRANSACTION = 159

        const val ON_PAUSE = 4
        const val ON_RESUME = 3

        const val LAUNCH_ACTIVITY = 100
        const val PAUSE_ACTIVITY = 101
    }
}

然后 我们就要从looper中 取得这个queue,然后取到message,用while循环的方式 来遍历这个消息列表中的消息,判断类型, 这里代码简单 就不过多讲了,

if (log?.contains("Finished to Handler") == true) {
    // 获取主线程的queue
    val mainMsg = Looper.getMainLooper().queue

    // 获取第一个消息
    var message: Message? = Reflect.on(mainMsg).field("mMessages").get()
    // 获取前一个消息
    var preMessage: Message? = null
    //  记录该消息 在原来链表中的第几个位置
    var index = 0
    //  遍历消息链表
    while (message != null) {
        // 因为消息中心 是一个链表,所以 要调整顺序 必然涉及到 preMessage的指针 改变
        if (preMessage != null) {
        }
        
        preMessage = message
        message = (Reflect.on(message).field("next").get())
        index++

    }    

提高页面跳转消息的优先级

有了前面的基本循环以后 就是考虑如何调整消息优先级

  1. 找到对应的jump类型的消息
  2. 把这个消息放到队列头部去
  3. 因为你把消息放到队列头部了,所以你必须把 message的preMsg 的next指针 指向 message的下一个next即可 否则你队列中就有重复的消息了,这里千万不要忘记了。

其实就是一个很简单的 链表删除

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
    // android8 以下
    if (message.what == FakeActivityLifecycleItem.LAUNCH_ACTIVITY ||
        message.what == FakeActivityLifecycleItem.PAUSE_ACTIVITY
    ) {
        val copyJumpMsgAndroid8 = Message.obtain(message)
        // 这里一定要设置异步消息
        if (message.isAsynchronous) {
            copyJumpMsgAndroid8.isAsynchronous = true
        }

        val nextMessage = Reflect.on(message).field("next").get<Message>()
        Reflect.on(preMessage).set("next", nextMessage)
        // 将关键的消息放到队列头部去执行
        mainHandler?.sendMessageAtFrontOfQueue(copyJumpMsgAndroid8)
        break
    }
} else {
    // android9 及 以上
    if (message.what == FakeActivityLifecycleItem.EXECUTE_TRANSACTION) {
        val sr = Reflect.on(message.obj).field("mLifecycleStateRequest").call("getTargetState").get<Int>()
        if (sr == FakeActivityLifecycleItem.ON_PAUSE || sr == FakeActivityLifecycleItem.ON_RESUME) {
            val copyJumpMsgAndroid9 = Message.obtain(message)
            if (message.isAsynchronous) {
                copyJumpMsgAndroid9.isAsynchronous = true
            }
            Log.e("lixiao", "preMessage!=null result:$sr  index:$index")
            val nextMessage = Reflect.on(message).field("next").get<Message>()
            Reflect.on(preMessage).set("next", nextMessage)
            // 将关键的消息放到队列头部去执行
            mainHandler?.sendMessageAtFrontOfQueue(copyJumpMsgAndroid9)
            break
        }
    }
}

提高首帧表现

可以重点参考同步屏障

这篇文章的说明

讲白了,你要提高doFrame的优先级,就是要想办法 提高同步屏障消息的优先级

if (message.target == null) {
    val copy = Message.obtain(message)
    val success = mainHandler?.sendMessageAtFrontOfQueue(copy)
    // 这里千万不要遗忘
    copy.target = null
    if (success == true) {
        val next = Reflect.on(message).field("next").get<Message>()
        Reflect.on(preMessage).set("next", next)
        Reflect.on(message).set("next", null)
        break
    } else {
        break
    }
}