1. Testing the effectiveness of different Android Tools in detecting performance issues caused by NestedViewHierarchy issues on Android

Abhishek Luthra
12 min readDec 17, 2022

--

1.1 Introduction

There are multiple phases of measure, layout and draw involved in rendering views on the device screen. Every phase requires top-down traversal of the view tree. Therefore, the more a layout is nested and the more is the height of the view hierarchy, the more computation power will be needed in rendering the layout. If the layout can be flattened out without changing the final appearance of the UI to the screen, it can help reduce the computation power needed to render the layout and can free out the main thread for other important tasks.

1.2 Code Implementation of Test

As part of testing the effectiveness of different tools on the nested view hierarchy problem, we created two layouts with the same visual appearance as can be seen in Figure 36 and Figure 37. One layout(Code 8) is nested and is created using LinearLayout and RelativeLayout. The other layout(Code 9) is created using constraint layout and is non-nested.

1.2.1 NestedViewHierarchyTestActivty.java

package com.example.perforamancetest.nestedviewhierarchy

import android.annotation.SuppressLint
import android.os.AsyncTask
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.FrameMetrics
import android.view.View
import android.view.ViewGroup
import android.view.Window
import android.widget.Button
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.example.nestedviewoverdraw.R
import java.lang.ref.WeakReference

class NestedViewHierarchyTestActivty : AppCompatActivity() {

private val frameMetricsHandler = Handler()

private val frameMetricsListener = Window.OnFrameMetricsAvailableListener {
_, frameMetrics, _ ->
val frameMetricsCopy = FrameMetrics(frameMetrics)
// Layout measure duration in Nano seconds
val layoutMeasureDurationNs = frameMetricsCopy.getMetric(FrameMetrics.LAYOUT_MEASURE_DURATION)

Log.d(TAG, "layoutMeasurementDurationNs: " + layoutMeasureDurationNs)
}

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_nested_view_hierarchy)

val traditionalNestedCalcButton = findViewById<Button>(R.id.button_start_calc_traditional)
val constraintNonNestedCalcButton = findViewById<Button>(R.id.button_start_calc_constraint)
val textViewFinish = findViewById<TextView>(R.id.textview_finish)
traditionalNestedCalcButton.setOnClickListener {
@SuppressLint("InflateParams")
constraintNonNestedCalcButton.visibility = View.INVISIBLE
val container = layoutInflater
.inflate(R.layout.activity_nested_traditional, null) as ViewGroup
val measurementAsyncTask = MeasurementLayoutAsyncTask(
getString(R.string.executing_nth_iteration),
WeakReference(traditionalNestedCalcButton),
WeakReference(textViewFinish),
WeakReference(container))
measurementAsyncTask.execute()
}

constraintNonNestedCalcButton.setOnClickListener {
@SuppressLint("InflateParams")
traditionalNestedCalcButton.visibility = View.INVISIBLE
val container = layoutInflater
.inflate(R.layout.activity_nonnested_constraintlayout, null) as ViewGroup
val measurementAsyncTask = MeasurementLayoutAsyncTask(
getString(R.string.executing_nth_iteration),
WeakReference(constraintNonNestedCalcButton),
WeakReference(textViewFinish),
WeakReference(container))
measurementAsyncTask.execute()
}
}

override fun onResume() {
super.onResume()
window.addOnFrameMetricsAvailableListener(frameMetricsListener, frameMetricsHandler)
}

override fun onPause() {
super.onPause()
window.removeOnFrameMetricsAvailableListener(frameMetricsListener)
}

/**
* AsyncTask that triggers measure/layout in the background. Not to leak the Context of the
* Views, take the View instances as WeakReferences.
*/
private class MeasurementLayoutAsyncTask(val executingNthIteration: String,
val startButtonRef: WeakReference<Button>,
val finishTextViewRef: WeakReference<TextView>,
val containerRef: WeakReference<ViewGroup>) : AsyncTask<Void?, Int, Void?>() {

override fun doInBackground(vararg voids: Void?): Void? {
for (i in 0 until TOTAL) {
publishProgress(i)
try {
Thread.sleep(100)
} catch (ignore: InterruptedException) {
// No op
}

}
return null
}

override fun onProgressUpdate(vararg values: Int?) {
val startButton = startButtonRef.get() ?: return
startButton.text = String.format(executingNthIteration, values[0], TOTAL)
val container = containerRef.get() ?: return
// Not to use the view cache in the View class, use the different measureSpecs
// for each calculation. (Switching the
// View.MeasureSpec.EXACT and View.MeasureSpec.AT_MOST alternately)
measureAndLayoutExactLength(container)
measureAndLayoutWrapLength(container)
}

override fun onPostExecute(aVoid: Void?) {
val finishTextView = finishTextViewRef.get() ?: return
finishTextView.visibility = View.VISIBLE
val startButton = startButtonRef.get() ?: return
startButton.visibility = View.GONE
}

private fun measureAndLayoutWrapLength(container: ViewGroup) {
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
WIDTH,
View.MeasureSpec.AT_MOST)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
HEIGHT,
View.MeasureSpec.AT_MOST)
container.measure(widthMeasureSpec, heightMeasureSpec)
container.layout(0, 0, container.measuredWidth,
container.measuredHeight)
}

private fun measureAndLayoutExactLength(container: ViewGroup) {
val widthMeasureSpec = View.MeasureSpec.makeMeasureSpec(
WIDTH,
View.MeasureSpec.EXACTLY)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(
HEIGHT,
View.MeasureSpec.EXACTLY)
container.measure(widthMeasureSpec, heightMeasureSpec)
container.layout(0, 0, container.measuredWidth,
container.measuredHeight)
}
}

companion object {

private val TAG = "MainActivity"

private val TOTAL = 100

private val WIDTH = 1920

private val HEIGHT = 1080
}
}

1.2.2 activity_nested_view_hierarchy.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<Button
android:id="@+id/button_start_calc_traditional"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_calc_traditional"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="16dp" />

<Button
android:id="@+id/textview_finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:text="@string/finish"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />

<Button
android:id="@+id/button_start_calc_constraint"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_calc_constraint"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintVertical_bias="0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:layout_marginTop="8dp"
app:layout_constraintTop_toBottomOf="@+id/button_start_calc_traditional" />

</androidx.constraintlayout.widget.ConstraintLayout>

1.2.3 activity_nested_traditional.xml

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">

<ImageView
android:src="@drawable/singapore"
android:layout_width="match_parent"
android:layout_height="150dp"
android:id="@+id/imageview_singapore"
android:scaleType="centerCrop"
android:contentDescription="@string/dummy" />

<ImageView
android:src="@drawable/ic_star"
android:layout_width="36dp"
android:layout_height="36dp"
android:id="@+id/favorite"
android:background="@drawable/info_background"
android:padding="5dp"
android:contentDescription="@string/dummy"
android:layout_below="@id/imageview_singapore"
android:layout_alignParentEnd="true"
android:layout_marginTop="-18dp"
android:layout_marginEnd="18dp" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/imageview_singapore" >

<TextView
android:text="@string/singapore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/title"
android:textSize="24sp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp" />

<LinearLayout
android:id="@+id/camera_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_below="@id/title" >

<TextView
android:text="@string/camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:id="@+id/cameraLabel"
android:labelFor="@+id/cameraType"
android:layout_marginStart="16dp" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<EditText
android:id="@+id/cameraType"
android:ems="10"
android:inputType="textPersonName"
android:text="@string/camera_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginTop="8dp"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
</RelativeLayout>
</LinearLayout>

<LinearLayout
android:id="@+id/setings_area"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/camera_area"
android:orientation="horizontal">

<TextView
android:text="@string/settings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:id="@+id/settingsLabel"
android:labelFor="@+id/settings"
android:layout_marginStart="16dp" />

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:inputType="textPersonName"
android:text="@string/camera_settings"
android:ems="10"
android:id="@+id/settings"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginTop="8dp" />
</RelativeLayout>
</LinearLayout>


<TextView
android:text="@string/singapore_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/description"
android:fadingEdge="vertical"
android:ellipsize="end"
android:textSize="15sp"
android:layout_below="@id/setings_area"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="8dp" />
</RelativeLayout>

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true">

<Button
android:text="@string/upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/upload"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp" />

<Button
android:text="@string/discard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/discard"
android:elevation="0dp"
android:layout_marginEnd="8dp" />
</LinearLayout>
</RelativeLayout>

1.2.4 activity_nonnested_constraintlayout.xml

<?xml version="1.0" encoding="utf-8"?>

<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent" >

<ImageView
android:src="@drawable/singapore"
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/header"
android:scaleType="centerCrop"
android:contentDescription="@string/dummy"
app:layout_constraintLeft_toLeftOf="@+id/container"
app:layout_constraintTop_toTopOf="@+id/container"
app:layout_constraintRight_toRightOf="@+id/container"
app:layout_constraintBottom_toBottomOf="@+id/favorite"
android:layout_marginBottom="16dp"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintVertical_bias="0.0" />

<ImageView
android:src="@drawable/ic_star"
android:layout_width="36dp"
android:layout_height="36dp"
android:id="@+id/favorite"
android:background="@drawable/info_background"
android:padding="5dp"
android:contentDescription="@string/dummy"
app:layout_constraintTop_toTopOf="@+id/container"
app:layout_constraintRight_toRightOf="@+id/container"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="@+id/container"
android:layout_marginBottom="16dp"
app:layout_constraintVertical_bias="0.19" />

<TextView
android:text="@string/singapore"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/title"
android:textSize="24sp"
app:layout_constraintLeft_toLeftOf="@+id/container"
android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="@+id/header"
android:layout_marginTop="16dp" />

<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="@string/camera_value"
android:ems="10"
android:id="@+id/cameraType"
app:layout_constraintLeft_toLeftOf="@+id/settings"
app:layout_constraintTop_toBottomOf="@+id/title"
android:layout_marginTop="8dp"
app:layout_constraintRight_toRightOf="@+id/settings" />

<TextView
android:text="@string/camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/cameraLabel"
android:labelFor="@+id/cameraType"
app:layout_constraintLeft_toLeftOf="@+id/container"
android:layout_marginStart="16dp"
app:layout_constraintBaseline_toBaselineOf="@+id/cameraType" />

<TextView
android:text="@string/settings"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/settingsLabel"
android:labelFor="@+id/settings"
app:layout_constraintLeft_toLeftOf="@+id/container"
android:layout_marginStart="16dp"
app:layout_constraintBaseline_toBaselineOf="@+id/settings" />

<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:inputType="textPersonName"
android:text="@string/camera_settings"
android:ems="10"
android:id="@+id/settings"
app:layout_constraintLeft_toRightOf="@+id/settingsLabel"
android:layout_marginStart="6dp"
app:layout_constraintTop_toBottomOf="@+id/cameraType"
android:layout_marginTop="8dp"
app:layout_constraintRight_toRightOf="@+id/description" />

<Button
android:text="@string/upload"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/upload"
app:layout_constraintRight_toRightOf="@+id/container"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="@+id/container"
android:layout_marginBottom="16dp" />

<Button
android:text="@string/discard"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/discard"
android:elevation="0dp"
app:layout_constraintRight_toLeftOf="@+id/upload"
android:layout_marginEnd="8dp"
app:layout_constraintBaseline_toBaselineOf="@+id/upload" />

<TextView
android:text="@string/singapore_description"
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/description"
android:fadingEdge="vertical"
android:ellipsize="end"
android:textSize="15sp"
app:layout_constraintLeft_toLeftOf="@+id/container"
android:layout_marginStart="16dp"
app:layout_constraintTop_toBottomOf="@+id/settings"
android:layout_marginTop="8dp"
app:layout_constraintRight_toRightOf="@+id/container"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toTopOf="@+id/discard"
android:layout_marginBottom="8dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

1.3 Tool Test

1.3.1 LayoutInspector Tool Test

1.3.1.1 LayoutInspector Nested Layout Test

Figure 36. LayoutInspector result for nested layout hierarchy

1.3.1.2 LayoutInspector Non Nested Layout Test

Figure 37. LayoutInspector result for non nested layout hierarchy

1.3.1.3 LayoutInspector Tool Test Result

It is clearly visible from component tree of Figure 36 and Figure 37 that the layout hierarchy for non nested layout is less deeper than nested layout. This clearly indicates that Layout Inspector is effective in testing for nested layout hierarchies .

1.3.2 Debug GPU Overdraw Tool Test

1.3.2.1 Debug GPU Overdraw Nested Layout Test

Figure 38. Debug GPU overdraw result for nested layout hierarchy

1.3.2.2 Debug GPU Overdraw Non Nested Layout Test

Figure 39. Debug GPU overdraw result for non nested layout hierarchy

1.3.2.3 Debug GPU Overdraw Tool Test Result

It is clearly visible from the overdraw areas of nested layout hierarchy(Figure 38) and non nested layout hierarchy(Figure 39) that there is no difference in output given by Debug GPU overdraw developer option provided no extra overdrawing has been added deliberately while converting a nested layout to non nested layout.

1.3.3 Profile GPU rendering Tool Test

1.3.3.1 Profile GPU rendering Nested Layout Test

Figure 40. Debug GPU overdraw result for nested layout hierarchy

1.3.3.2 Profile GPU rendering Non Nested Layout Test

Figure 41. Profile GPU rendering result for non nested layout hierarchy

1.3.2.3 Profile GPU rendering Tool Test Result

It is clearly visible from the rendering bars for nested layout hierarchy(Figure 40) and non nested layout hierarchy(Figure 41) that the rendering bars for non nested layout hierarchy are smaller in height than rendering bars for nested layout hierarchy. This indicates that the profile GPU rendering is effective in detecting the rendering performance issues caused by nested view hierarchy but by just looking at the bar heights, a person can not attribute that the increased bar height is caused by just nested layout hierarchy .

1.3.4 Show View Updates Tool Test

1.3.4.1 Show View Updates Nested Layout Test

The result of applying Show View Updates developer option on nested layout is shown in Reference

1.3.4.2 Show View Updates Non Nested Layout Test

The result of applying Show View Updates developer option on non nested layout is shown in Reference

1.3.4.3 Show View Updates Tool Test Result

It is clearly visible from the video result for nested layout hierarchy[16] and non nested layout hierarchy[17] that there is no significant difference in results of applying this tool to both types of layouts. This indicates that the Show view updates tool is ineffective in detecting the rendering performance issues caused by nested view hierarchy .

1.3.5 Android CPU Profiler Tool Test

1.3.5.1 Android CPU Profiler Nested Layout Test
1.3.5.1.1 Android CPU Profiler Nested Layout Test — System Trace

Figure 42. The CPU profiler system trace visualization for nested layout. This window clearly shows two jank frames.

Figure 43. The CPU profiler system trace — All Frame visualization for nested layout. This window shows all frames created in the current system trace recording.

Figure 44. The CPU profiler system trace — Janky Frame visualization for nested layout. This window shows all janky frames created in the current system trace recording

From Figure 42, we see that the traversal bar coincides with the primary jank bar corresponding to Frame 7273315. So, in order to investigate further, we open details for traversal window by clicking on the traversal bar. The results are shown in Figure 45,

Figure 45. The CPU profiler system trace — traversal event visualization for nested layout. This window shows summary of details for the traversal event.

From the selected event above, it can be seen that the traversal event takes 41.35 ms which is much higher than the frame duration of 16.6 ms. In order to get further details into what is causing this extended time in traversal phase, we further explore the top Down view of the traversal event, which shows that measure phase itself is the biggest time consuming task in traversal phase.

Figure 46. The CPU profiler system trace — traversal event Top Down visualization for nested layout. This window shows summary of what different calls are made in the traversal phase.

It can be found from the Figure 46, that not much information is provided regarding what exactly within the measure call is causing the extended time. This gives an indication into further exploring the Nested layout performance issue using Java/kotlin method tracing further to check the root cause of extended measure time.

1.3.5.1.2 Android CPU Profiler Nested Layout Test — Java/Kotlin Method Trace

Figure 47. The CPU profiler kotlin/java method trace visualization for nested layout. This window clearly shows that measure calls in traversal phase are the biggest contributors in extended cpu time . Additionally, within measure calls, LinearLayout measure call is the sole contributors.

Figure 48. The CPU profiler kotlin/java method trace Top Down visualization for nested layout. This window clearly shows that measure calls in traversal phase are the biggest contributors in extended cpu time . Additionally, within measure calls, LinearLayout measure call is the sole contributors.

Figure 49. The CPU profiler kotlin/java method trace Flame chart visualization for nested layout. This window clearly shows that measure calls in Linear Layout the biggest contributors in extended cpu time within Relative layout(root layout) measure calls.

Above profiler results for system trace and java/kotlin method trace clearly demonstrate that nested layouts are the main culprits for extended frame timings which in turn leads to heavier janky frame. In the next section, we will run the same steps on non nested layout created using constraint layout to see if it is more performant than our nested layout provided the visual appearance of the layout remains the same.

1.3.5.2 Android CPU Profiler Non Nested Layout Test4.1.3.5.2.1 Android CPU Profiler Nested Layout Test — System Trace

Figure 50. The CPU profiler system trace visualization for non nested layout. This window clearly shows one frame with frame duration of 20.21 ms which is slightly above ideal 16.6 ms..

Figure 51. The CPU profiler system trace — All Frame visualization for non nested layout. This window shows all frames created in the current system trace recording.

Figure 52. The CPU profiler system trace — Janky Frame visualization for non nested layout. This window shows all janky frames created in the current system trace recording

From Figure 50, we see that the traversal bar coincides with the primary jank bar corresponding to Frame 594483. So, in order to investigate further, we open details for traversal window by clicking on the traversal bar. The results are shown in Figure 43,

Figure 53. The CPU profiler system trace — traversal event visualization for non nested layout. This window shows summary of details for the traversal event.

From the selected event above, it can be seen that the traversal event takes just 19.42 ms in comparison to 41.35 ms for nested layout which is much higher than the frame duration of 16.6 ms. In order to get further details into what is causing this extended time in traversal phase, we further explore the Top Down view of the traversal event, which shows that measure phase itself is the biggest time consuming task in traversal phase.

Figure 54. The CPU profiler system trace — traversal event Top Down visualization for nested layout. This window shows summary of what different calls are made in the traversal phase.

It can be found from the Figure 54, that not much information is provided regarding what exactly within the measure call is causing the extended time. This gives an indication into further exploring the Nested layout performance issue using Java/kotlin method tracing further to check the root cause of extended measure time.

1.3.5.2.2 Android CPU Profiler Nested Layout Test — Java/Kotlin Method Trace

Figure 55. The CPU profiler kotlin/java method trace visualization for nested layout. This window clearly shows that measure calls in traversal phase are shorter than measure calls as shown in Figure 37.

Figure 56. The CPU profiler kotlin/java method trace Top Down visualization for nested layout. This window clearly shows that measure calls in traversal phase take just around 6 ms in comparison 101 ms for nested layout.

Figure 57. The CPU profiler kotlin/java method trace Flame chart visualization for nested layout. This window clearly shows that measure calls are quite smaller in comparison for nested layout shown in Fig 37.

Above profiler results for system trace and java/kotlin method trace clearly demonstrate that nested layouts are the main culprits for extended frame timings which in turn leads to heavier janky frame and usage of a non nested approach such as contraint layout leads to a big reduction in jank frame reduction .

1.3.5.3 Android CPU Profiler Tool Test Result

It is clearly visible from the CPU profiler result for nested layout hierarchy and non nested layout hierarchy that there is significant reduction in number of jank frames and the duration of frames itself by using non nested layout hierarchy in comparison to nested layout hierarchy.. This indicates that the CPU profiler tool is effective in detecting the rendering performance issues caused by nested view hierarchy .

This clearly shows that there is 50 percent improvement in number of jank frames by using constraintlayout in comparison to nested linear-relative layout. Moreover, there is 43 percent improvement in frame duration for constraint layout in comparison to nested linear-relative layout.

1.3.6 Tool Test Results on Nested View Hierarchy

--

--