1

I’m working on an Android app that uses a RecyclerView with a custom Adapter and ViewHolder. The RecyclerView contains EditText views, and the properties of these views (such as text, color, padding, font size, etc.) are dynamically loaded via JNI from a native C++ layer.

The problem I’m encountering is the initial creation of the RecyclerView and how long it takes to populate and render the EditText views. The entire process takes around 100ms. I’m wondering if this is normal for a complex UI like this, or if there are ways to optimize the creation process.

Details:

  1. RecyclerView Setup: The RecyclerView is populated with EditText items where each item’s properties are fetched from the native layer.

  2. Performance Concern: The initial creation (setup, layout, and data population) takes ~100ms, and I'm wondering if there are optimizations I can apply to improve this time.

Code:

class MainActivity : AppCompatActivity() {

     private lateinit var recyclerView: RecyclerView
     private lateinit var adapter: EditTextAdapter

    companion object {
        init {
             System.loadLibrary("recyclerviewtest")
        }

        external fun nativeGetItemCount(): Int
        external fun nativeGetStringProperty(index: Int, propId: Int): String
        external fun nativeGetIntProperty(index: Int, propId: Int): Int
        external fun nativeGetFloatProperty(index: Int, propId: Int): Float
    }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    val start = System.nanoTime()

    recyclerView = RecyclerView(this).apply {
        layoutManager = LinearLayoutManager(this@MainActivity)
    }

    adapter = EditTextAdapter(this)
    recyclerView.adapter = adapter

    val rootLayout = LinearLayout(this).apply {
        orientation = LinearLayout.VERTICAL
        addView(recyclerView, LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0, 1f))
    }

    setContentView(rootLayout)

    // Initialize data and trigger first data fetch
    adapter.setInitialData(nativeGetItemCount())

    val durationMs = (System.nanoTime() - start) / 1_000_000.0
    Log.d("Performance", "Create ALL duration (including layout): $durationMs ms")
}

}

class EditTextAdapter(private val context: Context) : RecyclerView.Adapter<EditTextViewHolder>() {
private val items = mutableListOf<ItemProps>()

fun setInitialData(count: Int) {
    items.clear()
    for (i in 0 until count) {
        items.add(loadProps(i)) // Load data from JNI
    }
    notifyItemRangeInserted(0, items.size)
}

private fun loadProps(index: Int): ItemProps {
    return ItemProps(
        text = MainActivity.nativeGetStringProperty(index, 0),
        textColor = MainActivity.nativeGetIntProperty(index, 1),
        paddingTop = MainActivity.nativeGetIntProperty(index, 2),
        paddingLeft = MainActivity.nativeGetIntProperty(index, 3),
        fontSize = MainActivity.nativeGetFloatProperty(index, 4),
        backgroundColor = MainActivity.nativeGetIntProperty(index, 5)
    )
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): EditTextViewHolder {
    return EditTextViewHolder(context)
}

override fun onBindViewHolder(holder: EditTextViewHolder, position: Int) {
    holder.bind(items[position])
}

override fun getItemCount(): Int = items.size

}

Performance Observations:

  • The initial creation of the RecyclerView (with all EditText views) takes around 100ms to complete.
  • Data is fetched from JNI for each EditText item during the initial population of the RecyclerView.

Questions:

  • Is ~100ms for the initial creation and population of a RecyclerView with hundreds of EditText views considered normal?

  • What optimizations can be applied to speed up the initial setup?

4
  • 1
    How many JNI calls are occurring? Just making sure you've seen: developer.android.com/training/articles/perf-jni#general-tips , specifcally Minimize marshalling of resources across the JNI layer Commented Jul 28 at 9:55
  • Even if I discard the JNI calls and keep some hardcoded values instead its almost similar time @MorrisonChang Commented Jul 28 at 11:16
  • @RohanPande - Have you tried comparing this in compose by using a lazyColumn? Not sure it will bring an improvement for the initial rendering time, but it could. Commented Jul 28 at 13:43
  • @tomerpacific Given that compose is less performant than views in general (it just generally doesn't matter), that's unlikely. Commented Jul 28 at 13:53

1 Answer 1

1

Stop front loading all your data on the UI thread. Launch a separate thread (or coroutine) to load the data. Have it update the UI when done. Everything your UI is doing should be fast- except the unknown amount of time needed to load N items from JNI. Depending on how you want to do it, either have each item load one at a time in a coroutine (havign them added dynamically as the coroutine process them so you get a growing list), or show a spinner until the coroutine is done and adds all N at once by calling the notify function.

Honestly I'd rearchitect your JNI layer as well. Don't make 4 calls for 6 properties for a single item. Make it one call that returns an ItemProps instance. Or better than that, one call that returns an array of ItemProps.

Honestly your Kotlin/Java side of your JNI interface is alsopoorly architected. Those functions should NOT be functions on the MainActivity. They should either be on an ItemManager object, or better yet on ItemProp itself. You should NEVER be directly calling functions on your Activity, at most it should pass callbacks to other classes.

In fact that would probably be a better saving then the above thread thing- make ItemProp a class that takes only a single parameter (the item number). Make each property be a getter that access it via JNI. You can add caching if needed. That would load the data lazily only as needed. Which for anything not on screen, isn't needed until it is.

Sign up to request clarification or add additional context in comments.

5 Comments

I just have a small doubt here -- Never tried coroutine but I feel its just to make asynchronous operations when we are trying to do heavy computations. But setting a property on a UI Elements how is it possible on without Main Thread? Would be very helpful if you provide a small example.
You load the data on another thread (either via threads or a coroutine) then you pass back control to the UI thread via runOnUiThread() or via launching a corouting on the main thread.
We are already loading the data on Worker thread and you can assume the data is fetched in optimal way. My question was regarding setting the data on Main Thread is causing this amount of time and how can we reduce the time here?
No you're not. Not in the code you posted.You're doing it all on the main thread. Even if the C code you're using is launching a thread, the fact you're calling it synchronously in the kotlin layer makes that pointless.
Just for your clarification I create my structure (Cpp) already on the worker thread and later on when it comes to the aspect of painting on screen it would just access that structure from kotlin. I have not added that part of code as its regarding the data model. That's the reason I said you can assume fetching of data is done in optimal time. My doubt was how can I reduce my time in rendering?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.