Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

NativeScript-Vue rendering RadListView very slow under android and freeze the UI

I have a nativescript-vue app with the RadListView component to display data to the user. Each row of the list include multiple informations for the current item. When i tap on a button to load and show the list the UI freeze (fast hardware -> short; slow hardware -> long). I figured out that the code part to load/combine the data run very short but the nativescript-internal rendering or creation of the UI elements are the problem. The android console show the information

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

Platform info:

  • tns-ios 5.2.0
  • tns-android 5.2.1
  • nativescript-ui-listview 6.2.0
  • tns-core-modules 5.3.1

Similar problem reports

I look at stackoverflow and github but the problems there (e.g. NativeScript Angular RadListView rendering extremely slowly) looks similar but the solutions not suitable for me.

I use the LinearListView (not Grid nor Stagged) and maybe in my real app i will be able to simplify the row elements from the list but in my sample app (look down) i use a simple row ui design.

Sample app

For a simpler and better report i created a sample app on the {N}-Playground. The app creates 10000 array elements and set them as source of the RadListView which has a Label, a Switch and an ActivityIndicator for every element.

<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>

In the first step the 10000 elements will be generated in a temporary array:

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

With a second button set the temporary array as source:

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

Note: I use 10000 elements to make the problem visible even the sample app runs on a S9 or another high end smartphone.

The full source is runnable under https://play.nativescript.org/?template=play-vue&id=Td1GWR

A slightly different version with an ObservableArray instead of a plain array is under https://play.nativescript.org/?template=play-vue&id=5BXOFG

In both versions the data handling is fast but as soon the UI elements generated from the internal functions the UI will be frozen.

Sample: On my Nexus 7 2013 with Android 6.x the UI freeze nearly 6 seconds and no other apps running in background.

iOS

If i try the same apps on an iOS device (e.g. iPhone 7s) the rendering is very fast and the UI running smooth even so with the 10000 elements and more.

Ideas?

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.

like image 257
TeHa Avatar asked Apr 26 '19 20:04

TeHa


1 Answers

Android and IOS have a very different way of rendering the ui. This difference added to particularity of the Rad list can affect performance. Note that the rad list reuse the instance of the list element to improve performance. This reuse is not done the same way in IOS and in Android.

There are two simple optimizations that can improve a lot the rendering in ios and android.

  • The first one is to reduce the layouts in layouts. It is easier for the rendering if there are less elements. This reduce the depth/passes rendering
<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>
  • The second is the one that have the most impact. Set an height to component when possible. Here the use of *,auto,auto will means that it will have to recalculate for each elements of the list. And here is where IOS and Android differs.

IOS will re render only what is required in the element. This means that the stack layout and the grid layout will stay the same when the element is recycled but if the content of the grid layout would be bigger in lower elements, the rendering on IOS may be all wrong the more you scroll. This is really faster because no need to adjust the height each time. But can be bad if size need to change.

Android tends recycle but to rerender all the components and therefore recalculate for each. This is slower but safer for size change.

In general, setting and height will boost the performance of the app because there are less inference form the rendering engine that need to be done. This is true everywhere in your app but really important in rad list.

like image 89
Plpicard Avatar answered Oct 24 '22 07:10

Plpicard