如何稳定 Xamarin.Forms 应用程序的性能并发布 Xamarin.Forms 应用程序。 这包括优化性能,以及使用 Xamarin.UITest 和 App Center 自动执行测试。
提供应用程序性能
应用程序性能差表现在许多方面,这会使应用程序看起来无响应,导致滚动缓慢,还可降低设备电池寿命。 但是,优化性能不止需要实现高效的代码,还必须考虑用户对应用程序性能的体验。 例如,确保操作执行不会妨碍用户执行其他活动,这有助于改进用户的体验。
可以通过许多方法来提高 Xamarin.Forms 应用程序的性能和感知性能,这些方法共同可以极大地降低由 CPU 执行的工作量和应用程序占用的内存量。
首先:跨平台性能,讨论了非平台特定方法,可用于改善使用 Xamarin 平台生成的应用程序的内存使用情况和性能。
1、启用 XAML 编译器
参考:https://www.cnblogs.com/peterYong/p/11576802.html#_label2
2、使用已编译的绑定
已编译的绑定通过在编译时解析绑定表达式而不是在运行时使用反射来提升 Xamarin.Forms 应用程序中的数据绑定性能。 编译绑定表达式会生成编译代码,通常比使用经典绑定快 8 到 20 倍。 有关详细信息,请参阅已编译的绑定。
3、减少不必要的绑定
不要将绑定用于可以轻松静态设置的内容。 绑定不需要绑定的数据没有任何优势,因为绑定的成本效率不高。 例如,设置Button.Text =“ Accept”的开销要小于将Button.Text绑定到值为“ Accept”的ViewModel字符串属性的开销。
4、使用快速呈现器
快速渲染器通过展平原生控件层次结构,降低了Xamarin.Forms控件在Android上的膨胀和渲染成本。 通过创建更少的对象,这进一步提高了性能,从而减少了视觉树的复杂性和内存使用量。
从 Xamarin.Forms 4.0 开始,所有以 FormsAppCompatActivity
为目标的应用程序均默认使用快速呈现器。 有关详细信息,请参阅快速呈现器。
5、在 Android 上启用启动跟踪
在 Android 上预先编译 (AOT) 可最小化实时 (JIT) 应用程序启动开销和内存使用情况,代价是会创建更大的 APK。 一种替代方法是使用启动跟踪,与传统的 AOT 编译相比,启动跟踪在 Android APK 大小和启动时间之间可实现权衡。
与尽可能多地将应用程序编译为非托管代码不同,启动跟踪只编译一组托管方法,这些方法表示空白 Xamarin.Forms 应用程序中最昂贵的应用程序启动部分。 与传统的 AOT 编译相比,这种方法可以减少 APK 的大小,同时仍然提供类似的启动改进。
6、启用布局压缩
为了提升页面呈现性能,布局压缩从可视化树中删除指定的布局。 这带来的性能优势因页面复杂性、要使用的操作系统版本以及运行应用的设备而异。 不过,在旧设备上实现的性能提升最大。 有关详细信息,请参阅布局压缩。
在 XAML 中,可以通过设置启用布局压缩CompressedLayout.IsHeadless
附加属性设置为true
布局类上:
<StackLayout CompressedLayout.IsHeadless="true"> ... </StackLayout>
注:由于布局压缩会从视觉树中删除布局,因此它不适用于具有视觉外观或获得触摸输入的布局。 因此,设置VisualElement属性(例如BackgroundColor,IsVisible,Rotation,Scale,TranslationX和TranslationY或接受手势)的布局不适合布局压缩,但是,可以在设置视觉外观属性或接受外观的布局上启用布局压缩 手势不会导致构建或运行时错误,而是将应用布局压缩,并且视觉外观属性和手势识别将自动失败。
7、选择正确布局
能够显示多个子项但只有一个子项的布局非常浪费。 例如,以下代码示例显示带有单个子代的StackLayout:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DisplayImage.HomePage"> <StackLayout> <Image Source="waterfront.jpg" /> </StackLayout> </ContentPage>
这比较浪费,应删除 StackLayout
元素,
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="DisplayImage.HomePage"> <Image Source="waterfront.jpg" /> </ContentPage>
此外,不要尝试使用其他布局的组合来重现特定布局的外观,因为这会导致执行不需要的布局计算。 例如,不要尝试使用 StackLayout
实例的组合来重现 Grid
布局。
8、优化布局性能
若要获取最佳可能布局性能,请遵循以下准则:
- 通过指定
Margin
属性值来减少布局层次结构的深度,从而允许创建具有更少换行视图的布局。 有关详细信息,请参阅边距和填充。 - 使用
Grid
时,尝试确保将尽可能少的行和列设置为Auto
大小。 每个自动调整大小的行或列都会导致布局引擎执行额外布局计算,而是应在可能时使用固定大小的行和列。 或者,使用GridUnitType.Star
枚举值将行和列设置为占据成比例的空间量,前提是父树遵循这些布局准则。 - 除非需要,否则不要设置布局的
VerticalOptions
和HorizontalOptions
属性。LayoutOptions.Fill
和LayoutOptions.FillAndExpand
的默认值可以实现最佳布局优化。 更改这些属性会产生成本并消耗内存,即使是将它们设置为默认值。 - 尽可能避免使用
RelativeLayout
。 它会导致 CPU 不得不执行显著更多的工作。 - 使用
AbsoluteLayout
时,尽可能避免使用AbsoluteLayout.AutoSize
属性。 - 使用
StackLayout
时,确保只有一个子级设置为LayoutOptions.Expands
。 此属性可确保指定子级会占用StackLayout
可以向它提供的最大空间,而多次执行这些计算比较浪费。 - 避免调用
Layout
类的任何方法,因为它们会导致执行成本高昂的布局计算。 相反,可能可以通过设置TranslationX
和TranslationY
属性来获取所需布局行为。 或者,将Layout<View>
类设为子类以实现所需布局行为。 - Label:不要比需要 更频繁地更新任何
Label
实例,因为标签大小的更改可能会导致重新计算整个屏幕布局;除非需要,否则不要设置Label.VerticalTextAlignment
属性;尽可能将任何Label
实例的LineBreakMode
都设置为NoWrap
。
9、仔细选择依赖关系注入容器
依赖关系注入容器在移动应用程序中引入了其他性能限制。 使用容器来注册和解析类型会影响性能,因为容器使用反射来创建每个类型,特别是在应用中为每个页面导航重构依赖关系的情况。 如果存在许多或深度依赖关系,则创建成本会显著增加。 此外,类型注册(通常在应用程序启动期间发生)可能会对启动时间产生明显影响,具体取决于所使用的容器。
作为替代方案,可以通过使用工厂手动实现依赖注入来提高性能。
10、创建 Shell 应用程序
Xamarin.Forms Shell 应用程序提供了基于浮出控件和选项卡的固定导航体验。 如果应用程序用户体验可以通过 Shell 实现,那么这样做很有用。 Shell 应用程序有助于避免糟糕的启动体验,因为页面是根据导航所需而创建的,而不是在应用程序启动时创建,而在使用 `TabbedPage' 的应用程序中会出现这种情况。 有关详细信息,请参阅 Xamarin.Forms Shell。
11、使用 CollectionView 而不是 ListView
CollectionView
是使用不同布局规范显示数据列表的视图。 它为 ListView
提供了更灵活、更高效的替代方案。 有关详细信息,请参阅 Xamarin.Forms CollectionView。
12、优化 ListView 性能
使用 ListView
时,应对许多用户体验进行优化:
- 初始化 – 从创建控件时开始,到在屏幕上显示项时结束的时间间隔。
- 滚动 – 能够滚动列表,并确保 UI 不滞后于触控笔势。
- 交互,用于添加、删除和选择项。
ListView
控件需要应用程序提供数据和单元格模板。 实现此目标的方法会对该控件的性能产生很大影响。 有关详细信息,请参阅 ListView 性能。
13、优化图像资源
显示图像资源可能会极大提高应用程序的内存占用情况。 因此,仅应在必要时创建图像,应用程序不再需要图像后应立即将其释放。 例如,如果应用程序通过从流中读取其数据来显示图像,请确保仅当需要时才创建流,并确保在不再需要时释放流。 可以通过在创建页面时或是在 Page.Appearing
事件触发时创建流,然后在 Page.Disappearing
事件触发时释放流,来实现此目标。
使用 ImageSource.FromUri
方法下载图像进行显示时,通过确保将 UriImageSource.CachingEnabled
属性设置为 true
,来缓存下载的图像。 有关详细信息,请参阅使用图像。
有关详细信息,请参阅优化图像资源。
14、减小可视化树大小
减少页面上的元素数可以更快呈现页面,可通过两种主要方法来实现此目标。
第一种方法是隐藏不可见的元素,每个元素的 IsVisible
属性可确定该元素是否应属于可视化树的一部分。 因此,如果某个元素因为隐藏在其他元素后面而不可见,则删除该元素,或将其 IsVisible
属性设置为 false
。
第二种方法是删除不需要的元素。
15、减小应用程序资源字典大小
在整个应用程序中使用的任何资源 都应存储在应用程序的资源字典中以避免重复。 这会有助于减少整个应用程序中必须进行分析的 XAML 量。 下面的代码示例演示 HeadingLabelStyle
资源,它在应用程序范围内使用,因此在应用程序的资源字典中进行定义:
<Application xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Resources.App"> <Application.Resources> <ResourceDictionary> <Style x:Key="HeadingLabelStyle" TargetType="Label"> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="FontSize" Value="Large" /> <Setter Property="TextColor" Value="Red" /> </Style> </ResourceDictionary> </Application.Resources> </Application>
但是,特定于页面的 XAML 不应包含在应用程序(App.xaml)的资源字典中,因为这些资源随后会在应用程序启动时(而不是页面需要时)进行分析。 如果某个资源由不是启动页面的页面使用,则它应置于该页面的资源字典中,因此有助于减少在应用程序启动时分析的 XAML。
下面的代码示例演示 HeadingLabelStyle
资源,它只位于单个页面上,因此在该页面的资源字典中进行定义:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="Test.HomePage" Padding="0,20,0,0"> <ContentPage.Resources> <ResourceDictionary> <Style x:Key="HeadingLabelStyle" TargetType="Label"> <Setter Property="HorizontalOptions" Value="Center" /> <Setter Property="FontSize" Value="Large" /> <Setter Property="TextColor" Value="Red" /> </Style> </ResourceDictionary> </ContentPage.Resources> ... </ContentPage>
有关应用程序资源的详细信息,请参阅 XAML 样式。
16、使用自定义呈现器模式
有关自定义呈现器的详细信息,请参阅在每个平台上自定义控件。