10

我有一个带有 RadListView 组件的 nativescript-vue 应用程序来向用户显示数据。列表的每一行都包含当前项目的多个信息。当我点击按钮加载并显示列表时,UI 冻结(快速硬件 -> 短;慢速硬件 -> 长)。我发现加载/组合数据的代码部分运行时间很短,但原生脚本内部渲染或 UI 元素的创建是问题所在。android控制台显示信息

I/Choreographer: Skipped 430 frames!  The application may be doing too much work on its main thread.

平台信息:

  • tns-ios 5.2.0
  • tns-android 5.2.1
  • nativescript-ui-listview 6.2.0
  • tns 核心模块 5.3.1

类似问题报告

我查看了 stackoverflow 和 github,但那里的问题(例如NativeScript Angular RadListView 渲染速度非常慢)看起来很相似,但解决方案不适合我。

我使用 LinearListView(不是 Grid 也不是 Stagged),也许在我的真实应用程序中,我将能够简化列表中的行元素,但在我的示例应用程序(向下看)中,我使用简单的行 ui 设计。

示例应用

为了获得更简单更好的报告,我在 {N}-Playground 上创建了一个示例应用程序。该应用程序创建 10000 个数组元素并将它们设置为 RadListView 的源,其中每个元素都有一个标签、一个开关和一个 ActivityIndi​​cator。

<RadListView ref="listView" 
    for="alarm in alarms"
    layout="linear">
    <v-template>
        <StackLayout class="list-element" orientation="vertical" >
            <GridLayout columns="*, auto, auto" rows="*">
                <Label col="0" row="0">{{alarm.name}}</Label>
                <Switch col="1" row="0" :checked="alarm.active" />
                <ActivityIndicator col="2" row="0" :busy="alarm.active"/>
             </GridLayout>
             <Label class="list-element-divider"></Label>
         </StackLayout>
     </v-template>
</RadListView>

在第一步中,将在一个临时数组中生成 10000 个元素:

loadData() {
    this.tmpAlarms = [];
    for (let i = 0; i <= 10000; i++) {
        this.tmpAlarms.push({
            name: "Hase " + i,
            active: i % 2 === 0,
        });
    }
}

使用第二个按钮将临时数组设置为源:

setData() {
    this.alarms = this.tmpAlarms;
}    

注意:即使示例应用程序在 S9 或其他高端智能手机上运行,​​我也使用 10000 个元素来使问题可见。

完整源码可在https://play.nativescript.org/?template=play-vue&id=Td1GWR下运行

在https://play.nativescript.org/?template=play-vue&id=5BXOFG下使用 ObservableArray 而不是普通数组的稍微不同的版本

在这两个版本中,数据处理速度很快,但是一旦从内部函数生成的 UI 元素,UI 就会被冻结。

示例:在我的带有 Android 6.x 的 Nexus 7 2013 上,UI 冻结了近 6 秒,并且没有其他应用程序在后台运行。

iOS

如果我在 iOS 设备(例如 iPhone 7s)上尝试相同的应用程序,渲染速度非常快,即使有 10000 个元素等等,UI 运行也很流畅。

想法?

Have anyone an idea how i can speed up the rendering? If not is there a suggestion how i can build an animation (e.g. ActivityIndicator) to show the user that the device is working? At the moment when i put an ActivityIndicator on the UI the user can not see it because of the frozen UI.

4

1 回答 1

0

Android 和 IOS 渲染 ui 的方式非常不同。这种添加到 Rad 列表的特殊性的差异会影响性能。请注意,rad 列表重用列表元素的实例以提高性能。这种重用在 IOS 和 Android 中的方式不同。

有两个简单的优化可以大大改善 ios 和 android 中的渲染。

  • 第一个是减少布局中的布局。如果元素较少,则渲染更容易。这减少了深度/通过渲染
<RadListView ref="listView" for="alarm in alarms" layout="linear">
    <v-template>
         <GridLayout class="list-element" columns="*, auto, auto" rows="*, auto">
             <Label col="0" row="0">{{alarm.name}}</Label>
             <Switch col="1" row="0" :checked="alarm.active" />
             <ActivityIndicator col="2" row="0" :busy="alarm.active"/>
             <Label colSpan="3" row="1" class="list-element-divider"></Label>
          </GridLayout>
     </v-template>
</RadListView>
  • 第二个是影响最大的一个。尽可能为组件设置高度。这里使用*,auto,autowill 意味着它必须为列表的每个元素重新计算。这就是IOS和Android不同的地方。

IOS 将仅重新呈现元素中需要的内容。这意味着当元素被回收时,堆栈布局和网格布局将保持不变,但如果网格布局的内容在较低的元素中会更大,那么您滚动的越多,IOS上的渲染可能就会完全错误。这确实更快,因为无需每次调整高度。但是如果尺寸需要改变可能会很糟糕。

Android 倾向于回收,但会重新渲染所有组件,因此会为每个组件重新计算。这对于大小更改来说更慢但更安全。

一般来说,设置和高度会提高应用程序的性能,因为渲染引擎需要做的推理更少。这在您的应用程序中无处不在,但在 rad 列表中非常重要。

于 2020-08-12T14:12:28.483 回答