找出性能问题

时间:2018-02-05 16:00:52

标签: react-native react-native-android

在过去的几天里,我一直在与我创建的应用程序中的FlatList的性能问题作斗争。

FlatList由静态标头和x行组成。在测量的情况下,有62行,每行包含4个组件 - 其中一个使用4次,总计最多7个行单元。这个列表的每一个细胞或者是TouchableNativeFeedback或TouchableOpacity(用于测试目的,这些都高度() => null到onPress。有shouldComponentUpdate上几级(列表容器,行,单细胞)中使用,我想渲染性能够用了那种清单。

为了进行一致的测量,我使用了initialNumToRender={data.length},因此整个列表一次呈现。列表使用按钮呈现,数据加载不是我测量的一部分 - 它被预加载到组件本地状态。

根据附带的Chrome Performance Profile JS线程需要1.33秒来渲染组件。我已经使用CPU减速x6来更准确地模拟Android设备。

但是,列表显示在设备上大约15秒标记,因此从按钮按下到列表显示的实际渲染需要超过14秒!

我想弄清楚的是JS渲染组件和组件实际出现在屏幕上之间会发生什么,因为设备是 整个那个时候反应迟钝。每个触摸事件都已注册,但只有当列表最终显示在屏幕上时才会播放。

我已经附加了chrome dev工具的跟踪,使用android systrace工具进行的systrace和来自android profiler的屏幕(遗憾的是我无法找到导出后者的选项)。

跟踪几乎同时运行 - 订单是systrace,android profiler,chrome dev工具。

我应该采取哪些措施来帮助我了解应用冻结时发生了什么?

Simple reproduction application (code in src.js, first commit)

Chrome Performance Profile Chrome Performance Profile

Systrace HTML Systrace HTML

Android Profiler Android Profiler

3 个答案:

答案 0 :(得分:3)

查看source code you posted,我不认为这是React渲染问题。

问题在于,您在var comment = $$('#comment').getValue() + post_id.toString();方法和渲染过程中调用的辅助方法中做了太多工作。

每次在数组上调用render.filter.forEach时,整个列表都会重复.map次。对n组件执行此操作时,计算复杂度为m

例如,这是O(n * m)渲染方法:

TransportPaymentsListItem

在示例代码中有11个这样的迭代器调用。对于您使用过的62行数据集,数组迭代器的总计为 4216 次!即使每个迭代器都是非常简单的比较,只需遍历所有这些列表太慢并阻塞主JS线程。

要解决此问题,您应该在组件链中将状态转换提升到更高的位置,这样您只能进行一次迭代并构建一个视图模型,您可以向下传递组件树并以声明方式呈现而无需额外的工作。

答案 1 :(得分:1)

我现在尝试了不同的东西,我甚至考虑包装原生的Android RecyclerView,但公平地说,这似乎是一个挑战,因为我之前没有使用原生Android代码的经验。

过去几天我尝试过的其中一件事就是使用react-native-largelist,但它未能实现承诺的性能提升。公平地说,它可能比FlatList更慢,但我没有采取精确的测量。

经过几天的谷歌搜索,编码和分析我终于得到了this Medium post,它引用了recyclerlistview包,它似乎提供了比FlatList更好的体验。对于已分析的案例,渲染时间降至约2秒,包括300毫秒的JS线程工作。

必须注意到初始渲染的改进来自减少的渲染项目数量(在我的情况下为11)。 FlatList initialNumToRender={11} FlatList设置最初会在同一时间内呈现。

在我的情况下,初始渲染虽然仍然很重要,但并不是唯一重要的事情。较大列表的recyclerlistview性能下降主要是因为滚动时它会将所有渲染的行保留在内存中,而console.log会回收渲染的行以放入新数据。

重新渲染的观察者性能改进的原因实际上很容易测试。我在行组件中添加了shouldComponentUpdaterecyclerlistview,并计算了实际重新呈现的行数。对于我的行高和测试设备分辨率FlatList,只重新渲染17行,而shouldComponentUpdate触发数据集中每个项目的recyclerlistview。值得注意的是,FlatList重新呈现的行数不依赖于数据集大小。

我的结论是,recyclerlistview性能可能会因较大的数据集而降低,而TouchableNativeFeedback速度应该保持在相似的水平。

recyclerlistview里面的swal({ title: 'Are you sure?', text: "You won't be able to revert this!", type: 'warning', showCancelButton: true, cancelButtonColor: '#d33', confirmButtonColor: '#3085d6', confirmButtonText: 'Yes, delete it!' }).then(function(result){ if (result.value) { // perform the AJAX request } });似乎也更敏感,因为动画会毫不拖延地启动,但我无法解释这种看待分析器的行为。

我的行组件肯定还有改进的余地,但是现在我对整体列表渲染性能感到满意。

Simple reproduction application with recyclerlistview (code in src.js, second commit)

Chrome效果资料(recyclerlistview) Chrome Performance Profile (recyclerlistview)

Systrace HTML(recyclerlistview) Systrace HTML (recyclerlistview)

答案 2 :(得分:0)

只需再提供一个源即可尝试。

您可以像这样在android / app / build.gradle中启用Hermes。

project.ext.react = [
        entryFile   : "index.js",
        enableHermes: true,  // clean and rebuild if changing
]

我的应用程序没有崩溃,但经过一些繁琐的操作后,可触摸设备将停止工作。发生这种情况时,进程仍可以正常运行,应用程序的抽屉仍在工作。但是我的应用程序中所有可触摸的对象都停止工作,没有任何错误消息。我花了三天时间找到解决方案,最后尝试了爱马仕。在react native docs上显示

Hermes is an open-source JavaScript engine optimized for running React Native apps on Android.