在Compose NavHost外部使用toRoute获取路由对象失败求助
问题原因
你遇到的IllegalArgumentException: Polymorphic value has not been read for class null异常,本质是在NavHost外部调用toRoute<Route>()时,kotlinx.serialization的多态序列化上下文未正确配置。
NavHost内部的composable<Route> lambda中,导航库会自动注入包含Route多态信息的序列化上下文,因此toRoute可以正常解析。但在NavHost外部,这个上下文不存在,导致序列化器无法识别密封接口的子类,抛出解析异常。
解决方案
方案1:手动解析路由(推荐)
直接从NavBackStackEntry的参数中取出路由的序列化字节数据,使用自定义的序列化模块手动解析:
- 先定义包含
Route多态信息的序列化模块:
val routeModule = SerializersModule { polymorphic(Route::class) { subclass(Route.Route1::class) subclass(Route.Route2::class) subclass(Route.Route3::class) } }
- 修改路由获取逻辑:
val currentRoute = currentBackStackEntry?.let { entry -> // 获取导航库存储路由的key val routeKey = NavType.SerializableType(Route::class.java).name // 取出序列化后的字节数组 val routeBytes = entry.arguments?.getByteArray(routeKey) routeBytes?.let { bytes -> // 用配置好的序列化模块解析 Json { serializersModule = routeModule } .decodeFromString(String(bytes)) as Route } }
方案2:通过LaunchedEffect延迟获取
利用LaunchedEffect监听currentBackStackEntry的变化,在协程中调用toRoute(此时导航上下文已初始化),并将结果存入状态变量:
@Composable fun Content( navController: NavHostController = rememberNavController() ) { val currentBackStackEntry by navController.currentBackStackEntryAsState() var currentRoute by remember { mutableStateOf<Route?>(null) } // 监听backStack变化,在协程中安全调用toRoute LaunchedEffect(currentBackStackEntry) { currentBackStackEntry?.let { entry -> currentRoute = entry.toRoute<Route>() } } NavHost(navController = navController, startDestination = Route.Route1){ composable<Route.Route1> { } composable<Route.Route2> { } composable<Route.Route3> { } } }
方案3:封装自定义扩展函数
把手动解析逻辑封装成扩展函数,方便复用:
private val routeJson = Json { serializersModule = SerializersModule { polymorphic(Route::class) { subclass(Route.Route1::class) subclass(Route.Route2::class) subclass(Route.Route3::class) } } } fun NavBackStackEntry.customToRoute(): Route? { return try { val routeKey = NavType.SerializableType(Route::class.java).name val routeBytes = arguments?.getByteArray(routeKey) routeBytes?.let { routeJson.decodeFromString(String(it)) as Route } } catch (e: Exception) { null // 捕获解析异常,避免崩溃 } }
使用时直接调用:
val currentRoute = currentBackStackEntry?.customToRoute()
完整示例代码
import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.navigation.NavBackStackEntry import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule import kotlinx.serialization.modules.polymorphic import kotlinx.serialization.modules.subclass // 定义多态序列化模块 val routeModule = SerializersModule { polymorphic(Route::class) { subclass(Route.Route1::class) subclass(Route.Route2::class) subclass(Route.Route3::class) } } @Composable fun Content( navController: NavHostController = rememberNavController() ) { val currentBackStackEntry by navController.currentBackStackEntryAsState() // 手动解析当前路由 val currentRoute = currentBackStackEntry?.let { entry -> val routeKey = NavType.SerializableType(Route::class.java).name val routeBytes = entry.arguments?.getByteArray(routeKey) routeBytes?.let { bytes -> Json { serializersModule = routeModule } .decodeFromString(String(bytes)) as Route } } NavHost(navController = navController, startDestination = Route.Route1){ composable<Route.Route1> { } composable<Route.Route2> { } composable<Route.Route3> { } } } @Serializable sealed interface Route { @Serializable data object Route1 : Route @Serializable data object Route2 : Route @Serializable data object Route3 : Route }
内容的提问来源于stack exchange,提问作者Inidam Leader
相关产品推荐
相关产品推荐

