有没有一种方便的方法可以使用 Kotlin 在 Android 中创建 Parcelable 数据类?
Posted
技术标签:
【中文标题】有没有一种方便的方法可以使用 Kotlin 在 Android 中创建 Parcelable 数据类?【英文标题】:Is there a convenient way to create Parcelable data classes in Android with Kotlin? 【发布时间】:2016-02-06 17:24:25 【问题描述】:我目前在我的 Java 项目中使用了出色的 AutoParcel,它有助于 Parcelable 类的创建。
现在,我为下一个项目考虑的 Kotlin 具有数据类的概念,它会自动生成 equals、hashCode 和 toString 方法。
有没有一种方便的方法可以方便地使 Kotlin 数据类 Parcelable(无需手动实现方法)?
【问题讨论】:
你试过 kapt 吗? blog.jetbrains.com/kotlin/2015/06/… 您的意思是在 Kotlin 中使用 AutoParcel?我考虑过这一点,但如果有一种方法可以将数据类 Parcelable 内置到语言中,那就太好了。特别考虑到 Kotlin 的很大一部分使用将来自 android 应用程序。 【参考方案1】:Kotlin 1.1.4 不在
Android 扩展插件现在包括一个自动 Parcelable 实现生成器。在主构造函数中声明序列化属性并添加@Parcelize注解,会自动创建writeToParcel()/createFromParcel()方法:
@Parcelize
class User(val firstName: String, val lastName: String) : Parcelable
因此,您需要让他们将其添加到您模块的 build.gradle:
apply plugin: 'org.jetbrains.kotlin.android.extensions'
android
androidExtensions
experimental = true
【讨论】:
想看的朋友:blog.jetbrains.com/kotlin/2017/08/kotlin-1-1-4-is-out 为什么这不再是数据类。这个例子是否只是为了表明这可以应用于任何通用的 kotlin 类? 编译器抱怨this calss implements Parcelable but does not provice CREATOR field
。你的答案够不够(完整)?
@murt 你成功使用 Parcelable 了吗?由于 CREATOR,我面临编译错误
您可以使用@SuppressLint("ParcelCreator")
来消除 lint 警告。【参考方案2】:
你可以试试这个插件:
android-parcelable-intellij-plugin-kotlin
它可以帮助您为 kotlin 的数据类生成 Android Parcelable 样板代码。它最终看起来像这样:
data class Model(var test1: Int, var test2: Int): Parcelable
constructor(source: Parcel): this(source.readInt(), source.readInt())
override fun describeContents(): Int
return 0
override fun writeToParcel(dest: Parcel?, flags: Int)
dest?.writeInt(this.test1)
dest?.writeInt(this.test2)
companion object
@JvmField final val CREATOR: Parcelable.Creator<Model> = object : Parcelable.Creator<Model>
override fun createFromParcel(source: Parcel): Model
return Model(source)
override fun newArray(size: Int): Array<Model?>
return arrayOfNulls(size)
【讨论】:
【参考方案3】:只需点击你的kotlin数据类的data关键字,然后按alt+Enter,选择第一个选项"Add Parceable Implementation"
【讨论】:
我用了这个方法,但是有几个问题。如果字段不是val/var
,有时它会用TODO
替换parcel.read...
。如果你在一个类中使用List
,你的实现就会成为一个问题。所以我在接受的答案中转向@Parcelize
。【参考方案4】:
你试过PaperParcel吗?它是一个注解处理器,可自动为您生成 Android Parcelable
样板代码。
用法:
使用@PaperParcel
注释您的数据类,实现PaperParcelable
,并添加生成的CREATOR
的JVM 静态实例,例如:
@PaperParcel
data class Example(
val test: Int,
...
) : PaperParcelable
companion object
@JvmField val CREATOR = PaperParcelExample.CREATOR
现在您的数据类是Parcelable
,可以直接传递给Bundle
或Intent
编辑:用最新的 API 更新
【讨论】:
IDE 现在显示“禁止从其他类继承数据类”。我认为 PaperParcel 不能再用于数据类... 它们永远不能从其他类继承,但它们可以实现接口(例如 PaperParcelable) @Bradley Campbell 这让我在这一行出现错误 JvmField val CREATOR = PaperParcelExample.CREATOR cannot create PaperParcelExample class【参考方案5】:没有样板代码的最佳方法是Smuggler gradle 插件。您只需要实现 AutoParcelable 接口,例如
data class Person(val name:String, val age:Int): AutoParcelable
仅此而已。也适用于密封类。此插件还为所有 AutoParcelable 类提供编译时验证。
UPD 17.08.2017 现在有了Kotlin 1.1.4 and Kotlin Android extensions plugin,您可以使用@Parcelize
注释。在这种情况下,上面的示例将如下所示:
@Parcelize class Person(val name:String, val age:Int): Parcelable
不需要data
修饰符。目前最大的缺点是使用 kotlin-android-extensions 插件,它有很多其他功能可能是不必要的。
【讨论】:
【参考方案6】: 在模型/数据类之上使用 @Parcelize 注释 使用最新版本的 Kotlin 在您的应用模块中使用最新版本的 Kotlin Android 扩展例子:
@Parcelize
data class Item(
var imageUrl: String,
var title: String,
var description: Category
) : Parcelable
【讨论】:
【参考方案7】:一个更简单和最新的方法是使用@Parcelize
注释,它消除了实现 Parcelable 的艰巨工作
build.gradle(应用模块)
apply plugin: "kotlin-parcelize"
// If you are using the new plugin format use this instead.
plugins
...
id "kotlin-parcelize"
YourClass.kt
import kotlinx.parcelize
@Parcelize
class User(val firstName: String, val lastName: String, val age: Int): Parcelable
【讨论】:
【参考方案8】:使用 Android Studio 和 Kotlin 插件,我找到了一种简单的方法来转换我的旧 Java Parcelable
s,没有额外的插件 (如果您只想将全新的 data
类变成 Parcelable
,请跳到第 4 个代码 sn-p)。
假设您有一个 Person
类,其中包含所有 Parcelable
样板:
public class Person implements Parcelable
public static final Creator<Person> CREATOR = new Creator<Person>()
@Override
public Person createFromParcel(Parcel in)
return new Person(in);
@Override
public Person[] newArray(int size)
return new Person[size];
;
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age)
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
protected Person(Parcel in)
firstName = in.readString();
lastName = in.readString();
age = in.readInt();
@Override
public void writeToParcel(Parcel dest, int flags)
dest.writeString(firstName);
dest.writeString(lastName);
dest.writeInt(age);
@Override
public int describeContents()
return 0;
public String getFirstName()
return firstName;
public String getLastName()
return lastName;
public int getAge()
return age;
首先剥离Parcelable
实现,留下一个简单的、普通的、旧的Java 对象(属性应该是最终的并由构造函数设置):
public class Person
private final String firstName;
private final String lastName;
private final int age;
public Person(String firstName, String lastName, int age)
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
public String getFirstName()
return firstName;
public String getLastName()
return lastName;
public int getAge()
return age;
然后让Code > Convert Java file to Kotlin File
选项发挥它的魔力:
class Person(val firstName: String, val lastName: String, val age: Int)
将其转换为data
类:
data class Person(val firstName: String, val lastName: String, val age: Int)
最后,让我们再次把它变成Parcelable
。将鼠标悬停在类名上,Android Studio 应该会为您提供Add Parcelable Implementation
的选项。结果应如下所示:
data class Person(val firstName: String, val lastName: String, val age: Int) : Parcelable
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.readInt()
)
override fun writeToParcel(parcel: Parcel, flags: Int)
parcel.writeString(firstName)
parcel.writeString(lastName)
parcel.writeInt(age)
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)
如您所见,Parcelable
实现是一些自动生成的代码,附加到您的 data
类定义中。
注意事项:
-
尝试将 Java
Parcelable
直接转换为 Kotlin 不会产生与当前版本的 Kotlin 插件相同的结果( 1.1.3
)。
我不得不删除当前Parcelable
代码生成器引入的一些额外的花括号。应该是小bug。
我希望这个技巧对你有用,对我也有用。
【讨论】:
【参考方案9】:您可以使用@Parcelize
注释来做到这一点。它是一个自动 Parcelable 实现生成器。
首先,您需要允许他们将其添加到您的模块的 build.gradle:
apply plugin: 'org.jetbrains.kotlin.android.extensions'
在主构造函数中声明序列化属性并添加@Parcelize
注解,writeToParcel()
/createFromParcel()
方法将自动创建:
@Parcelize
class User(val firstName: String, val lastName: String) : Parcelable
您不要需要在 androidExtensions
块内添加 experimental = true
。
【讨论】:
【参考方案10】:我会放弃我的做法,以防它可能对某人有所帮助。
我所做的是我有一个通用的Parcelable
interface DefaultParcelable : Parcelable
override fun describeContents(): Int = 0
companion object
fun <T> generateCreator(create: (source: Parcel) -> T): Parcelable.Creator<T> = object: Parcelable.Creator<T>
override fun createFromParcel(source: Parcel): T = create(source)
override fun newArray(size: Int): Array<out T>? = newArray(size)
inline fun <reified T> Parcel.read(): T = readValue(T::class.javaClass.classLoader) as T
fun Parcel.write(vararg values: Any?) = values.forEach writeValue(it)
然后我创建这样的包裹:
data class MyParcelable(val data1: Data1, val data2: Data2) : DefaultParcelable
override fun writeToParcel(dest: Parcel, flags: Int) dest.write(data1, data2)
companion object @JvmField final val CREATOR = DefaultParcelable.generateCreator MyParcelable(it.read(), it.read())
这让我摆脱了样板覆盖。
【讨论】:
【参考方案11】:不幸的是,在 Kotlin 中没有办法在接口中放置一个真实的字段,所以你不能从接口适配器免费继承它:
data class Par : MyParcelable
您可以查看委托,但它对字段没有帮助,AFAIK:https://kotlinlang.org/docs/reference/delegation.html
所以我看到的唯一选项是 Parcelable.Creator
的结构函数,这很明显。
【讨论】:
【参考方案12】:我更喜欢将https://github.com/johncarl81/parceler 库与
一起使用@Parcel(Parcel.Serialization.BEAN)
data class MyClass(val value)
【讨论】:
这给出了错误“类 'MyClass' 不是抽象的并且没有实现抽象成员 public abstract fun writeToParcel(dest: Parcel!, flags: Int): Unit defined in android.os.Parcelable 【参考方案13】:Kotlin 的 @Parcel 注解让 Android 中的整个 Parcelization 过程变得非常简单。
这样做
第 1 步。 在您的应用模块 gradle 中添加 Kotlin 扩展
第 2 步。 添加实验性 = true,因为此功能仍在 gradle 中进行实验。
androidExtensions 实验=真
第 3 步。 使用 @Parcel 标注数据类
Here 是一个关于@Parcel 用法的简单示例
【讨论】:
【参考方案14】:以下 Kotlin 代码可以帮助您使用 Parent 和 Child Data 类创建 Parcelable 数据类
父数据类 - MyPostModel
data class MyPostModel(
@SerializedName("post_id") val post_id: String? = "",
@SerializedName("category_id") val category_id: String? = "",
@SerializedName("images") val images: ArrayList<ImagesModel>? = null
) : Parcelable
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString(),
parcel.createTypedArrayList(ImagesModel.CREATOR)
)
override fun writeToParcel(parcel: Parcel, flags: Int)
parcel.writeString(post_id)
parcel.writeString(category_id)
parcel.writeTypedList(images)
override fun describeContents(): Int
return 0
companion object CREATOR : Parcelable.Creator<MyPostModel>
override fun createFromParcel(parcel: Parcel): MyPostModel
return MyPostModel(parcel)
override fun newArray(size: Int): Array<MyPostModel?>
return arrayOfNulls(size)
子数据类 - ImagesModel
data class ImagesModel(
@SerializedName("image_id") val image_id: String? = "",
@SerializedName("image_url") val image_url: String? = ""
) : Parcelable
constructor(parcel: Parcel) : this(
parcel.readString(),
parcel.readString()
)
override fun writeToParcel(parcel: Parcel, flags: Int)
parcel.writeString(image_id)
parcel.writeString(image_url)
override fun describeContents(): Int
return 0
companion object CREATOR : Parcelable.Creator<ImagesModel>
override fun createFromParcel(parcel: Parcel): ImagesModel
return ImagesModel(parcel)
override fun newArray(size: Int): Array<ImagesModel?>
return arrayOfNulls(size)
【讨论】:
【参考方案15】:有一个插件,但并不总是随着 Kotlin 的发展而更新: https://plugins.jetbrains.com/plugin/8086
替代方案:
我有一个使用Parcelable
和列表的自定义数据类的工作示例:
使用 Parcelable 和 Lists 的数据类:
https://gist.github.com/juanchosaravia/0b61204551d4ec815bbf
希望对你有帮助!
【讨论】:
【参考方案16】:由于没有人提到这一点(或者更确切地说,没有提供有关版本的具体细节),我将对此发表评论:
我使用上述指导尝试了@Parcelize
的所有可能组合,但未能成功。
如果您的 Android 工作室在您的课程中显示 describeContents
或 writeToParcel
未实现的错误,即使使用 @Parcelize
注释,请在您的 app/build.gradle
中检查您没有启用 apply plugin: 'kotlin-android-extensions'
。
这不起作用。出于某种原因,我的项目使用的是旧版本的已弃用扩展 (ktxVersion = '1.0.2')。
所以这与@Parcelize
实现相冲突。
当我尝试添加 apply plugin: 'kotlin-parcelize'
时,它说它不能同时与扩展一起使用,我只能使用其中一个。
在与同事合作后,我们发现扩展程序导致整个 @Parcelize
失败。我尝试了很多不同的东西,但即使在克服编译时错误之后,运行时错误也会说“CREATOR”未找到异常或类似的。
所以最后我删除了kotlin-android-extensions
的apply 插件,只包含了apply plugin: 'kotlin-parcelize'
,它解决了这个问题,它按预期工作。
【讨论】:
以上是关于有没有一种方便的方法可以使用 Kotlin 在 Android 中创建 Parcelable 数据类?的主要内容,如果未能解决你的问题,请参考以下文章