Handle Android State Changes in Inherited Custom View

Works with Non-inherited and Inherited Custom View

Photo by Annie Spratt on Unsplash

In Android, there are some behaviors that we called “State Changes” which can occur from many conditions. Whether the platform might kill the application to reclaim the memory for the currently active app or Configuration Changes which recreated the Context in running Activity with the new configurations. To keep the data in your application from these changes, we have to handle the state in each Activity, Fragment and View.

Using existing Views from Android Frameworks or 3rd-party Libraries (which handle the state changes correctly), the state changes will be handled by itself. But when you create your own (called “Custom View”) you have to put the additional code for handling the state changes in your Custom View.

For example, my PostView contains 3 states (title, description and dividerColorResId) inside.

To support the state changes, I have to implement onSaveInstanceState and onRestoreInstanceState and handle all states in there.

As you see, handling state changes, you need SavedState which extends from BaseSavedState to transform the data to Parcelable when onSaveInstanceState called, and onRestoreInstanceState to restore the data back from Parcelable.

But the Android system does not hold the Parcelable while the state changes directly. Parcelable will be converted to Parcel and held in the system, and convert back to Parcelable when state restoring. That is why we have to declare the CREATOR which create from Parcelable.Creator to convert Parcel back to SavedState (Parcelable) by createFromParcel

This code is a popular example of handling state changes in Custom View.

Unfortunately, it is not working with Inherited Custom View.

Inherited Custom View, How?

Instead of a single (non-inherited) Custom View, separated the code into a base class and derived class.

The code in BasePostView will be

And RegularPostView will be

These 2 classes handle state changes in the same way with a single Custom View. title and description handled by BasePostView and dividerColorResId handled by RegularPostView.

And everything seems to work well until the app crashes while state changes by the application process.

BasePostView.SavedState not found in RegularPostView when restoring from state change by the application process

What is going on?

Using BaseSavedState for SavedState and Parcelable.Creator for CREATOR in Inherited Custom View only works well when the state changes by configuration changes, not state changes by the application process.

In Android, when users put your app in the background for a long time, the Android system might reclaim the memory by killing your application’s process. But also be able to restore the data when the user opens your app again.

When restored from state changes by the application process, ClassLoader in CREATOR is required for Parcel to Parcelable converting. But there is no ClassLoader in the above example. So RegularPostView can not restore the state in BasePostView(from super).

Restoring from state changes by the application process, ClassLoader in CREATOR is required for Parcel to Parcelable converting. But there is no ClassLoader in the above example. So RegularPostView can not restore the state in BasePostView (from super).

To test your app in this behavior, just put your app into background, Click at Terminates selected Android Application in Android Studio’s Logcat, then re-open your app again.

Before talking about how to solve this problem. Let’s talk about…

Why am I using the Inherited Custom View instead of a single Custom View?

From the previous example code, you might say “Just keep all code in one class because it’s Custom View, nothing more”.

My reason for using the Inherited Custom View is the RoundCornerProgressBar library for the customized progress bar.

In this library, I created 6 types of progress bars with some logic in common. So using Inherited Custom View is more suitable to maintain it in the future. (reusable + maintainable = 👍👍👍)

How I designed the class structure in RoundCornerProgressBar library

All classes also have their own states to handle. So BaseSavedState and Parcelable.Creator do not work for my library anymore.

Then, let’s get back to the previous code.

To solve this problem in Inherited Custom View

First, replace the BaseSavedState with AbsSavedState (which allows you to ensure the state of all classes along the hierarchy is saved) to all classes in Custom View’s hierarchy.

AbsSavedState also has a constructor(source: Parcel, loader: ClassLoader?) which supports ClassLoader. And must be AbsSavedState from androidx.customview.view.AbsSavedState for backward compatibility because the constructor of android.view.AbsSavedState is available in API level 24 or higher.

androidx.customview.view.AbsSavedState is in the CustomView library of Android Jetpack libraries and also included in the AppCompat library.

Second, replace the Parcelable.Creator with Parcelable.ClassLoader (which allows the Creator to receive the ClassLoader the object is being created in) to all classes in Custom View’s hierarchy.

Then, test the Custom View in every state changes behavior again. And it finally works! 🎉🎉🎉

Conclusion

To handle state changes in Inherited Custom View, Using AbsSavedState and Parcelable.ClassLoaderCreator which allows us to save/restore the state of all classes along the hierarchy.

Even the single Custom View, if your project contains both Custom Views, using AbsSavedState and Parcelable.ClassLoaderCreator in all of them is better than separating the code style between non-inherited and inherited.

No Inherited Custom View in your project? Don’t worry. BaseSavedState and Parcelable.Creator still work well for your Custom View, no need to replace them.

Finally, the example code of this article. See the link below

References

--

--

--

Experts on various Google products talking tech.

Recommended from Medium

ASYNC TASK CONCEPTS AND IMPLEMENTATION

Resolving FlutterJNI not attached to native — Flutter Android Exception

Singleton Class in Kotlin [2022]

Now in Android #27

2.4 Making the Data Accessible to OpenGL

images/StartingAirHockey/VertexArrays.png

Permanent Data I/O in Flutter: Adding “Starre d” Comics

images/Networking/ComicPageNonFavorite.png

Now in Android #26

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Akexorcist

Akexorcist

Lovely android developer who enjoys learning in android technology, habitual article writer about Android development for Android community in Thailand.

More from Medium

Some Best Practices for Android App Architecture

Accessing App Usage History In Android

Kotlin/Gradle/JUnit starter project

Android Annotation Processing Review&Introduce