如何在Jetpack Compose类型安全导航(Navigation 2.8.0+)中传递自定义Parcelable对象?
Jetpack Compose Navigation 2.8.0支持用@Serializable数据类实现类型安全路由,但默认无法处理路由类中包含自定义Parcelable对象的场景。比如你定义的密封路由类:
@Serializable sealed class Route { @Serializable data class DetailsScreen(val userData: UserData) : Route() }
其中UserData是@Parcelize标记的自定义Parcelable类,下面提供两种可行的解决方案:
方案一:自定义NavType + NavTypeSerializer(推荐,保留Parcelable传递特性)
1. 实现自定义NavType
创建处理UserData的NavType子类,负责Bundle中Parcelable对象的读写和字符串解析(支持Deep Link):
class UserDataNavType : NavType<UserData>(isNullableAllowed = false) { override fun get(bundle: Bundle, key: String): UserData? { return bundle.getParcelable(key) } override fun parseValue(value: String): UserData { // 从Base64字符串解析为Parcelable val bytes = Base64.decode(value, Base64.DEFAULT) val parcel = Parcel.obtain() parcel.unmarshall(bytes, 0, bytes.size) parcel.setDataPosition(0) val userData = UserData.CREATOR.createFromParcel(parcel) parcel.recycle() return userData } override fun put(bundle: Bundle, key: String, value: UserData) { bundle.putParcelable(key, value) } override val name: String get() = "UserData" }
2. 注册NavTypeSerializer
创建NavTypeSerializer实例,将UserData类型与自定义NavType绑定:
@NavTypeSerializer object UserDataNavTypeSerializer : NavTypeSerializer<UserData> { override val navType: NavType<UserData> = UserDataNavType() }
3. 使用类型安全路由
直接在导航图中使用类型安全的路由定义,跳转时传递完整的Route对象即可:
// 导航图定义 NavHost(navController = navController, startDestination = ...) { composable<Route.DetailsScreen> { backStackEntry -> val route = backStackEntry.toRoute<Route.DetailsScreen>() DetailsScreen(route.userData) } } // 跳转调用 navController.navigate(Route.DetailsScreen(userData))
方案二:给Parcelable添加Kotlinx Serialization序列化器
通过将Parcelable对象序列化为Base64字符串,让导航组件以字符串形式传递数据:
1. 给UserData添加自定义序列化器
@Parcelize @Serializable(with = UserDataSerializer::class) data class UserData(val id: String, val name: String) : Parcelable
2. 实现Kotlinx Serialization序列化器
object UserDataSerializer : KSerializer<UserData> { override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("UserData", PrimitiveKind.STRING) override fun serialize(encoder: Encoder, value: UserData) { // 将Parcelable转为Base64字符串 val parcel = Parcel.obtain() value.writeToParcel(parcel, 0) val bytes = parcel.marshall() parcel.recycle() encoder.encodeString(Base64.encodeToString(bytes, Base64.DEFAULT)) } override fun deserialize(decoder: Decoder): UserData { // 将Base64字符串转回Parcelable val bytes = Base64.decode(decoder.decodeString(), Base64.DEFAULT) val parcel = Parcel.obtain() parcel.unmarshall(bytes, 0, bytes.size) parcel.setDataPosition(0) val userData = UserData.CREATOR.createFromParcel(parcel) parcel.recycle() return userData } }
3. 直接使用类型安全路由
完成序列化器定义后,无需额外配置,直接使用类型安全的导航API即可,导航组件会自动处理UserData的序列化与反序列化。
内容的提问来源于stack exchange,提问作者Michael B
相关产品推荐
相关产品推荐

