移动性能测试 基于 shell 脚本的 Android 启动时间测试

Addison · 2016年04月29日 · 最后由 小朋友-哟 回复于 2018年05月14日 · 5650 次阅读
本帖已被设为精华帖!

Android 启动时间测试

  • 把被测安装包放到脚本同一路径下,命名为被测安装包的包名
  • 首先需要输入的两个参数是:被测包名和被测包名的启动类
    • aapt 获取启动类:aapt dump badging +file_path.apk
  • 三种测试场景
    • 冷启动
    • 热启动
    • 首次安装启动

应用启动的流程

Application 的构造器方法——>attachBaseContext()——>onCreate()——>Activity 的构造方法——>onCreate()——>配置主题中背景等属性——>onStart()——>onResume()——>测量布局绘制显示在界面上。

什么是应用启动的时间

  • 在上面这个启动流程中,任何一个地方有耗时操作都会拖慢我们应用的启动速度,而应用启动时间是用毫秒度量的,对于毫秒级别的快慢度量我们还是需要去精确的测量到到底应用启动花了多少时间,而根据这个时间来做衡量。
    什么才是应用的启动时间

  • 从点击应用的启动图标开始创建出一个新的进程直到我们看到了界面的第一帧,这段时间就是应用的启动时间。
    我们要测量的也就是这段时间,测量这段时间可以通过 adb shell 命令的方式进行测量,这种方法测量的最为精确,命令在下面的原理里面。

原理

adb shell am start -W [packageName]/[packageName.MainActivity]

  • 执行成功后将返回三个测量到的时间:
    • ThisTime:一般和 TotalTime 时间一样,除非在应用启动时开了一个透明的 Activity 预先处理一些事再显示出主 Activity,这样将比 TotalTime 小。
    • TotalTime:应用的启动时间,包括创建进程 +Application 初始化 +Activity 初始化到界面显示。
    • WaitTime:一般比 TotalTime 大点,包括系统影响的耗时。
  • 脚本取得是 TotalTime

减少应用启动时的耗时

针对冷启动时候的一些耗时,如上测得这个应用算是中型的 app,在冷启动的时候耗时已经快 700ms 了,如果项目再大点在 Application 中配置了更多的初始化操作,这样将可能达到 1s,这样每次启动都明显感觉延迟,所以在进行应用初始化的时候采取以下策略:

  • 在 Application 的构造器方法、attachBaseContext()、onCreate() 方法中不要进行耗时操作的初始化,一些数据预取放在异步线程中,可以采取 Callable 实现。
  • 对于 sp 的初始化,因为 sp 的特性在初始化时候会对数据全部读出来存在内存中,所以这个初始化放在主线程中不合适,反而会延迟应用的启动速度,对于这个还是需要放在异步线程中处理。
  • 对于 MainActivity,由于在获取到第一帧前,需要对 contentView 进行测量布局绘制操作,尽量减少布局的层次,考虑 StubView 的延迟加载策略,当然在 onCreate、onStart、onResume 方法中避免做耗时操作。

遵循上面三种策略可明显提高 app 启动速度。

参考代码段

用 shell 脚本写的比较简单的算法,具体跑的时间可以自己设置,个人以为 5 次左右就够了。下面是首次安装启动的代码,冷启动,热启动就不赘述了。



firstLaunch(){


    echo "start first launch 3 times"
    for i in {1..3}
    do
        echo "-----第 $i 次首次启动测试-----"
        uninsallApp
        installApp
        TotalTime[i]=`adb shell am start -W $PackageName/$ActivityName |grep TotalTime|awk -F ' ' '{print $2}'|tr -d "\r"`
        sleep 3s
        echo ${TotalTime[i]}
    done
    max=0
    for n in "${TotalTime[@]}"
    do
        ((n>max)) && max=$n
    done
    echo "热启动峰值:$max ms"

    avg=0
    sum=$((${TotalTime[1]} + ${TotalTime[2]} + ${TotalTime[3]}))
    avg=$[$sum/3]
    echo "热启动均值:$avg ms"
}


installApp(){

    echo "----重新安装被测APP $PackageName ----"
    apps_dir=$(pwd)
    echo $apps_dir
    adb install $apps_dir/$PackageName.apk

}

uninsallApp(){
    echo "-----开始卸载被测App $PackageName-----"
    adb uninstall $PackageName
}



echo -n "请输入被测包名:"
read PackageName
echo -n "请输入启动Activity:"
read ActivityName

echo -e "-----请输入测试类型:----- \n 冷启动测试输入1 \n 热启动测试输入2 \n 首次安装启动时间输入3"
read testType

if [[ $testType -eq 1 ]]; then
    echo "-----冷启动测试-----"
    coldLaunch
elif [[ $testType -eq 2 ]]; then
    echo "-----热启动测试-----"
    warmLaunch
elif [[ $testType -eq 3 ]]; then
    echo "-----首次安装启动测试-----"
    firstLaunch
else
    ERROR "测试Tpye输入错误"
fi

echo "----测试结束----"


附言

有什么其他好的测试方法 我们也可以一起交流交流

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
共收到 35 条回复 时间 点赞

冷启动和首次启动的差别在哪里?

很不错

#2 楼 @yuweixx 首次启动时间相对比冷启动测试来说应该是会更长一些。至于长在哪,跟具体 App 相关了。比如说:

  • 某些网络的资源,首次安装需要下载
  • 一般 app,首次启动会有一些初始化操作,数据的创建等,还有导航页面

#3 楼 @monkey 谢谢晔哥 还在学习中...

#4 楼 @addison 这里我可能有一个概念的理解偏差,我理解的冷启动就是首次启动,热启动是非首次启动。所以还想问问你对冷、热启动的定义是啥。

#6 楼 @yuweixx
我理解的是:

  • 冷启动:这时候你的应用程序的进程是没有创建的. 这也是大部分应用的使用场景.用户在桌面上点击你应用的 icon 之后,首先要创建进程,然后才启动 MainActivity.用户在桌面上点击这个 Activity 的时候,系统会直接起这个 Activity. 我们知道 Activity 在启动的时候会走 onCreate/onStart/onResume .这几个回调函数.

  • 热启动:如果是你按 Back 键,并没有将应用进程杀掉的话,那么执行上述命令就会快一些,因为不用创建进程了,只需要启动一个 Activity 即可。这也就是我们说的应用热启动。

#7 楼 @addison 去测试进程从后台状态恢复并没有太大的实际意义:
1、都清楚从后台直接唤醒会更快,管理好更慢的两个启动场景即可
2、用户实际使用中是会管理后台进程的。无论是手动回收后台进程,还是系统优化测试超阀值触发回收,进程始终是要被从后台清理的。
我作为系统端测试,对于抢占后台资源的行为都视为 “不合理行为”。进入后台需主动释放资源控制 PSS 和 CPU 占用,否则当触发系统优化管理措施的阀值,就会主动回收。我给预装测试提供的性能把控方案就严格控制了后台资源占用。

嗯很赞同@sandeman 冷热启动的计算方式,从软件或者说从系统日志角度来讲,lz 获取数据的方式颗粒度太粗。就简单来讲,我们回到获取数据的目的来讲,就是我们想达到的效果是什么。如果是冷热的启动测试需要更精准的数据,所以严格录像计算时间差比较好

#9 楼 @sandman touch 到 am start 这段时间单 APP 有办法优化吗,我以为是系统消耗掉的

—— 来自 TesterHome 官方 安卓客户端

#11 楼 @sanlengjingvv touch 到应用层响应 intent 事件确实需要安卓系统优化,并非是 APP 做优化。但如果 app 的启动涉及监听广播启动,就得另算了,得具体问题具体分析。
单就性能测试而言,准确的性能数据是从 touch 算起。可以细分为两部分:touch 到 app 首帧画面;app 首帧画面到指定 activity 显示完全。第一部分涉及系统性能优化,后一部分涉及更多的是 app 自身的优化。

#12 楼 @sandman 学习了🙏

学习了

#12 楼 @sandman 大神,am start 是没有 touch 到 app 首帧画面这个时间的吗?

#16 楼 @warcraft1236 am start 的启动顺序和由用户点击启动的流程完全不一样,在 app 首帧启动前的开销无可比性。

#17 楼 @sandman 能详细讲讲吗,或者给个相关的文章之类的,感谢

#18 楼 @warcraft1236 要了解细节百度就好了。
【Android framework】am 命令启动 Activity 流程 (http://www.cnblogs.com/sickworm/p/4220139.html)
Android4.0 input 事件输入流程详解 (中间层到应用层) (http://blog.csdn.net/wlwl0071986/article/details/8247138)

嗯收藏收藏!

好棒,目前用过的方法有录屏和代码注入。命令行的方式下次试试,最简洁,不知道准确度如何

#21 楼 @doublecchen 当对颗粒度要求没那么高的时候,可以试试命令行的方式

学习了。

好棒,又能学习到好东西了。

不错的分享

很不错

学习了,谢谢分享。

#19 楼 @sandman 看你的帖子和答复学到很多东西 感谢感谢~~😜

请问应用的 activity name 怎么获取啊?谢谢

#29 楼 @Wayne_2009 adb 命令就可以获取了

—— 来自 TesterHome 官方 安卓客户端

@sandman 问一下,为什么通过 adb shell am start -W 这种方式有时候获取到的 totalTime 的时间特别大,大概是 8 分钟?

这得具体问题具体分析,时间得和实际现象对的上,准确的方式还得从其他测试手段入手,am start 的形式,结果可分析意义不大。

@sandman 什么方式比较准确,能说一下吗?另外 TotalTime 特别大的原因,应该是我第一次用 am start 启动应用之后,此时应用已启动,我第二次调用 am start 并没有返回值等一段时间之后,杀掉应用。第三次调用 am start 启动应用,得到返回来的 ToTalTime 特别大(8 分钟)。怀疑是第二次调用 am start 得到的时间被累加了。

匿名 #34 · 2018年03月14日

TotalTime[i]=adb shell am start -W $PackageName/$ActivityName |grep TotalTime|awk -F ' ' '{print $2}'|tr -d "\r"这条命令似乎有问题,总是获取不到

提示 activity not started 怎么解决呢

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册