Android 性能监控之——消息调度启动优化方案实践_性能优化demo(1)

模拟劣化场景

我们首先模拟一个会影响冷启动的耗时消息场景, 在demo中,插入一个耗时消息到 startActivity对应的消息之前。

package com.knightboost.appoptimizeframework

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.knightboost.optimize.looperopt.ColdLaunchBoost
import com.knightboost.optimize.looperopt.ColdLaunchBoost.WatchingState

class SplashActivity : AppCompatActivity() {
val handler = Handler(Looper.getMainLooper())

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
Log.d(“MainLooperBoost”, “SplashActivity onCreate”)

}

override fun onStart() {
super.onStart()
Log.d(“MainLooperBoost”, “SplashActivity onStart”)
}

override fun onResume() {
super.onResume()
Log.d(“MainLooperBoost”, “SplashActivity onResume”)
Handler().postDelayed({
//发送3秒的耗时消息到队列中
//这里为了方便模拟,直接在主线程发送耗时任务,模拟耗时消息在 启动Activity消息之前的场景
handler.post({
Thread.sleep(3000)
Log.e(“MainLooperBoost”, “任务处理3000ms”)
})
val intent = Intent(this, MainActivity::class.java)
Log.e(“MainLooperBoost”, “begin start to MainActivity”)
startActivity(intent)
//标记接下来需要优化 启动Activity的相关消息
ColdLaunchBoost.getInstance().curWatchingState = WatchingState.STATE_WATCHING_START_MAIN_ACTIVITY
},1000)

}

override fun onPause() {
super.onPause()
Log.d(“MainLooperBoost”, “SplashActivity onPause”)
}

override fun onStop() {
super.onStop()
Log.d(“MainLooperBoost”, “SplashActivity onStop”)
}

}

这里的startActivity函数在实现底层会生成2个消息,其目的分别对应“Pause当前的Activity",以及 “resume MainActivity”。在函数刚执行结束时,此时的消息队列大概是这样的(为了方便理解,忽略延迟1秒对应的消息以及其它消息)。

以下视频为代码运行效果,可以发现在闪屏页展示一秒后,并未立即进行页面跳转操作,其被阻塞了3秒。

对应运行时的日志:

那么为了不让其他消息,影响到 startActivity的操作,就需要提升 startActivity操作相应消息的顺序。

优化方案

消息调度监控

提高目标消息的顺序,首先需要一个检查消息队列内消息的时机, 我们可以在每次消息调度结束时进行,如果发现当前队列中 有相应的需要提升优先级的消息,则将其移动至消息队首。

消息的调度监控有两种方式,在低版本系统可以基于设置Printer替换实现,不过这种方式只能获取到消息的开始和结束时间,无法获取到Message对象,并且基于Printer的方案会有额外的字符串拼接的性能开销。 第二种是通过调用Looper的 setObserver 函数设置消息调度观察者,相比Printer的方案,它可以拿到调度的Message对象,并且没有额外的性能开销,缺点是 有hiddenApi的限制。

消息类型判断

修改消息的顺序,需要先从队列中获取到目标消息,上个小节已经说过,startActivity 会有2个消息调度,分别是:“pause 当前Activity”,以及“resum新的Activity” 。 在Android 9.0以下版本,可以通过判断 message的target(Handler) 以及 what值区分,它们分别对应 ActivityThread中 mH Handler 的 LAUNCH_ACTIVITY (100), PAUSE_ACTIVITY(107)

而在Android 9.0以上版本,所有Activity生命周期事务变化被合并到一个消息 EXECUTE_TRANSACTION 中

那么高版本如何判断一个消息是为了 PauseActivity呢?通过源码分析,可以发现这个Message的obj属性是一个ClientTransaction类型的对象,而该对象的mLifecycleStateRequest的getTargetState()函数返回值 标识了期望的生命周期状态

以pauseActivity为例,其实际的对象类型为 PauseActivityItem, 它的getTargetState 函数返回值为 ON_PAUSE =4。

因此,我们可以先通过判断Message what值为 EXECUTE_TRANSACTION (159), 再通过反射最终获取到 mLifecycleStateRequest 对象getTargetState函数的返回值,来判断消息是pauseActivity,还是 resumeActivity。

以下为整个流程具体的实现代码: 首先在startActivity 后,主动标记后续需要优化 启动页面的消息

class SplashActivity : AppCompatActivity() {
//…
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_splash)
Log.d(“MainLooperBoost”, “SplashActivity onCreate”)
Handler().postDelayed({
//发送3秒的耗时消息到队列中
//这里为了方便模拟,直接在主线程发送耗时任务,模拟耗时消息在 启动Activity消息之前的场景
handler.post({
Thread.sleep(3000)
Log.e(“MainLooperBoost”, “任务处理3000ms”)
})
val intent = Intent(this, MainActivity::class.java)
Log.e(“MainLooperBoost”, “begin start to MainActivity”)
startActivity(intent)
//标记接下来需要优化 启动Activity的相关消息
ColdLaunchBoost.getInstance().curWatchingState = WatchingState.STATE_WATCHING_START_MAIN_ACTIVITY

},1000)
}
//…
}

基于Looper消息调度监控,每次消息调度结束时,检查消息队列中的消息,判断是否存在目标消息

其中pauseActivity的Message判断逻辑为, launchActivity消息判断同理。

launchActivity消息判断同理,只是判断targetState的值不同。

修改消息顺序、优化页面跳转

修改普通消息的顺序比较简单。当遍历消息队列找到目标message后,可以修改前一个消息的next值,使其指向下一个消息,这样就从消息队列中移除了消息,之后再复制一份目标消息,重新发送到队列首部。

public boolean upgradeMessagePriority(Handler handler, MessageQueue messageQueue,
TargetMessageChecker targetMessageChecker) {
synchronized (messageQueue) {
try {
Message message = (Message) filed_mMessages.get(messageQueue);
Message preMessage = null;
while (message != null) {
if (targetMessageChecker.isTargetMessage(message)) {
// 拷贝消息
Message copy = Message.obtain(message);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
if (message.isAsynchronous()) {
copy.setAsynchronous(true);
}
}
if (preMessage != null) { //如果已经在队列首部了,则不需要优化
//当前消息的下一个消息

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
img

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

习笔记、源码讲义、实战项目、讲解视频,并且会持续更新**

如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-ZBtwac7z-1712645473051)]

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值