开启成长之旅!这是我参加「日新方案 2 月更文挑战」的第1天,点击检查活动概况

偶然在Youtube上看到一名国外安卓开发者共享了一个进步运用功能的视频,其中运用到了macro benchmark来进行功能丈量,包括发动速度和列表帧率,办法是生成一个baseline-prof.txt文件放于app/src/main/下。查阅google的官方文档,其背面原理如下:

经过在运用或库中分发基准装备文件,Android 运转时 (ART) 能够经过预先 (AOT) 编译来优化包含的代码途径,然后针对每位新用户以及每个运用更新进步功能。这种装备文件引导的优化 (PGO) 可让运用优化发动、削减互动卡顿,并进步全体的运转时功能,然后让用户从初次发动开始便取得更好的运用体验。

基准装备文件介绍

baseline-prof.txt文件中定义了安装时要预编译的代码途径,打包时会跟从aab一同上传到Google Play,经过Google play安装时将取得预编译的收益。

这个方案看起来很不错,比较于其它的那些难以上手的发动优化方案,这个好像比较好落地,于是乎我开始了接入尝试,最终艰难成功了。

丈量工具

官方主张运用Jetpack Macrobenchmark来测验运用在已发动基准装备文件时的功能,然后将这些结果与已停用基准装备文件时的基准进行比较。接入的方法也很简单,假如你的AS版别满足要求,File/New Module/Benchmark就能够了。

Android使用基准配置文件(Baseline Profile)方案提升启动速度记录

会在benchmark Module生成一个ExampleStartupBenchmark测验类,将其修正一下变成如下。

@RunWith(AndroidJUnit4ClassRunner::class)
class ColdStartupBenchmark {
  @get:Rule
  val benchmarkRule = MacrobenchmarkRule()
    /**
    * 不运用基准装备文件
    */
  @Test
  fun startupNoCompilation() = startup(CompilationMode.None() )
    /**
    * 运用基准装备文件形式
    */
  @Test
  fun startupBaselineProfile() = startup(CompilationMode.Partial()) 
  @Test
  fun startupFullCompilation() = startup(CompilationMode.Full())
  private fun startup(compilationMode: CompilationMode) = benchmarkRule.measureRepeated(
    packageName = "com.example.macrobenchmark.target",
    metrics = listOf(StartupTimingMetric()),
    compilationMode = compilationMode, 
    iterations = 10,
    startupMode = StartupMode.COLD,
    setupBlock = {
      pressHome()
    }
  ) {
    // Waits for the first rendered frame, which represents time to initial display.
    startActivityAndWait()
    // Waits for content to be visible, which represents time to fully drawn.
        //此处可删去,my-content根据自己项目主页的布局决定
    device.wait(Until.hasObject(By.res("my-content")), 5_000)
  }
}

选择带有Benchmark后缀的build variant,测验结果如下所示:

ExampleStartupBenchmark_startUpCompilationModePartial
timeToInitialDisplayMs   min 290.7,   median 310.5,   max 391.2
Traces: Iteration 0 1 2 3 4
ExampleStartupBenchmark_startUpCompilationModeNone
timeToInitialDisplayMs   min 359.4,   median 381.9,   max 420.6
Traces: Iteration 0 1 2 3 4
  • timeToInitialDisplayMs– 从系统收到发动 intent 到渲染方针 activity 的榜首帧的时刻

  • timeToFullDisplayMs– 从系统收到发动 intent 到运用经过reportFullyDrawn办法陈述已完结制作的时刻。这个需要你手动调用activity.reportFullDrawn()才会有结果展现,表示此刻已完全制作。

  • Trace: Iteration能够看到每次发动的trace记录,点击数字会跳到Profiler分析界面

运转的时候可能会遇到的问题:

  • 有装备多渠道(Flavor),然后提示Run configuration ExampleStartupBenchmark is not supported in the current project.Cannot obtain the package.
    解决办法是benchmark里的flavor保持跟app模块一致就能够了

  • aar依赖找不到

    Could not determine the dependencies of null.
        Could not resolve all task dependencies for configuration':benchmark:flavorDemoBenchmarkTestedApks'.  
            Could not find :your_aar_name_in_testModule_libs:.  
               Required by:  
                   project :benchmark > project :app > project :testModule
    

    解决方案:在benchmark模块的build.gradle中添加

    repositories {
        flatDir {
            dirs '../testModule/libs', '../app/libs'
        }
    }
    
  • Unable to read any metrics during benchmark
    由于benchmark模块中的benchmark buildtype中debuggable要设为true才行

官方文档

生成基准装备文件

在benchmark模块处新建一个测验类:

@ExperimentalBaselineProfilesApi
@RunWith(AndroidJUnit4::class)
class BaselineProfileGenerator {
    @get:Rule val baselineProfileRule = BaselineProfileRule()
    @Test
    fun startup() =
        baselineProfileRule.collectBaselineProfile(packageName = "com.example.app") {
            pressHome()
            // This block defines the app's critical user journey. Here we are interested in
            // optimizing for app startup. But you can also navigate and scroll
            // through your most important UI.
            startActivityAndWait()
        }
}

新建一个Android9以上版别模拟器(真机不行),留意系统选择不包含Google Api的,执行adb root指令,修正ndk filter添加支撑,之后就能够跑上面新建的测验了,执行完结之后基准装备文件会生成于benchmark/build/outputs/connected_android_test_additional_output/flavorDemoBenchmark/Pixel 2处,名字类似于BaselineProfileGenerator_generateBaselineProfile-baseline-prof-2023-01-30-07-29-28.txt,将之拷贝到app/src/main/目录下,重命名为baseline-prof.txt。

官方文档

验证优化作用

万事俱备,只欠惊喜,验证一下对发动速度有多大进步。

在app模块添加以下依赖:

dependencies {
     implementation("androidx.profileinstaller:profileinstaller:1.3.0-alpha03")
}

衔接真机再次跑ExampleStartupBenchmark测验,在不同机型分别得到的结果为:

Pixel 1: android 10

ExampleStartupBenchmark_compilationPartial
timeToInitialDisplayMs   min 1,359.2,   median 1,422.4,   max 2,583.0  
ExampleStartupBenchmark_compilationNone  
timeToInitialDisplayMs   min 1,454.1,   median 1,556.7,   max 2,610.3 

三星S20: android 13

ExampleStartupBenchmark_compilationPartial
timeToInitialDisplayMs   min 597.2,   median 683.9,   max 763.4
ExampleStartupBenchmark_compilationNone
timeToInitialDisplayMs   min 699.5,   median 726.1,   max 753.5

三星S8+: android7

ExampleStartupBenchmark_compilationPartial
timeToInitialDisplayMs   min 1,089.1,   median 1,121.6,   max 1,249.4 
ExampleStartupBenchmark_compilationNone  
timeToInitialDisplayMs   min 1,147.5,   median 1,166.2,   max 1,338.2

观察数据能够看出,总体来说有必定的进步,特别是在功能低一点的机器会比较明显,但比较于google官方给的文档中的示例结果(进步20%+)还有一点距离,猜想应该跟生成的baseline-prof.txt有关,由于我这里只生成了发动过程到完结榜首帧制作时的热门代码列表,google的例子是生成了到主页并且切换tab的热门代码。

此外,基准装备文件也能够用在进步初次翻开操作流畅性上,原理也是相同的,只需要在BaselineProfileGenerator处添加初次进入之后的一些操作,比如像官方的例子相同的切换tab、列表滑动,生成新的文件即可。