【问题标题】:NativeScript-Vue rendering RadListView very slow under android and freeze the UINativeScript-Vue在android下渲染RadListView很慢并冻结UI
【发布时间】:2025-11-29 00:30:01
【问题描述】:

我有一个带有 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-core-modules 5.3.1

类似问题报告

我查看了 * 和 github,但那里的问题(例如 NativeScript Angular RadListView rendering extremely slowly)看起来相似,但解决方案不适合我。

我使用 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)上尝试相同的应用程序,渲染速度非常快,并且 UI 运行流畅,即使有 10000 个元素等等。

想法?

有人知道如何加快渲染速度吗?如果没有,是否有建议我如何构建动画(例如 ActivityIndi​​cator)来向用户显示设备正在工作?当我在 UI 上放置 ActivityIndi​​cator 时,由于 UI 冻结,用户无法看到它。

【问题讨论】:

  • ListView 的全部目的是减少加载时间,因为它实际上不会一次渲染 10k 个项目。在我的设备上渲染列表需要一秒/不到一秒,我正在使用带有 Android 8.1 的 Moto G5 Plus。如果您从模板中删除活动指示器,是否会有所不同?
  • 感谢@Manoj 的建议。我删除了 ActivityIndi​​cator 和 Switch,但在我的慢速设备上,渲染仍然很慢。看来有无Switch+ActivityIndi​​cator没有时间上的区别。
  • 我添加了一个赏金,因为我们在 Android 上看到了类似的东西,但在 iOS 上没有。 @TeHa,你有没有做任何事情来改善你在 Android 上的缓慢渲染?
  • @clay,不抱歉。我最终切换到了 Nativescript + Angular。即使在 Android 下,“相同”的代码也快得多,因为我在 iOS 下编写的早期 Vue 没有问题。我对 Vue + Android 的最后看法表明,GUI 元素的内部生成速度很慢。

标签: nativescript nativescript-vue nativescript-telerik-ui radlistview


【解决方案1】:

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,auto 意味着它必须为列表的每个元素重新计算。这就是 IOS 和 Android 的不同之处。

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

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

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

【讨论】: