Jetpack Compose Best& Bad Practices with Common Usages Part II
As a continuation of the previous article, we continue with Best & Bad Practices Part II , where I talk about what we use in Jetpack Compose, what we should use, what we should not use, where we need to use and why we use it.
Tip 4: Perfection with ‘remember’ against Recomposition and State Survival
Problem: As we know in Jetpack Compose, recomposition is a mechanism where the UI is redrawn in response to state changes. This can occur frequently and unexpectedly, sometimes leading to loss of state if not handled properly.
- Data Integrity Issues, the loss of user-entered or dynamically changing data can lead to integrity issues
- CPU & Memory Usage, redrawing the entire UI structure on every state change can increase CPU and memory usage, leading to longer response times and degraded user experience. Please check this story for more profiler.
Solutions: For this tip our solution is manage recomposition issues effectively, utilizing ‘remember’ in Jetpack Compose to preserve states across recompositions optimizes application performance and user experience. But this is not only one solution we’ll mention for other tips, anyway please check another solutions ;
1.State Hoisting: This involves lifting the state up to a higher level in the composable hierarchy. By managing state in a parent composable or at the ViewModel level, you decouple the state management from the UI logic, which makes the components more reusable and testable.
2.Immutable Data Models : Using immutable data models ensures that the data structures you pass into composables do not change unexpectedly.
3.Use ViewModel with LiveData(think before use this way is non-native) or StateFlow.
4.Proper Use Effects (maybe LaunchedEffect) : This is particularly useful for tasks like logging, analytics, or triggering one-time actions.
Example: Let’s imagine an E-commerce application search screen. It should be protected when the user enters some text and when the user clicks on another UI component or flips the screen landscape/portrait. To manage this state, we can preserve the text state by using remember and mutableStateOf.
Let’s Coding
- SearchBarGoodPractice() , we utilize
remember { mutableStateOf("") }
for managing the search text state. This is good practice as it ensures that the state ofsearchText
is preserved across recompositions caused by UI updates unrelated to the search text. This approach avoids unnecessary resets of the input field, thus maintaining a good user experience and state integrity. - SearchBarBadPractice(), the absence of
remember
in conjunction withmutableStateOf("")
is indeed a bad practice. Withoutremember
, the state ofsearchText
will be reset to its initial value every time the composable recomposes, which is frequent in reactive frameworks like Compose.
P.S. @SuppressLint(“UnrememberedMutableState”) annotation is used to suppress the lint warning that would normally alert developers that the mutable state is not remembered. It’s a clear indicator that the code is consciously circumventing a recommended practice.
Tip 5: Management of “lazy”
Pre-Information : LazyRow and LazyColumn are lazy-loading components that only compose and layout visible items plus a small buffer around them.
Advantages
- It prevents all items from being unnecessarily loaded and kept in memory.
- Loading only visible items optimizes performance and memory usage.
Problem: These components do extra processing to calculate which elements appear on the screen.
- Constantly performs calculations to determine visible items.
- Dynamically manages which elements are loaded or destroyed during the scroll operation.
Solution : These operations may be unnecessary, especially when the list items are very few in number (for example, five or fewer). So, in such cases, using simple Row or Column may be more effective and efficient.
- It loads all items at startup and these items are constantly kept in memory.
- It can seamlessly manage all items without incurring extra performance costs for small data sets.
Example: In most applications in the industry, profile page.
Do not forget
*If list items are not updated frequently and the list size is small (such as five items), using Column or Row may be sufficient and effective.
*If list items change frequently and significantly, such that each item needs to be updated, it may be more appropriate to use LazyColumn or LazyRow.
Let’s Coding
- UserProfileListMoreEffectivePerformancePractice(), we using a standard
Column
and iterating over users withforEach
is effective for small or static lists where the overhead of handling lazy loading is unnecessary. However, this is efficient only for small datasets as every element is recomposed every time there is a state change that affects any element in the list. - UserProfileListPractice(), we utilizing a
LazyColumn
withitems(users) { user -> UserProfileItem(user) }
is the standard practice for handling larger lists or lists where the dataset could change dynamically. This method ensures that only visible items are recomposed, which is more efficient for memory and performance when dealing with large amounts of data.
P.S. As we said before if the data used in forEach changes frequently, this may lead to unnecessary rebuilds of the entire list.
Happy coding! See you in Part III.
Jetpack Compose Best& Bad Practices with Common Usages Part II
For Jetpack Compose Fully Projects
Jetpack Compose with Hexagonal and Clean Architecture
Multi Module( Layer Based)with Clean Architecture & MVVM+MVIÂ
Boost Your Android App with CPU Profiling
Boost Your Android App with and Mastering with Memory Profiling