Kotlin - 實現Android中的Parcelable

使用 Parcelable 的最基本用例是當我們需要將模型從一個活動傳遞到另一個活動時。當傳遞原始類型時,很簡單,但是當我們想要傳遞自己的對象時,我們需要對它們做一些事情:
class ActivityA : Activity() {

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

        val intent = Intent(this, ActivityB::class.java)
        val person = Person("name", 32, "email@email.com", 1234)

        intent.putExtra("A_STRING", "some string")
        intent.putExtra("A_NUMBER", 1234)
        intent.putExtra("AN_OBJECT", person) // compilation error

        startActivity(intent)
    }
}

如果我們認為我們使用的 Person 對象與上一篇文章相同,則會在第11行收到編譯錯誤,因為我們無法照原樣傳遞 Person 模型。



我們的模型不符合以上任何條件,因此我們需要對此做些事情,我們有一些選擇:

  • Implement Serializable:希望您不再這樣做,儘管它有效且易於實施,但由於它基於反射,因此性能相當差。
  • Json String representation您也可以這樣做,並將模型作為String傳遞。同樣,這也非常簡單,特別是如果您已經Gson在項目中使用了類似功能,但又不是最佳選擇。
  • Implement Parcelable:這顯然是正確的答案。根據官方文檔,這是推薦的方式。它也適用於   marshalling/unmarshallingJava 對象,例如序列化,但性能更高。

因此,Parcelable是最好的選擇,但是不幸的是,它涉及很多樣板代碼,每次我們對模型進行更改時,我們所有人都必須編寫和更新這些樣板代碼。在Java中,如果我們使用AutoValue,就可以避免編寫和維護所有樣板代碼,但是在Kotlin中呢?

接下來,讓我們使用上一篇文章中的Person模型,看看如何在Kotlin中實現Parcelable。如果您還記得,這就是現在的樣子,只有一行代碼。
data class Person(val name: String, val age: Int, val email: String, val phone: Long)


標準方式

首先,讓我們採用標準方法,看看它在Kotlin中的外觀。因此,如果我們使Person模型實現Parcelable接口並要求Android Studio寫下所有必需的代碼,那麼我們將得到:
data class Person(val name: String, val age: Int, val email: String, val phone: Long) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readInt(),
        parcel.readString(),
        parcel.readLong())

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeInt(age)
        parcel.writeString(email)
        parcel.writeLong(phone)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Person> {
        override fun createFromParcel(parcel: Parcel): Person {
            return Person(parcel)
        }

        override fun newArray(size: Int): Array<Person?> {
            return arrayOfNulls(size)
        }
    }
}
如前所述,其中涉及很多樣板代碼。我們可以通過使用Android Studio生成模板來避免最初編寫它,但是我們仍然必須維護它。對於這麼小的模型,我們從1行代碼增加到28行。在更現實的示例中,當我們需要添加屬性時,會發生什麼?例如,如果要向Person模型添加address屬性,則需要至少更新constructor和writeToParcel方法並添加新字段,以便我們的模型仍符合Parcelable實現。

Parcelize

打包救助來了。JetBrains在Kotlin 1.1.4版本中引入了它,這是什麼?在他們的話:
自動 打包實現生成器。在主要構造函數中聲明序列化的屬性,並添加一個   @Parcelize 註釋,以及 writeToParcel()/createFromParcel() 方法將自動創建

目前這是一項實驗性功能,因此我們需要將以下代碼段添加到我們的應用程序build.gradle文件中:
androidExtensions {
 experimental = true
}
這是我們的模型現在的樣子:
@Parcelize
data class PersonParcelize(val name: String, 
                           val age: Int, 
                           val email: String, 
                           val phone: Long) : Parcelable

太好了吧?沒有更多的可打包樣板代碼。Parcelable實現所需的所有代碼都是由註釋處理器生成的,我們根本不必擔心,也不必首先編寫它,也不必在每次更改模型時進行維護和更新。

遠離28行代碼,回到簡潔易讀的模型。如果您忽略了我為使要點看起來不錯而在這裡執行的換行符,則可以輕鬆地將其包含在兩行代碼中,其中一行用於註釋,另一行用於類定義。


Kotlin再次做了出色的工作,幫助我們擺脫了不必要的樣板代碼。我每天都越來越愛這種語言,您是否有同感?:)

不過,Parcelize仍然存在一個小問題。目前,Android Studio出現問題,顯示有關Parcelable接口的不完整實現的錯誤:


這是IDE本身中的一個已知錯誤,您可以忽略它,代碼沒有錯,並且可以按預期工作。您可以在此處跟踪問題。目前處於In Progress狀態。

注意:AndroidDeveloperLB 在文章的第一條評論中提到了Parcelize的另外兩個問題。其中一個僅影響1.1.4版,另一個也影響1.1.5版。請確保更新到可用的最新Kotlin版本,以免出現這些情況。第一個有點極端情況,因為如果您嘗試在Parcelable中使用Serializable對象並且不確定為什麼我會這樣做,則會發生這種情況。

第二個可能更重要,因為它實際上阻止您在運行Android 4.4以下版本的設備上安裝該應用程序,而這實際上更為常見。

留言

這個網誌中的熱門文章

Android - 使用 adb 安装apk

Android ContentProvider 實現多個應用程式共享資料

Android TextView autosizing 自動調整大小