背景
随着挪动应⽤程序开发越来越流⾏,越来越多的应⽤程序浮现于市场。然而,开发挪动应⽤程序 并不是⼀个简略的过程,须要破费⼤量工夫,尤其是如果想要⼀个可跨 Apple、Android 和 Windows 运⾏的可扩大挪动应⽤程序。
然⽽,蹩脚的性能可能会极⼤地侵害⽤户体验。⽤户在任何时候都不心愿看到 10 秒以上的启动 画⾯。如果等待时间过⻓,他们可能会感到⽣⽓、放弃购物、缩小停留时间或齐全卸载应⽤程 序。
随着开发平台的遍及,咱们须要正确的⼯具和⽅法来满⾜一直增⻓的需要。
Xamarin 就是这样⼀种框架,它⽀持在 Android、iOS 和 Windows 平台上共享单个代码库。
所以,咱们将在 Xamarin.Android 应⽤程序中测试性能,就像在 Android Studio 中使⽤ Java 开发⼀样,咱们能够使⽤ c# 对性能进⾏测试,从⽽优化启动工夫。
测试总启动工夫
⾸先测试程序在不同设施的启动工夫,此处⽤到的⼯具是友盟 + 推出的 U -APM。从图中能够看 出应⽤程序在启动工夫上还存在着⼀定的优化空间。
在 Android 上,ActivityManager 零碎过程会显示⼀条“初始显示工夫”⽇志音讯,能够更好地了 解整体启动工夫。在命令⾏使⽤ adb logcat 疾速查看 Android 设施⽇志。或者使⽤ Visual Studio 中的 Android 调试⽇志。
在 Windows 上,运⾏以下 powershell:
输入:
上述⽇志音讯是在 x86 Android 模拟器上从 Visual Studio 调试应⽤程序时捕捉的。启动 / 连贯 调试器会产⽣⼀些额定的开销,并且短少 Debug 编译时的优化。
如果咱们简略地切换到 Release 配置并再次部署和运⾏应⽤程序:
如果咱们在 Pixel 3 XL 设施上测试应⽤程序:
因为咱们最终⽬标是提⾼挪动应⽤程序的性能,那么第⼀步应该是理论测试卡顿函数的具体位 置。如果盲⽬地进⾏代码更改,最终可能会和咱们揣测的后果产⽣很⼤的一致,如果⼀些简单的 性能改良甚⾄会侵害代码库的可维护性。这个过程应该是:测试,做出批改,再次测试,并且重 复以上步骤。
采⽤ U -APM 测得卡顿地位次要呈现于:
诊断问题
好,⽬前应⽤程序因为 readRawTextFile 很慢。当初我该怎么办?
⾸先咱们须要对以下⼏个组件有⼀个系统性的理解
安卓 ART
Android 运⾏时 (ART) 是 Android 上的应⽤程序和零碎服务使⽤的托管运⾏时。ART 作为运⾏ 时执⾏ Dalvik 可执⾏⽂件(.dex ⽂件 – D alvik EX 可执⾏⽂件),这是⼀种⽤于存储 Dalvik 字节码的紧凑格局。
ART 通过在装置应⽤程序时将整个应⽤程序编译为本机代码,引⼊了提前 (AOT) 编译。这带来 了更快的应⽤程序执⾏和改良的内存调配。以及垃圾收集机制、更精确的剖析等等。
为了实现这⼀点,ART 使⽤ dex2oat 来创立⼀个 ELF(可执⾏和链接格局)的可执⾏⽂件。毛病 是须要额定的工夫来编译。此外,应⽤程序会占⽤⼤量磁盘内存来存储已编译的代码。
Mono 运⾏时提供 AOT 性能。Mono 将预编译程序集以最⼩化 JIT 工夫并缩小内存使⽤。Mono 能够在⽀持它的平台(如 Android)上⽣成 ELF .so ⽂件。而后它在原始程序集旁边存储 ⼀个预编译的图像。
即
而后,这些⽂件能够被 Mono 运⾏时使⽤,并省略 JIT 开销
启动跟踪
Mono 引⼊了⼀项性能,容许在应⽤程序上使⽤内置的 AOT 分析器来⽣成 AOT 配置⽂件。分 析器进⾏内存剖析、执⾏工夫剖析,甚⾄是基于统计的抽样剖析。这会⽣成⼀个 AOT 配置⽂ 件,当使⽤带有配置⽂件的 Mono 的 AOT 性能时,该配置⽂件可⽤于优化应⽤程序。
启动跟踪可⽤于 Visual Studio 2019 版本 16.2 或 Visual Studio for Mac 2019 版本 8.2。
能够通过编辑 Android 项⽬的 .csproj ⽂件并在 Release <PropertyGroup> 中增加以下属性来 开始使⽤启动跟踪:
也能够在项⽬设置的 Android 选项中进⾏设置,Mono 的 AOT 编译器启⽤使⽤会默认配置⽂件 的启动跟踪,并在部署时放慢 Android 应⽤程序的启动工夫。
理论剖析
咱们须要理论剖析咱们的代码并须要改良。切换回 Debug 配置,并通过运⾏以下命令启⽤ Mono 分析器:
adb shell 在 Android 设施或模拟器上运⾏单个 shell 命令。setprop 设置 Android 零碎属性,类 似于其余平台上的环境变量。
而后只需强制退出并重新启动应⽤程序。下次启动时,Mono 会在 Android 应⽤程序的本地⽬ 录中保留⼀个⽂件。profile.mlpd
留神这⾥存在⼀个问题,该⽂件只能由应⽤程序自身拜访,因而咱们必须使⽤命令来定位⽂件:run-as
为了从设施上获取⽂件,我使⽤了⼀个已知的可写⽬录,例如:/sdcard/Download/
复制⽂件后,您能够使⽤ adb pull 将⽂件获取到您的台式计算机:profile.mlpd
profile.mlpd 是⼀个⼆进制⽂件,
Windows ⽤户须要在⽤于 Linux 的 Windows ⼦零碎中装置 Mono 能力运⾏。
有了上⾯的⼀系列代码,就会呈现⼀些乏味的数字。
解决方案
咱们通过前⽂的调⽤,能够发现以下⼏个函数可能须要相当⻓的工夫:
还能够看到内存调配,例如:
请留神,如果您须要查看这些调配来⾃哪些⽅法,您能够传递到。–tracesmprof-report
咱们做出了多种尝试,也都收到了⼀定功效。然而咱们最意想不到的是,下⾯这个简略的改变。咱们尝试将 string 间接从 stream 中读取,⽽不是使⽤响应的内容创立,而后使⽤新的
System.Text.Json 库来进⾏更⾼效的 JSON 解析:
查看⽅法调⽤的差别,咱们能够看到⼀个显著的工夫优化:
咱们还能够看到以下内存调配的差别:
这⼀点,和咱们在 U -APM 中测试失去的瓶颈函数相吻合,瓶颈的确是处在 readRawTextFile 函 数中,咱们尝试了以下⼏种⽅法,也⼀定水平上缓解了启动问题,但收益并没有 U -APM 中的 readRawTextFile 那么⼤。在此列出,仅供参考:
- 咱们能够缓存 Web 申请的后果
- 咱们能够从磁盘上的⽂件加载之前的调⽤后果,⽐如设置 24 ⼩时内无效。
- 因为调⽤不是相互依赖,咱们能够同时进⾏异步调⽤
- 在服务器端,咱们能够进⾏⼀个新的 API 调⽤,在⼀个申请中返回所有调⽤的数据
论断
优化性能很难,⽅向也很多。对于代码慢的定位局部,改变后可能会发现这⼀局部基本不会产⽣ 成果,对代码产⽣影响的最佳⽅法是测试、测试,而后再次测试。扭转后再次测试。⽽通过测试 去晋升性能,往往能针对问题做事后筹备。也往往能更核⼼地晋升核⼼性能瓶颈,从⽽带来⽅⽅ ⾯⾯的全⽅位晋升。