基于Xamarin.Android的应用程序启动性能优化

背景 随着移动应?程序开发越来越流?,越来越多的应?程序浮现于市场。但是,开发移动应?程序并不是?个简单的过程,需要花费?量时间,尤其是如果想要?个可跨Apple、 Android 和 Windows 运?的可扩展移动应?程序。
然?,糟糕的性能可能会极?地损害?户体验。?户在任何时候都不希望看到10秒以上的启动画?。如果等待时间过?\他们可能会感到??、放弃购物、减少停留时间或完全卸载应?程序。
随着开发平台的普及,我们需要正确的?具和?法来满?不断增?的需求。
Xamarin就是这样?种框架,它?持在 Android、iOS 和 Windows 平台上共享单个代码库。
所以,我们将在 Xamarin.Android 应?程序中测试性能, 就像在 Android Studio 中使? Java 开发?样, 我们可以使?c#对性能进?测试, 从?优化启动时间。
测试总启动时间 ?先测试程序在不同设备的启动时间, 此处?到的?具是友盟+推出的U-APM。 从图中可以看出应?程序在启动时间上还存在着?定的优化空间。
基于Xamarin.Android的应用程序启动性能优化
文章图片

在 Android 上,ActivityManager系统进程会显示?条“初始显示时间”?志消息, 可以更好地了解整体启动时间。在命令?使?adb logcat快速查看 Android 设备?志。 或者使?Visual Studio中的Android 调试?志。
在 Windows 上,运?以下powershell

> adb logcat -d | Select-String Displayed

输出:
ActivityTaskManager: Displayed com.lgq.wood.expiramentation/.MainActivity:

上述?志消息是在 x86 Android 模拟器上从 Visual Studio 调试应?程序时捕获的。 启动/连接调试器会产??些额外的开销,并且缺少Debug编译时的优化。
如果我们简单地切换到Release配置并再次部署和运?应?程序:
ActivityTaskManager: Displayed com.lgq.wood.expiramentation/.MainActivity:

如果我们在 Pixel 3 XL 设备上测试应?程序:
ActivityTaskManager: Displayed com.lgq.wood.expiramentation/.MainActivity:

因为我们最终?标是提?移动应?程序的性能, 那么第?步应该是实际测试卡顿函数的具体位置。 如果盲?地进?代码更改,最终可能会和我们推测的结果产?很?的分歧, 如果?些复杂的性能改进甚?会损害代码库的可维护性。这个过程应该是:测试,做出修改,再次测试,并且重复以上步骤。
采?U-APM测得卡顿位置主要出现于:
com.lgq.wood.expiramentation.apache.http.impl.exec.readRawTextFile

基于Xamarin.Android的应用程序启动性能优化
文章图片

诊断问题 好, ?前应?程序由于readRawTextFile很慢。 现在我该怎么办?
?先我们需要对以下?个组件有?个系统性的了解
安卓ART
Android 运?时 (ART) 是 Android 上的应?程序和系统服务使?的托管运?时。 ART 作为运?时执? Dalvik 可执??件 (.dex ?件 - D alvik EX可执??件) , 这是?种?于存储 Dalvik 字节码的紧凑格式。
ART 通过在安装应?程序时将整个应?程序编译为本机代码, 引?了提前 (AOT) 编译。 这带来 了更快的应?程序执?和改进的内存分配。 以及垃圾收集机制、更准确的分析等等。
为了实现这?点, ART使?dex2oat来创建?个ELF(可执?和链接格式) 的可执??件。缺点是需要额外的时间来编译。 此外,应?程序会占??量磁盘内存来存储已编译的代码。
AOT
Mono 运?时提供AOT功能。 Mono 将预编译程序集以最?化 JIT 时间并减少内存使? 。Mono可以在?持它的平台(如 Android)上?成 ELF .so ?件。 然后它在原始程序集旁边存储?个预编译的图像。

Mono.Android.dll → libaot-Mono.Android.dll.so

然后, 这些?件可以被 Mono 运?时使?, 并省略 JIT 开销
启动跟踪
Mono 引?了?项功能,允许在应?程序上使?内置的 AOT 分析器来?成 AOT 配置?件。 分析器进?内存分析、执?时间分析,甚?是基于统计的抽样分析。这会?成?个 AOT 配置?件,当使?带有配置?件的 Mono 的 AOT 功能时,该配置?件可?于优化应?程序。
启动跟踪可?于Visual Studio 2019 版本 16.2或Visual Studio for Mac 2019 版本 8.2。
可以通过编辑 Android 项?的 .csproj ?件并在 Release 中添加以下属性来 开始使?启动跟踪:

也可以在项?设置的Android 选项中进?设置,Mono 的 AOT 编译器启?使?会默认配置?件 的启动跟踪, 并在部署时加快 Android 应?程序的启动时间。
实际分析
我们需要实际分析我们的代码并需要改进。切换回Debug配置, 并通过运?以下命令启?Mono 分析器:
$ adb shell setprop debug.mono.profile log:calls

adb shell在 Android 设备或模拟器上运?单个 shell 命令。 setprop设置Android系统属性, 类似于其他平台上的环境变量。
然后只需强制退出并重新启动应?程序。 下次启动时, Mono 会在 Android 应?程序的本地? 录中保存?个?件。 pro?le.mlpd
注意这?存在?个问题,该?件只能由应?程序本身访问,因此我们必须使?命令来定位?件: run-as
$ adb shell run-as com.lgq.wood.expiramentation ls -l files/.__override__ -rw-rw-r-- 1 u0_a411 u0_a411 515303 2020-07-27 09:29 profile.mlpd

为了从设备上获取?件, 我使?了?个已知的可写?录, 例如: /sdcard/Download/
$ adb shell run-as com.lgq.wood.expiramentation cp files/.__override__/prof

复制?件后,您可以使?adb pull将?件获取到您的台式计算机:pro?le.mlpd
$ adb pull /sdcard/Download/profile.mlpd /sdcard/Download/profile.mlpd: 1 file pulled, 0 skipped. 162.7 MB/s (515303

pro?le.mlpd是?个?进制?件
Windows ?户需要在?于 Linux 的 Windows ?系统中安装 Mono才能运?。
有了上?的?系列代码, 就会出现?些有趣的数字。
解决方案 我们通过前?的调?, 可以发现以下?个函数可能需要相当?的时间:
基于Xamarin.Android的应用程序启动性能优化
文章图片

还可以看到内存分配, 例如:
基于Xamarin.Android的应用程序启动性能优化
文章图片

请注意,如果您需要查看这些分配来?哪些?法,您可以传递到。--tracesmprof-report
我们做出了多种尝试,也都收到了?定成效。但是我们最意想不到的是,下?这个简单的改动。 我们尝试将string直接从stream中读取,?不是使?响应的内容创建,然后使?新的System.Text.Json库来进?更?效的 JSON 解析:
// At the top using System.Text.Json; //... async Task GetSlides()

{ var response = await httpClient.GetAsync("https://httpbin.org/json"); response.EnsureSuccessStatusCode(); using (var stream = await response.Content.ReadAsStreamAsync()) { return await JsonSerializer.DeserializeAsync(stream); } }

查看?法调?的差异, 我们可以看到?个明显的时间优化:
基于Xamarin.Android的应用程序启动性能优化
文章图片

基于Xamarin.Android的应用程序启动性能优化
文章图片

这?点, 和我们在U-APM中测试得到的瓶颈函数相吻合,瓶颈确实是处在readRawTextFile 函数中,我们尝试了以下?种?法,也?定程度上缓解了启动问题但收益并没有U-APM中的 readRawTextFile 那么?。在此列出,仅供参考:
  1. 我们可以缓存 Web 请求的结果
  2. 我们可以从磁盘上的?件加载之前的调?结果, ?如设置24 ?时内有效。
  3. 由于调?不是互相依赖, 我们可以同时进?异步调?
  4. 在服务器端, 我们可以进??个新的 API 调?, 在?个请求中返回所有调?的数据
【基于Xamarin.Android的应用程序启动性能优化】优化性能很难,?向也很多。关于代码慢的定位部分,改动后可能会发现这?部分根本不会产? 效果, 对代码产?影响的最佳?法是测试、测试,然后再次测试。改变后再次测试。?通过测试去提升性能,往往能针对问题做预先准备。也往往能更核?地提升核?性能瓶颈,从?带来????的全?位提升。

    推荐阅读