应用启动性能 | 介绍 App Startup 库

image

最近我开始尝试使用 AndroidX 的应用启动 (App Startup) 库。在这个库发 布了 1.0 版本 之后,我觉得是时候深入理解一下为什么需要、什么时候以及如何使用这个库。

首先我注意到的是它的名字 —— 应用启动,其表明这个库的功能可能比它字面上的意义更广泛。这个库并不涉及普通的启动 (起码目前如此)。它主要是为了降低由 content provider 初始化导致的对应用启动速度的影响。

眼下您可能和我一样从来没有考虑过第三方库都是如何被初始化的。也许是因为所有这些处理过程都在底层完成。准确地说,您在 build.gradle 文件中添加了一行代码来使一个开发库作为工程的依赖项,大功告成 (当然您还需要在工程中调用这个库的 API,要不然您为什么要添加它呢?)。

可是有很多库并不是简单地封装好一堆方法以供调用,它们时常还需要首先被初始化,而往往这个初始化还是很耗时的过程。更糟糕的是,这其中还暗藏陷阱,因为这些库常常在应用启动的时候进行加载和初始化,究其原因是由于其内部使用了 content provider

敞开您的心扉 - Content Provider

Content provider 是 Android 中在不同应用之间共享数据的方式。举个例子,手机中的联系人是通过 content provider 来实现数据共享的,这也使得其他应用可以访问用户的联系人数据 (当然,我们假设用户给予这些应用访问联系人数据的权限)。您也同样可以为其他应用提供访问授权,来使用您应用创建的数据。或许您的应用管理着一个 甜甜圈评分的数据库,而作为如此重要的信息,其他应用可能需要频繁地使用。

只要一个应用通过任何一种方式声明 content provider 开启,此时就会自动创建并且启动 content provider。

使用 content provider 有一个重要但可能并不那么明显的问题,就是应用在声明 content provider 开启后,它会被自动创建并运行。而且需要注意的是,一个应用的启动并不只是通过用户启动,其还可以是通过系统访问该应用的服务,又或者是 job scheduler 触发了应用的一个循环作业等等。所有的这些都会触发 content provider 的资源开销以及产生相应的运算作业。当有需要访问该 content provider 的时候,系统需要该应用能够处于就绪状态,所以系统会在应用启动的时候自动运行 content provider。

这些细节对于仅仅调用这些库的开发者都是不可见的,因为具体实现都隐藏在自动生成的代码中。您需要查看 合并后的 manifest 文件 来理解这一切是如何发生的。

合并 Manifest

我针对 Android 应用清单的交互操作基本上都发生在工程自生成的 Manifest.xml 文件中,我会通过编辑该文件来添加 activity、服务和权限。但是这个 manifest 文件并不是最终提交到系统的那个,这个文件只是提供了关于您应用的信息,这些信息会被 "合并" 到最终的 manifest 文件中。合并后的文件包含了您的 Manifest.xml,以及编译工具挑选的其他信息,包括了您应用使用库的 manifest 文件。也正是这个合并后的 manifest 文件告诉我们库的 content provider 究竟发生了什么。

让我们来看一个具体的例子。并不是所有的库都使用了 content provider (尽管这还是很常见的),所以我们要用一个包含 content provider 的库 -- WorkManager。为了在我的工程中使用 WorkManager,我在应用的 build.gradle 文件中添加了如下依赖:

// 查看最新的版本号 https://developer.android.google.cn/jetpack/androidx/releases/work
def work_version = "2.5.0"
implementation "androidx.work:work-runtime-ktx:$work_version"

在我同步以及构建了该应用之后,我测算了一下启动时间 (稍后会详细介绍) 来对比添加这一依赖前后启动时间上的差别。我注意到应用在添加依赖后,启动时间比之前多了 70ms,而且这是在还没有调用 WorkManager 任何功能的情况下,我只不过是添加了这个依赖。

我在合并后的 manifest 文件中发现了启动时间延迟的原因,您可以在查看 Manifest.xml 文件时,通过点击 Android Studio 编辑窗口左下方的 Merged Manifest 标签来查看合并后的 manifest 文件。

编辑窗口下方的标签控制着您所看到的是您应用的 manifest 文件还是最终合并后的 manifest 文件

编辑窗口下方的标签控制着您所看到的是您应用的 manifest 文件还是最终合并后的 manifest 文件

在合并后的 manifest 文件中,我发现声明 WorkManager 依赖增加了很多额外的信息,包括如下的 provider 代码块:

这个 provider 存在于添加 WorkManager 依赖后合并的 manifest 文件

这个 provider 存在于添加 WorkManager 依赖后合并的 manifest 文件

我很好奇这个 provider 是从哪里来的,所以我点击了其中的第一行,编辑器直接跳到 WorkManager 的 manifest 文件,其中包含如下代码:

<application>
    <provider
        android:name="androidx.work.impl.WorkManagerInitializer"
        android:authorities="${applicationId}.workmanager-init"
        android:directBootAware="false"
        android:exported="false"
        android:multiprocess="true"
        tools:targetApi="n" />
    <!-- ... and a bunch of other stuff ... -->
</application>

合并的 manifest 文件的工作原理就是合并了组成应用的所有模块的 manifest 文件,当然也包括我刚刚添加的 WorkManager 库的 manifest 文件。因为其包含的 content provider 现在是合并的 manifest 文件的一部分,系统会在应用启动的时候自动地创建并运行该 content provider。

现在我知道我是如何加载这个库以及运行相关的 content provider。但是这究竟有什么影响呢?

测算启动时间

我最近发布了一篇文章 - 测试应用启动性能,其中详细描述了如何测算应用的启动时间。我用了同样的方法来测算在添加 WorkManager 依赖前后的应用启动时间,并且发现 WorkManager 增加了平均 67ms 的应用启动时间。

请注意,正如我在 启动测试 的文章中提到的,我锁定了我的 Pixel 2 的 CPU 时钟频率,所以应用启动时间在其他用户的设备上可能会短一些,而在另外一些使用低端设备上会长一些。另外需要注意的是 (我也在那篇文章中提到),我可能并不需要锁定时钟频率,因为系统通常会在应用启动的时候以最高的频率运行。但是锁定时钟频率在性能测试的时候永远都是一个好做法,因为这样我们才能获得稳定的结果。同时,锁定时钟频率还通常会造成更长的运行时间 (由于更低的频率),这也会帮助我们降低由于过短运行时间造成的噪音数据。

还有一点需要强调的是,这个启动时间并不全是 content provider 产生的。Content provider 确实会需要一定时间来创建,但是其需要大概 1-2ms 而不是我看到的 67ms。其实这是这个库被加载以及初始化的总时间,外加创建和运行 content provider 的时间来初始化该代码库。

所以看起来仅仅是添加这个库到我的项目就造成了将近 70 毫秒的启动延迟。在一个真正的应用中,我可能会使用多个库,而在应用启动时它们中很多都有自己的 content provider 需要运行,这就会造成更严重的启动延迟。

所以,我们要做点什么来减轻这个问题的影响呢?敬请关注我们的后续文章,在下一篇文章中,我将深入探讨如何利用 AndroidX 的应用启动 (App Startup) 库来实现库的延迟加载。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容