背景
随着挪动应⽤程序开发越来越流⾏, 越来越多的应⽤程序浮现于市场。 然而, 开发挪动应⽤程序 并不是⼀个简略的过程, 须要破费⼤量工夫, 尤其是如果想要⼀个可跨 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那么⼤。 在此列出, 仅供参考:

  1.  咱们能够缓存 Web 申请的后果
  2.  咱们能够从磁盘上的⽂件加载之前的调⽤后果, ⽐如设置24 ⼩时内无效。
  3.  因为调⽤不是相互依赖, 咱们能够同时进⾏异步调⽤
  4.  在服务器端, 咱们能够进⾏⼀个新的 API 调⽤, 在⼀个申请中返回所有调⽤的数据

论断

优化性能很难, ⽅向也很多。 对于代码慢的定位局部, 改变后可能会发现这⼀局部基本不会产⽣ 成果, 对代码产⽣影响的最佳⽅法是测试、 测试, 而后再次测试。 扭转后再次测试。 ⽽通过测试 去晋升性能, 往往能针对问题做事后筹备。 也往往能更核⼼地晋升核⼼性能瓶颈, 从⽽带来⽅⽅ ⾯⾯的全⽅位晋升。