十五、优化应用的性能
在本章中,我们将介绍以下配方:
- 优化 JavaScript 代码
- 优化自定义 UI 组件的性能
- 保持动画以每秒 60 帧的速度运行
- 充分利用 ListView
- 提高我们应用的性能
- 优化本机 iOS 模块的性能
- 优化本机 Android 模块的性能
- 优化本机 iOS UI 组件的性能
- 优化本地 Android UI 组件的性能
介绍
性能是软件开发中几乎每一项技术的关键要求。React Native 的引入是为了解决将 web 应用包装在本机容器中的混合应用中存在的性能差的问题。React Native 的体系结构既具有灵活性,又具有优异的性能
在考虑 React-Native 应用的性能时,重要的是要考虑 React-Native 是如何工作的。React 原生应用有三个主要部分,其相对性能如下图所示:
本章中的方法侧重于使用占用更少内存和更少操作的低级函数,从而减少完成任务所需的时间。
优化 JavaScript 代码
可以肯定地说,您的 React 本机应用可能主要使用 JavaScript 编写。可能有一些本机模块和自定义 UI 组件,但在大多数情况下,所有视图和业务逻辑都可能用 JSX 和 JavaScript 编写。如果您使用的是现代 JavaScript 开发技术,那么您还将使用 ES6、ES7 及更高版本引入的语言结构。这些可以作为与 React Native(JavaScriptCore)捆绑的 JavaScript 解释器的一部分以本机方式提供,也可以由 Babel transpiler 提供。由于 JavaScript 可能构成任何给定 React 本机应用的大部分,因此这应该是我们优化应用的第一部分,以便从应用中挤出额外的性能。
本食谱将提供一些有用的提示,以优化 JavaScript 代码,使其尽可能具有性能
准备
此方法不一定依赖于 React Native,因为它关注用于编写任何 React 应用的 JavaScript。其中一些建议是微优化,可能只会提高较旧/较慢设备的性能。根据您打算支持的设备,一些提示将比其他提示更进一步。
怎么做。。。
- 首先要考虑的优化是加速迭代。通常,您可能会使用以迭代器函数为参数的函数(
forEach
、filter
和map
)。根据经验,这比执行标准的for
循环要慢。如果您正在迭代的集合的大小非常大,这可能会有所不同。下面是一个更快的过滤功能示例:
let myArray = [1,2,3,4,5,6,7];
let newArray;
// Slower:
function filterFn(element) {
return element > 2;
}
newArray = myArray.filter(filterFn);
// Faster:
function filterArray(array) {
var length = array.length,
myNewArray = [],
element,
i;
for(i = 0; i < length; i++) {
element = array[i];
if(element > 2) {
myNewArray.push(array[i]);
}
}
return myNewArray;
}
newArray = filterArray(myArray);
- 在优化迭代时,还可以更有效地确保将迭代中访问的变量存储在靠近以下位置的位置:
function findInArray(propertyerties, appConfig) {
for (let i = 0; i < propertyerties.length; i++) {
if (propertyerties[i].somepropertyerty ===
appConfig.userConfig.permissions[0]) {
// do something
}
}
}
function fasterFindInArray(propertyerties, appConfig) {
let matchPermission = appConfig.userConfig.permissions[0];
let length = propertyerties.length;
let i = 0;
for (; i < length; i++) {
if (propertyerties[i].somepropertyerty === matchPermission) {
// do something
}
}
}
- 您还可以优化逻辑表达式。将最快和最接近的执行语句放在左侧:
function canViewApp(user, isSuperUser) {
if (getUserPermissions(user).canView || isSuperUser) {
return true;
}
}
function canViewApp(user, isSuperUser) {
if (isSuperUser || getUserPermissions(user).canView) {
return true;
}
}
- 虽然现代 JavaScript(ES6、ES7 等)结构更易于开发,但它们的一些特性执行起来比 ES5 版本慢。这些特性可以包括
for of
、generators
、Object.assign
等。有关性能比较的良好参考资料,请参见https://kpdecker.github.io/six-speed/ 。 - 避免使用
try-catch
语句是有帮助的,因为它们会影响解释器进行的优化(如 V8 中的情况)。 - 数组应具有所有相同类型的成员。如果需要具有类型可能不同的集合,请使用对象。
它是如何工作的。。。
JavaScript 性能是一个经常争论的话题。有时很难跟上最新的性能指标,因为谷歌、苹果、Mozilla 和全球开源社区一直在努力改进 JavaScript 引擎。对于 React Native,我们主要关注 WebKit 的 JavaScriptCore
优化自定义 UI 组件的性能
在构建 React 本机应用时,您肯定会创建自定义 UI 组件。这些组件可以是其他几个组件的组合,也可以是构建在现有组件之上并添加更多功能的组件。随着功能的增加,复杂性也随之增加。这种复杂性的增加会导致更多的操作,反过来,也会导致减速。幸运的是,有一些方法可以确保我们的自定义 UI 组件能够发挥最佳性能。这个配方展示了几种最充分利用组件的技术。
准备
此配方要求您有一个带有一些自定义组件的 React 本机应用。由于这些性能建议可能会也可能不会为您的应用提供价值,因此在您选择将其应用于代码时,请谨慎行事。
怎么做。。。
- 我们应该关注的第一个优化是在给定组件的
state
对象中跟踪的内容。我们应该确保state
中的所有对象都在使用中,并且每个对象都可能发生更改,从而导致所需的重新渲染 -
看看每个组件的
render
功能。总体目标是保持此功能尽可能快地执行,因此请尝试确保其中不会出现长时间运行的进程。如果可以,请将计算和常量值缓存在render
函数之外,以便它们不会每次都实例化。 -
如果您有可能在
render
函数中返回的条件 JSX,请尽早返回return
。下面是一个简单的例子:
// unoptimized
render() {
let output;
const isAdminView = this.propertys.isAdminView;
if(isAdminView) {
output = (<AdminButton/>);
} else {
output = (
<View style={styles.button}>
<Text>{this.propertys.buttonLabel}</Text>
</View>
);
}
return output;
}
// optimized
render() {
const isAdminView = this.propertys.isAdminView;
if (isAdminView) {
return (<AdminButton/>);
}
return (
<View style={styles.button}>
<Text>{this.propertys.buttonLabel}</Text>
</View>
);
}
- 我们可以进行的最重要的优化是,如果不需要,则完全跳过
render
方法。这是通过实现shouldComponentUpdate
方法并从中返回false
,使其成为纯组件来实现的。下面是我们如何制作组件 aPureComponent
:
import React, { PureComponent } from 'react';
export default class Button extends PureComponent {
}
它是如何工作的。。。
大多数 React 本机应用将由自定义组件组成。将混合使用有状态和无状态组件。正如步骤 2中强调的,总体目标是在尽可能短的时间内呈现我们的组件。如步骤 4所述,如果一个组件可以被设计为只需渲染一次组件,然后保持不变,则可以实现另一个增益。有关如何使用纯成分以及它们如何有益的更多信息,请查看https://60devs.com/pure-component-in-react.html 。
另见
您可以在官方文档中找到有关 React 组件性能优化的更多信息 https://reactjs.org/docs/optimizing-performance.html 。
保持动画以每秒 60 帧的速度运行
任何高质量移动应用的一个重要方面是用户界面的流动性。动画用于提供丰富的用户体验,任何叮当声或抖动都会对此产生负面影响。动画可能用于各种交互,从视图之间的更改到对用户在组件上的触摸交互做出反应。创建高质量动画的最重要因素之一是确保它们不会阻塞 JavaScript 线程。为了保持动画流畅且不中断 UI 交互,渲染循环必须在 16.67 毫秒内渲染每个帧,以便达到 60 FPS。
在本教程中,我们将介绍几种提高动画性能的技术。这些技术特别关注于防止 JavaScript 执行中断主线程。
准备
对于这个配方,我们假设您有一个定义了一些动画的 React 本机应用。
怎么做。。。
- 首先,在 React Native 中调试动画性能时,我们需要启用性能监视器。要执行此操作,请显示开发菜单(摇动设备或模拟器中的cmd+D,然后点击显示性能监视器。 iOS 中的输出将类似于以下屏幕截图:
Android 中的输出类似于以下屏幕截图:
- 如果您希望为组件的过渡(
opacity
或尺寸(width
、height
)设置动画,请确保使用LayoutAnimation
。您可以在第 6 章中找到使用LayoutAnimation
的示例在展开和折叠容器配方中为您的应用添加基本动画。
If you want to use LayoutAnimation
on Android, you need to add the following code when your application starts: UIManager.setLayoutAnimationEnabledExperimental && UIManager.setLayoutAnimationEnabledExperimental(true)
.
- 如果您需要对动画进行有限控制,建议您使用 React Native 附带的
Animated
库。此库允许您将所有动画工作卸载到本机 UI 线程上。为此,我们必须将useNativeDriver
属性添加到Animated
调用中。让我们取一个例子,将它卸载到本地线程:
componentWillMount() {
this.setState({
fadeAnimimation: new Animated.Value(0)
});
}
componentDidMount() {
Animated.timing(this.state.fadeAnimimation, {
toValue: 1,
useNativeDriver: true
}).start();
}
Currently, only a subset of the functionality of the Animated library supports native offloading. Please refer to the There's more... section for a compatibility guide.
- 如果您无法将动画工作转移到本机线程上,仍然有一个解决方案可以提供平滑的体验。动画完成后,我们可以使用
InteractionManager
执行任务:
componentWillMount() {
this.setState({
isAnimationDone: false
});
}
componentWillUpdate() {
LayoutAnimation.easeInAndOut();
}
componentDidMount() {
InteractionManager.runAfterInteractions(() => {
this.setState({
isAnimationDone: true
});
})
}
render() {
if (!this.state.isAnimationDone) {
return this.renderPlaceholder();
}
return this.renderMainScene();
}
- 最后,如果仍然存在性能差的问题,则必须重新考虑动画策略,或者将性能差的视图作为目标平台上的自定义 UI 视图组件实现。这意味着使用 iOS 和/或 Android SDK 本机实现视图和动画。在第 11 章中添加本机功能中,我们在呈现自定义 iOS 视图组件和呈现自定义 Android 视图组件配方中介绍了创建自定义 UI 组件。
它是如何工作的。。。
本食谱中的技巧主要关注防止 JavaScript 线程锁定的简单目标。当我们的 JavaScript 线程开始丢弃帧(锁)时,我们就失去了与应用交互的能力,即使只是一瞬间。这看起来似乎无关紧要,但精明的用户会立即感受到效果。本食谱中技巧的重点是将动画卸载到 GPU 上。当动画在主线程(GPU 渲染的本机层)上运行时,用户可以自由地与应用交互,而不会出现口吃、挂起、刺耳或抖动。
还有更多。。。
以下是useNativeDriver
可用位置的快速参考:
| 功能 | iOS | 安卓 |
| style
、value
、propertys
| √ | √ |
| decay
| | √ |
| timing
| √ | √ |
| spring
| | √ |
| add
| √ | √ |
| multiply
| √ | √ |
| modulo
| √ | |
| diffClamp
| √ | √ |
| interpoloate
| √ | √ |
| event
| | √ |
| division
| √ | √ |
| transform
| √ | √ |
充分利用 ListView
React Native 提供了一个现成的性能列表组件。它非常灵活,支持渲染几乎任何可以在其中想象的组件,并且渲染速度相当快。如果你想阅读更多关于如何使用ListView
的例子,本书中有几个方法,包括第 2 章中的显示项列表、创建一个简单的使用它的 React 原生应用。React NativeListView
构建在ScrollView
之上,以实现使用任何视图组件渲染可变高度行的灵活性。
ListView
组件的主要性能和资源缺陷发生在您处理非常大的列表时。当用户滚动列表时,下一页的行在底部呈现。顶部的不可见行可以设置为从渲染树中删除,我们将在稍后介绍。但是,只要装入组件,对行的引用就仍然在内存中。当然,当我们的组件使用完可用内存时,为即将到来的组件提供快速访问存储的空间就会减少。本食谱将介绍如何处理这些潜在的性能和内存资源问题。
准备
对于这个配方,我们假设您有一个 React 本机应用,该应用使用了一个ListView
,最好是一个大数据集。
怎么做。。。
- 让我们从我们可以对香草
ListView
组件进行的一些优化开始。如果我们将initialListSize
属性设置为1
,我们可以加快初始渲染速度。 -
接下来,如果每行中渲染的组件不复杂,我们可以增加
pageSize
。 -
另一个优化是将
scrollRenderAheadDistance
设置为一个舒适的值。如果您希望用户很少滚动过初始视口,或者他们可能滚动缓慢,则可以降低该值。这可以防止ListView
提前渲染过多行。 - 最后,我们可以利用的最后一个优化是
removeClippedSubviews
属性。然而,官方文件规定如下:
"The feature may have bugs (missing content) in some circumstances - use at your own risk."
- 结合步骤 1 到步骤 4可以在下面的示例代码中看到:
renderRow(row) {
return (
<View style={{height:44, overflow:'hidden'}}>
<Text>Item {row.index}</Text>
</View>
)
}
render() {
return (
<View style={{flex:1}}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
pageSize={10}
initialListSize={1}
pageSize={10}
scrollAheadDistance={200}
/>
</View>
)
}
它是如何工作的。。。
与开发任何应用一样,越灵活、越复杂的应用执行速度越慢。ListView
是这个概念的一个很好的例子。它非常灵活,因为它可以连续呈现任何View
,但如果不小心使用,它会很快使应用停止。步骤 1到步骤 4中定义的优化结果将根据您渲染的内容和ListView
使用的数据结构在不同情况下有所不同。您应该尝试这些值,直到找到一个良好的平衡。作为最后手段,如果您仍然无法达到所需的性能基准,您可以查看一些提供新的ListView
实现或替代方案的社区模块。
另见
以下是一些承诺提高性能的第三方ListView
实现的列表:
recyclerlistview
:该库是ListView
最强大的替代品,拥有大量的改进和功能,包括支持交错网格布局、水平模式和页脚支持。存储库位于https://github.com/Flipkart/recyclerlistview 。react-native-sglistview
:当从渲染树中删除屏幕外的行时,通过刷新内存,将removeClippedSubviews
提升到下一个级别。存储库位于https://github.com/sghiassy/react-native-sglistview 。
提高我们应用的性能
React Native 存在的原因是使用 JavaScript 构建本机应用。这不同于类似的框架,如 Ionic 或 Cordova hybrid 应用,后者包装用 JavaScript 编写的 web 应用,并试图模拟本机应用的行为。这些 web 应用只能访问用于执行处理的本机 API,但不能在其应用内呈现本机视图。这是对本机应用进行响应的一个主要好处,从而使它们天生比混合应用更快。由于它的开箱即用性能更高,我们通常不必像使用混合 web 应用那样担心总体性能。尽管如此,只要付出一点额外的努力,性能可能会有轻微的改善。这个配方将提供一些快速的胜利,我们可以用它来构建更快的本地应用。
怎么做。。。
- 我们可以做的最简单的优化就是不向控制台输出任何语句。执行一个
console.log
语句并不像你想象的框架那么简单,因此建议你在准备捆绑最终应用时删除所有控制台语句。 - 如果您在开发过程中使用了大量控制台语句,您可以让 Babel 在使用
transform-remove-console
插件创建包时自动删除它们。可使用yarn
通过终端将其安装到项目中:
yarn add babel-plugin-transform-remove-console
- 或者,您可以使用
npm
:
npm install babel-plugin-transform-remove-console --save
安装包后,您可以通过添加包含以下代码的.babelrc
文件将其添加到项目中:
{
"presets": ["react-native"],
"env": {
"production": {
"plugins": ["transform-remove-console"]
}
}
}
- 接下来,确保在分析性能时,应用正在生产模式下运行,最好是在设备上运行。如果您对如何实现这一点感到好奇,可以参考第 13 章部署我们的应用中的将测试构建部署到 HockeyApp配方。
-
有时,当您为
View
的位置或布局设置动画时,您可能会注意到 UI 线程中的性能下降。对于 iOS 和 Android 平台,您可以通过将shouldRasterizeIOS
和renderToHardwareTextureAndroid
属性设置为 true 来缓解这种情况。请注意,这可能会显著增加内存使用量,因此也请确保在这些更改后测试性能。 -
如果您发现需要使用导航状态更改来转换视图,同时还要执行同步的、可能长期运行的流程,那么这可能会成为性能瓶颈。这通常发生在为
ListView
构建DataSource
或转换数据为即将到来的视图供电时。您应该尝试只处理数据的初始子集,以足够快地呈现 UI。页面转换之间的动画完成后,您可以使用InteractionManager
加载其余数据。有关如何使用InteractionManager
的更多信息,请参考以 60 FPS 的速度运行动画配方。 - 最后,如果您已经识别了正在减慢应用的特定组件或任务,并且无法找到可行的解决方案,那么您应该考虑通过创建本机模块或本机 UI 组件来将其移动到本机线程来实现这一功能。
它是如何工作的。。。
本食谱涵盖了所有 React 本机应用的一些更高级别和范围更广的技巧。从这些技巧中,您可能会看到最显著的性能提升是将组件移动到本机层,如步骤 7所述。
优化本机 iOS 模块的性能
通常,在构建 React 本机应用时,您需要使用本机 Android 和 iOS 代码。您可能构建了这些本机模块以公开本机 API 提供的一些额外功能,或者您的应用需要执行密集的后台任务。
正如前面提到的,在本机层工作确实允许您充分利用设备的全部容量。然而,这并不意味着我们编写的代码将自动成为最快的。始终存在优化和实现性能提升的空间。
在本食谱中,我们将提供一些技巧,介绍如何使用 iOS SDK 使 Objective-C 代码运行得更快。我们还将考虑如何反应本地和本地 No 桥,这是用来在 JavaScript 和本机层之间通信的,适合于更大的图片。
准备
对于这个配方,您应该有一个 React 本机应用,它使用为 iOS 创建的本机模块。如果您需要编写本机模块的帮助,请查看第 11 章添加本机功能中的公开定制 iOS 模块配方。
怎么做。。。
- 首先也是最重要的是,在使用本机模块时,我们必须注意通过 React 本机桥接器的数据。将跨桥事件和回调中的数据保持在最小值始终是目标,因为 Objective-C 和 JavaScript 之间的数据序列化非常缓慢。
- 如果需要将数据缓存在内存中以供本机模块使用,请将其存储在本地属性或字段变量中。本机模块是单例的。这样做,而不是返回要存储在 React 本机组件中的大型对象。
- 有时,我们必须利用大型类,因为它们在其功能集中是健壮的。对于 Objective-C 和 iOS 方面,不是每次通过
RCT_EXPORT_METHOD
公开功能时都在方法中实例化类似NSDateFormatter
的内容,而是将此类的引用存储为属性或实例变量。 - 此外,像
NSDateFormatter
这样的本地方法通常非常繁重,因此尽可能避免它们是可取的。例如,如果您的应用只能处理 UNIX 时间戳,那么您可以通过以下函数轻松地从时间戳获取一个NSDate
对象:
- (NSDate*)dateFromUnixTimestamp:(NSTimeInterval)timestamp {
return [NSDate dateWithTimeIntervalSince1970:timestamp];
}
- 如果出现这种情况,您可以进行的最重要的性能优化就是生成异步后台线程来处理密集型处理。React Native 非常适合此模型,因为它使用异步消息传递/事件系统在 JavaScript 和本机线程之间进行通信。后台进程完成后,您可以调用回调/承诺,也可以触发事件让 JavaScript 线程拾取。要了解如何在 React Native iOS 本机模块中创建和利用后台进程,请查看第 11 章添加本机功能中的iOS后台处理配方。
它是如何工作的。。。
Objective-C 代码的执行速度非常快,几乎与 vanilla C 一样快。因此,我们执行的优化与执行任务没有多大关系,而是与事物的实例化方式以及不阻塞本机线程有关。您将看到的最大性能提升是由 property 使用Grand Central Dispatch(GCD)生成后台进程,如步骤 5所述。
优化本机 Android 模块的性能
在开发 React 本机应用时,您可能会发现自己编写本机 Android 模块是为了在 iOS 和 Android 上创建跨平台功能,或者利用本机 API,这些 API 没有包装为 Android 的第一方模块,但确实存在于 iOS 上。希望您能在第 11 章添加本机功能中找到一些关于使用本机模块的有用建议。
在本食谱中,我们将介绍几种加速 React 本机 Android 本机模块的技术。其中许多技术仅限于 Android 上的一般开发,少数技术将解决与 React 本机 JavaScript 层的通信问题。
准备
对于这个配方,您应该有一个 React 本机应用,使用您为 Android 创建的本机模块。如果您需要编写本机模块的帮助,请查看第 11 章添加本机功能中的公开定制 Android 模块配方。
怎么做。。。
-
首先也是最重要的一点是,与 iOS 本机模块一样,您需要限制通过 React 本机网桥的数据量。将事件和回调中的数据保持在最低限度将有助于避免 Java 和 JavaScript 之间的序列化导致的速度减慢。此外,与 iOS 一样,尝试将数据缓存在内存中,以供本机模块使用;将其保存在私有字段中。本机模块是单例的。应该利用这一点,而不是返回要存储在 React 本机组件中的大型对象。
-
在为 Android 编写 Java 代码时,应该尽量避免创建短期对象。如果可以,请使用原语,尤其是对于数组之类的数据集。
- 最好是重用对象,而不是依赖垃圾收集器来获取未使用的引用并实例化新对象。
- Android SDK 提供了一种内存高效的数据结构,以取代
Map
的使用,后者将整数映射到对象,称为SparseArray
。使用它可以减少内存使用并提高性能。下面是一个例子:
SparseArray<SomeType> map = new SparseArray<SomeType>();
map.put(1, myObjectInstance);
There is also SparseIntArray
, which maps integers to integers, and SparseBooleanArray
, which maps integers to Boolean values.
-
虽然对于习惯于用 Java 进行 OOP 开发的开发人员来说,这听起来可能有悖常理,但通过直接访问实例字段避免使用 getter 和 setter 也可以提高性能。
-
如果您使用过
String
串联,请使用StringBuilder
。 - 最后,如果可能的话,您可以进行的最重要的性能优化是通过利用 React-Native 的异步消息/事件系统在 JavaScript 和本机线程之间进行通信,生成异步后台线程来执行繁重的计算。后台进程完成后,您可以调用回调/承诺,也可以触发事件让 JavaScript 线程拾取。要了解如何在 React Native Android 本机模块中创建后台进程,请阅读第 11 章添加本机功能中的Android后台处理配方。
它是如何工作的。。。
本食谱中的大部分技巧都是围绕有效的内存管理展开的。Android 操作系统使用传统风格的垃圾收集器,类似于桌面 Java 虚拟机。当垃圾收集器启动时,释放内存可能需要 100-200 毫秒。步骤 3-6都提供了减少应用内存使用的建议。
优化本机 iOS UI 组件的性能
Nethe Nead 为我们提供了一个极好的基础,可以使用内置的组件和样式来构建几乎任何类型的用户界面。使用 iosdk、OpenGL 或其他一些图形库在 Objective-C 中构建的组件通常比使用 JSX 构建预构建组件的性能更好。使用这些本机视图组件时,有些用例可能会对应用性能产生负面影响。
这个方法将集中于在呈现自定义视图时充分利用 iOS UIKit SDK。我们的目标是尽可能快地渲染所有内容,使应用以 60 FPS 的速度运行。
准备
对于这个配方,您应该有一个 React 本机应用,它呈现您为 iOS 编写的自定义本机 UI 组件。如果您需要在 React Native 中包装 UI 组件的帮助,请查看第 11 章添加本机功能中的公开定制 iOS 视图组件配方。
怎么做。。。
- 如前所述,由于 Objective-C 和 JavaScript 类型之间的数据序列化速度很慢,因此只有在不可避免的情况下才能通过 React-Native 桥传递数据。
- 如果在不久的将来需要存储数据以供引用,最好将其存储在初始化的本机类中。根据您的应用,您可以将其存储为
ViewManager
上的属性、为View
实例提供服务的单例或View
本身上的属性。 - 如果视图组件涉及将多个
UIView
实例呈现为父UIView
容器的子实例,请确保所有实例的opaque
属性都设置为true
。 -
如果要在视图组件内渲染图像(不使用 React Native
Image
组件),则将图像设置为与UIImageView
组件相同的维度有助于提高性能。缩放和其他图像变换是会影响帧速率的繁重操作。 -
在编写 iOS 视图组件时,最有效的调整之一是避免屏幕外渲染。尽可能避免使用 SDK 功能执行以下操作:
- 使用以核心图形(CG库)开头的类
- 覆盖
UIView
的drawRect
实现 - 设置
shouldRasterize=YES
,或在UIView
实例的layer
属性上使用setMasksToBounds
或setShadow
- 使用
CGContext
的自定义图纸
- 如果需要在视图中添加阴影,请确保设置
shadowPath
以防止屏幕外渲染。以下是初始化和阴影定义的外观示例:
RCT_EXPORT_MODULE()
- (UIView *)view {
UIView *view = [[UIView alloc] init];
view.layer.masksToBounds = NO;
view.layer.shadowColor = [UIColor blackColor].CGColor;
view.layer.shadowOffset = CGSizeMake(0.0f, 5.0f);
view.layer.shadowOpacity = 0.5f;
view.layer.shadowPath = [[UIBezierPath bezierPathWithRect:view.bounds] CGPath];
return view;
}
它是如何工作的。。。
本食谱重点介绍了一些有用的技巧,这些技巧可以让 GPU 尽可能多地完成工作。第二部分讨论了如何尽可能降低 GPU 上的负载。在步骤 3中强制执行opaque
属性,告诉 GPU 不要担心检查其他组件的可见性,以便计算透明度。步骤 5和步骤 6防止屏幕外渲染。屏幕外渲染使用 CPU 生成位图图像(这是一个缓慢的过程),更重要的是,它可以防止 GPU 在生成图像之前渲染视图。
优化本地 Android UI 组件的性能
在过去几年中,安卓原生用户界面的性能有了显著提高。这主要是由于它能够使用 GPU 硬件加速渲染组件和布局。在 React 本机应用中,您可能会发现自己正在使用自定义视图组件,特别是如果您想使用尚未包装为 React 本机组件的内置 Android 功能。尽管 Android 平台已经有意识地努力提高其 UI 的性能,但组件的呈现方式可能会很快否定所有这些好处。
在本食谱中,我们将讨论几种方法,以获得定制 Android 视图组件的最佳性能。
准备
对于这个方法,您应该有一个 React 本机应用,它呈现您为 Android 编写的自定义本机 UI 组件。如果您需要在 React Native 中包装 UI 组件的帮助,请查看第 11 章添加本机功能中的公开定制 Android view 组件配方。
怎么做。。。
- 如前所述,仅在必要时使用数据跨 React 本机网桥。尽量减少事件和回调中的数据,因为 Java 和 JavaScript 之间的数据序列化速度较慢。
- 如果在不久的将来需要存储数据以供引用,最好将其存储在已初始化的本机类中。根据您的应用,您可以将其存储为
SimpleViewManager
上的属性、为View
实例提供服务的单例或View
本身上的属性。 -
在建立视图时,请考虑组件通常由其他子组件组成。这些组件保存在布局的层次结构中。过度嵌套布局可能会成为非常昂贵的操作。如果您使用的是多层嵌套的
LinearLayout
实例,请尝试将它们替换为单个RelativeLayout
。 -
您可以使用捆绑在 Android 设备监视器中的 HierarchyViewer 工具分析布局的效率。要从 Android 设备监视器打开它,请单击窗口|打开透视图|“层次结构”视图,然后选择“确定”。
- 如果在 Java 中以本机方式在自定义视图上执行重复动画(不使用 React 本机动画 API),则可以利用硬件层来提高性能。只需将一个
withLayer
方法调用添加到您的animate
调用中。例如:
myView.animate()
.alpha(0.0f)
.withLayer()
.start();
它是如何工作的。。。
不幸的是,在呈现 Android UI 组件时,没有多少优化可以执行。它们通常围绕嵌套布局而不是围绕嵌套布局展开,因为这会在数量级上增加复杂性。当您遇到布局性能问题时,应用很可能会因过度使用 GPU 或过度拉伸而受损。当 GPU 在已渲染的现有视图上渲染新视图时,会发生过度渲染。您可以在 Android 开发者设置菜单中启用 GPU Overdraw 调试。超速驾驶的严重程度顺序为无颜色->蓝色->绿色->浅红色->深红色。
在步骤 5中,我们提供了提高动画性能的快速提示。对于重复的动画尤其如此,因为它将动画输出缓存在 GPU 上并重放。
版权属于:月萌API www.moonapi.com,转载请注明出处