Orientation Change with Kotlin (Parcelable & Data Classes)

0
152
Kotlin Tutorial in Android Studio
Kotlin Tutorial in Android Studio

In this story we are going to review how to properly manage the orientation change in the device, in fact, we are going to see how to handle runtime changes so we don’t lose the already requested news and continue at the same point. At the same time, we are going to see some tricks that we can do with Kotlin in order to make the code more concise, clean and readable.

Orientation Change with Kotlin
Orientation Change with Kotlin

1Parcelable

If you were working with Android for sure you already know about Parcelable. A Parcelable is an interface for classes whose instances can be written to and restored from a Parcel. This is a fast mechanism that Android provides as a IPC (Inter Process Communication) transport and the Parcel is the message container to transfer.

Also, Android provides some lifecycle methods that will allow us to persist temporarily the information required for our classes in a Parcel class while having a runtime change (rotating the device or if the activity is killed by the OS). In our case, we want to save our news from this situations so we will be implementing the Parcelable interface.

Data Classes with Parcelable

Here you have the commit to see all the details to make our data classes implement Parcelable interface.

Let’s review one of the classes:

data class RedditNews(val after: String,
                      val before: String,
                      val news: List<RedditNewsItem>) : Parcelable {

    // 1
    companion object {
        // 2
        @JvmField @Suppress("unused")
        val CREATOR = createParcel { RedditNews(it) } // 3
    }

    // 4
    protected constructor(parcelIn: Parcel) : this(
            parcelIn.readString(),
            parcelIn.readString(),
            mutableListOf<RedditNewsItem>().apply {
                parcelIn.readTypedList(this, RedditNewsItem.CREATOR)
            }
    )

    override fun writeToParcel(dest: Parcel, flags: Int) {
        dest.writeString(after)
        dest.writeString(before)
        dest.writeTypedList(news)
    }

    override fun describeContents() = 0
}

1. Companion object

As you may notice, in Kotlin there is no static keyword to create a static member for a class but exists something called companion objects which allows you to provide the same behavior. When using this keyword we are declaring an object (an instance) directly in the code and all the properties and method inside companion object can be called directly by using the class name:

data class RedditNews(...)
    companion object {
        val ENDPOINT = "http://kotlinlang.org"
    }
}

Use it:

RedditNews.ENDPOINT

We are going to use it to expose the CREATOR function.

2. @JvmField Annotation

Classes implementing the Parcelable interface must also have a non-null static field called CREATOR of a type that implements the Parcelable.Creatorinterface. In order to make our CREATOR implementation visible as a field in Java we need this special annotation called “@JvmField”, otherwise it will not be found and in the middle of the process it will throw an exception.

3. Extension Function for the Creator

I have this useful extension function that creates the CREATOR passing as parameter a function:

val CREATOR = createParcel { RedditNews(it) }

The definition of this extension function is like this:

inline fun <reified T : Parcelable> createParcel(
        crossinline createFromParcel: (Parcel) -> T?): Parcelable.Creator<T> =
        object : Parcelable.Creator<T> {
            override fun createFromParcel(source: Parcel): T? = createFromParcel(source)
            override fun newArray(size: Int): Array<out T?> = arrayOfNulls(size)
        }

3.1) Inline Functions

The inline modifier means that the function itself and the function passed to it, will be inlined into the call site, it means like you were implementing manually this function at every place that you were using it. Behind the scenes, the compiler will be generating specific bytecode at every place that you use it (it’s like “copy & pasting” this function in all the place that you were using it) giving you better performance and avoiding to create new classes for you like a normal extension function could do.

3.2) reified T

If you need to access to a type passed as a parameter, well, reified keyword is here to save you! with the reified modifier now it’s accessible inside the function, almost as if it were a normal class and no reflection is needed!

3.3) crossinline

If you need to execute the function passed as parameter in another context then crossinline allows you to use it in this way and maybe run it also in another thread. In this case, inside the Creator object.

4. Secondary Constructors

We were really accustomed to create classes with primary constructors like this:

data class News(val title: String)

But maybe the primary constructor is not enough specially for scenarios where you need to provide alternatives way to create this class, like in this example that requires a Parcel parameter.

protected constructor(parcelIn: Parcel) : this(...)

Bonus Parcelable

If all of this is really complicated and you want just an easy way to make your data classes parcelables, well you have a great Plugin that will do this for you 😉

This plugin will not use the explained Creator extension function but will generate the creator for you.

2Saving News

Now we need to use the famous onSaveInstanceState to save our news.

The most interest code is this:

news_list.apply {
    setHasFixedSize(true)
    val linearLayout = LinearLayoutManager(context)
    layoutManager = linearLayout
    clearOnScrollListeners()
    ...
    ...
}

Apply is an Extension Function that allows you to execute a code block as you were inside the instance of the object that you are executing the apply function. It’s really useful for cases like this that you don’t want to put all the time “news_list.” every time you need to set a value or call a method from this object.

3Conclusion

We are almost at the end of this series! Hope this story was useful and this new concepts will help you to write better Kotlin apps.

In the Next Part we are going to review how to create Tests with Kotlin.

See you!

Share your thoughts

Loading Facebook Comments ...
Loading Disqus Comments ...