使用友盟+的APM服务实现基于Xamarin.Android的应用程序启动性能优化

本文涉及的产品
阿里云百炼推荐规格 ADB PostgreSQL,4核16GB 100GB 1个月
应用实时监控服务ARMS - 应用监控,每月50GB免费额度
简介: 随着开发平台的普及, 我们需要正确的⼯具和⽅法来满⾜不断增⻓的需求。Xamarin就是这样⼀种框架, 它⽀持在 Android、 iOS 和 Windows 平台上共享单个代码库。所以,我们将在 Xamarin.Android应⽤程序中测试性能, 就像在 AndroidStudio 中使⽤ Java 开发⼀样, 我们可以使⽤c#对性能进⾏测试, 从⽽优化启动时间。

背景

随着移动应⽤程序开发越来越流⾏,越来越多的应⽤程序浮现于市场。但是,开发移动应⽤程序并不是⼀个简单的过程,需要花费⼤量时间,尤其是如果想要⼀个可跨AppleAndroid Windows 运⾏的可扩展移动应⽤程序。

然⽽, 糟糕的性能可能会极⼤地损害⽤户体验。 ⽤户在任何时候都不希望看到 10 秒以上的启动 画⾯。如果等待时间过⻓,他们可能会感到⽣⽓、放弃购物、减少停留时间或完全卸载应⽤程序。

随着开发平台的普及,我们需要正确的⼯具和⽅法来满⾜不断增⻓的需求。Xamarin就是这样⼀种框架,它⽀持在AndroidiOSWindows平台上共享单个代码库。

所以,我们将在Xamarin.Android应⽤程序中测试性能,就像在AndroidStudio中使⽤Java开发⼀样,我们可以使⽤c#对性能进⾏测试,从⽽优化启动时间。

测试总启动时间

⾸先测试程序在不同设备的启动时间,此处⽤到的⼯具是友盟+推出的U-APM。从图中可以看 出应⽤程序在启动时间上还存在着⼀定的优化空间。

1.jpg

Android上,ActivityManager系统进程会显示⼀条初始显示时间⽇志消息,可以更好地了解整体启动时间。在命令⾏使⽤adblogcat快速查看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:

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

因为我们最终⽬标是提⾼移动应⽤程序的性能,那么第⼀步应该是实际测试卡顿函数的具体位置。如果盲⽬地进⾏代码更改,最终可能会和我们推测的结果产⽣很⼤的分歧,如果⼀些复杂的性能改进甚⾄会损害代码库的可维护性。这个过程应该是:测试,做出修改,再次测试,并且重复以上步骤。

采⽤U-APM测得卡顿位置主要出现于:

com.lgq.wood.expiramentation.apache.http.impl.exec.readRawTextFile

2.jpg

诊断问题

好, ⽬前应⽤程序由于readRawTextFile很慢。 现在我该怎么办?

⾸先我们需要对以下⼏个组件有⼀个系统性的了解

安卓ART

Android运⾏时(ART)Android上的应⽤程序和系统服务使⽤的托管运⾏时。ART作为运⾏时执⾏Dalvik可执⾏⽂件.dex⽂件-DalvikEX可执⾏⽂件这是⼀种⽤于存储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配置⽂    件,当使⽤带有配置⽂件的MonoAOT功能时,该配置⽂件可⽤于优化应⽤程序。启动跟踪可⽤于Visual Studio 2019 版本 16.2Visual Studio for Mac 2019 版本 8.2

可以通过编辑Android项⽬的.csproj⽂件并在Release<PropertyGroup>中添加以下属性来开始使⽤启动跟踪:

<PropertyGroup Condition = " '$(Configuration)|$(Platform)' == 'Release|Any

也可以在项⽬设置的Android选项中进⾏设置,  MonoAOT编译器启⽤使⽤会默认配置⽂件的启动跟踪,并在部署时加快Android应⽤程序的启动时间。

实际分析

我们需要实际分析我们的代码并需要改进。切换回Debug配置,并通过运⾏以下命令启⽤Mono分析器:

$ adb shellsetpropdebug.mono.profilelog:calls

adbshellAndroid设备或模拟器上运⾏单个shell命令。setprop设置Android系统属性,似于其他平台上的环境变量。

 

然后只需强制退出并重新启动应⽤程序。下次启动时,Mono会在Android应⽤程序的本地⽬录中保存⼀个⽂件。prole.mlpd注意这⾥存在⼀个问题,该⽂件只能由应⽤程序本身访问,因此我们必须使⽤命令来定位⽂件:run-as

$ adbshellrun-ascom.lgq.wood.expiramentationls-lfiles/.__override__

-rw-rw-r-- 1u0_a411u0_a4115153032020-07-2709:29profile.mlpd

为了从设备上获取⽂件,我使⽤了⼀个已知的可写⽬录,例如:  /sdcard/Download/

$ adb shell run-as com.lgq.wood.expiramentationcpfiles/.__override__/prof

复制⽂件后,您可以使⽤adbpull将⽂件获取到您的台式计算机:prole.mlpd

$ adb pull /sdcard/Download/profile.mlpd

/sdcard/Download/profile.mlpd: 1 file pulled,0skipped.162.7MB/s(515303

prole.mlpd是⼀个⼆进制⽂件,

Windows ⽤户需要在⽤于 Linux  Windows ⼦系统中安装 Mono才能运⾏。

有了上⾯的⼀系列代码, 就会出现⼀些有趣的数字。

解决方案

我们通过前⽂的调⽤,可以发现以下⼏个函数可能需要相当⻓的时间:

Dingtalk_20211112114825.jpg


还可以看到内存分配,例如:

Dingtalk_20211112114843.jpg

请注意, 如果您需要查看这些分配来⾃哪些⽅法, 您可以传递到。 --tracesmprof-report

我们做出了多种尝试,也都收到了⼀定成效。但是我们最意想不到的是,下⾯这个简单的改动。我们尝试将string直接从stream中读取,⽽不是使⽤响应的内容创建,然后使⽤新的System.Text.Json库来进⾏更⾼效的JSON解析:

// At the top

using System.Text.Json;

//...

async Task<Response> GetSlides()

{

var response = await httpClient.GetAsync("https://httpbin.org/json");

response.EnsureSuccessStatusCode();

using (var stream = await response.Content.ReadAsStreamAsync())

{

return await JsonSerializer.DeserializeAsync<Response>(stream);

}

}

查看⽅法调⽤的差异, 我们可以看到⼀个明显的时间优化:

Dingtalk_20211112115024.jpg

我们还可以看到以下内存分配的差异:

Dingtalk_20211112115035.jpg

这⼀点, 和我们在U-APM中测试得到的瓶颈函数相吻合, 瓶颈确实是处在readRawTextFile数中,我们尝试了以下⼏种⽅法,也⼀定程度上缓解了启动问题,但收益并没有U-APM中的    readRawTextFile那么⼤。在此列出,仅供参考:

1.  我们可以缓存 Web 请求的结果

2.  我们可以从磁盘上的⽂件加载之前的调⽤结果,⽐如设置24⼩时内有效。

3.  由于调⽤不是互相依赖,我们可以同时进⾏异步调⽤

4.  在服务器端,我们可以进⾏⼀个新的API调⽤,在⼀个请求中返回所有调⽤的数据


结论:

优化性能很难,⽅向也很多。关于代码慢的定位部分,改动后可能会发现这⼀部分根本不会产⽣效果,对代码产⽣影响的最佳⽅法是测试、测试,然后再次测试。改变后再次测试。⽽通过测试去提升性能,往往能针对问题做预先准备。也往往能更核⼼地提升核⼼性能瓶颈,从⽽带来⽅⽅⾯⾯的全⽅位提升。




作者:丁立戈

 

相关实践学习
通过云拨测对指定服务器进行Ping/DNS监测
本实验将通过云拨测对指定服务器进行Ping/DNS监测,评估网站服务质量和用户体验。
相关文章
|
10天前
|
IDE Java 开发工具
深入探索安卓应用开发:从环境搭建到第一个"Hello, World!"应用
本文将引导读者完成安卓应用开发的初步入门,包括安装必要的开发工具、配置开发环境、创建第一个简单的安卓项目,以及解释其背后的一些基本概念。通过一步步的指导和解释,本文旨在为安卓开发新手提供一个清晰、易懂的起点,帮助读者顺利地迈出安卓开发的第一步。
189 65
|
10天前
|
存储 Java Android开发
探索安卓应用开发:构建你的第一个"Hello World"应用
【9月更文挑战第24天】在本文中,我们将踏上一段激动人心的旅程,深入安卓应用开发的奥秘。通过一个简单而经典的“Hello World”项目,我们将解锁安卓应用开发的基础概念和步骤。无论你是编程新手还是希望扩展技能的老手,这篇文章都将为你提供一次实操体验。从搭建开发环境到运行你的应用,每一步都清晰易懂,确保你能顺利地迈出安卓开发的第一步。让我们开始吧,探索如何将一行简单的代码转变为一个功能齐全的安卓应用!
|
15天前
|
开发框架 搜索推荐 开发工具
打造个性化安卓应用:从零开始的Flutter之旅
【8月更文挑战第51天】本文是一篇面向初学者的Flutter入门教程,旨在通过简单易懂的语言和实际代码示例,引导读者步入跨平台移动应用开发的世界。文章首先介绍了Flutter的基本概念和优势,然后逐步展示了如何搭建开发环境、创建第一个Flutter应用,并实现了一个简单的待办事项列表。最后,文章探讨了Flutter在实现高性能和美观界面方面的潜力,鼓励读者发挥创意,探索更多可能。
65 15
|
5天前
|
监控 安全 Java
Kotlin 在公司上网监控中的安卓开发应用
在数字化办公环境中,公司对员工上网行为的监控日益重要。Kotlin 作为一种基于 JVM 的编程语言,具备简洁、安全、高效的特性,已成为安卓开发的首选语言之一。通过网络请求拦截,Kotlin 可实现网址监控、访问时间记录等功能,满足公司上网监控需求。其简洁性有助于快速构建强大的监控应用,并便于后续维护与扩展。因此,Kotlin 在安卓上网监控应用开发中展现出广阔前景。
9 1
|
19天前
|
搜索推荐 Java Android开发
打造个性化安卓应用:从设计到发布的全程指南
【9月更文挑战第15天】本篇文章将带领读者踏上一段激动人心的旅程,从构思一个独特的安卓应用想法开始,直至将其变为现实并成功发布。我们将一起探索如何捕捉灵感、设计界面、编写代码以及最终将应用推向市场。无论你是编程新手还是有经验的开发者,这篇文章都将为你提供宝贵的洞见和实用的技巧,让你的应用在竞争激烈的市场中脱颖而出。
56 17
|
15天前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
38 5
|
16天前
|
前端开发 Java 数据库
💡Android开发者必看!掌握这5大框架,轻松打造爆款应用不是梦!🏆
在Android开发领域,框架犹如指路明灯,助力开发者加速应用开发并提升品质。本文将介绍五大必备框架:Retrofit简化网络请求,Room优化数据库访问,MVVM架构提高代码可维护性,Dagger 2管理依赖注入,Jetpack Compose革新UI开发。掌握这些框架,助你在竞争激烈的市场中脱颖而出,打造爆款应用。
86 3
|
23天前
|
存储 Java 编译器
🔍深入Android底层,揭秘JVM与ART的奥秘,性能优化新视角!🔬
【9月更文挑战第12天】在Android开发领域,深入了解其底层机制对提升应用性能至关重要。本文详述了从早期Dalvik虚拟机到现今Android Runtime(ART)的演变过程,揭示了ART通过预编译技术实现更快启动速度和更高执行效率的奥秘。文中还介绍了ART的编译器与运行时环境,并提出了减少DEX文件数量、优化代码结构及合理管理内存等多种性能优化策略。通过掌握这些知识,开发者可以从全新的角度提升应用性能。
41 11
|
16天前
|
存储 API Android开发
"解锁Android权限迷宫:一场惊心动魄的动态权限请求之旅,让你的应用从平凡跃升至用户心尖的宠儿!"
随着Android系统的更新,权限管理成为应用开发的关键。尤其在Android 6.0(API 级别 23)后,动态权限请求机制的引入提升了用户隐私保护,要求开发者进行更精细的权限管理。
43 2
|
24天前
|
开发框架 Android开发 iOS开发
探索安卓与iOS开发的差异:构建未来应用的指南
在移动应用开发的广阔天地中,安卓与iOS两大平台各占半壁江山。本文将深入浅出地对比这两大操作系统的开发环境、工具和用户体验设计,揭示它们在编程语言、开发工具以及市场定位上的根本差异。我们将从开发者的视角出发,逐步剖析如何根据项目需求和目标受众选择适合的平台,同时探讨跨平台开发框架的利与弊,为那些立志于打造下一个热门应用的开发者提供一份实用的指南。
51 5

热门文章

最新文章

下一篇
无影云桌面