初めに
公開中のアプリに、型安全のNavigationを導入しリリースしたところ、更新した全ユーザーにクラッシュが発生してアプリが起動できなくなってしまいました。
調査
エラーの内容
D7.g: Serializer for class 'k' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
"Serializer対象のクラスが見つかりません。@Serializableを付け、serialization pluginを適用していることを確認してください。"とのことですが、どちらも適用されています。
ただし、クラスが見つからないのと、実装時の動作確認では問題無かったので、Proguard
絡みだと予想されます。
buildTypes {
release {
isMinifyEnabled = false
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
+ debug {
+ isMinifyEnabled = true
+ isShrinkResources = true
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt"),
+ "proguard-rules.pro"
+ )
+ }
}
設定を追加して、ローカルのデバッグビルドでも発生するのか確認します。
kotlinx.serialization.SerializationException: Serializer for class 'SearchGraph' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
デバッグ情報が付与されているため、見つからないというクラス名も出力されていますが、同じエラーが発生したので、こちらが発生しないよう調査、修正を行います。
導入手順の再確認
Kotlin DSL と Navigation Compose における型安全性 | Android Developers
こちらの手順を確認しましたが、こちらも問題ありませんでした。また、Proguard
に関する記述はありませんでした。
Proguard
に関する情報を探すと、Kotlin Serialization
側で見つかりましたが、プラグインを導入すると自動的に適用されるようです。実装方法によっては追加の設定が必要なようですが、そちらにも該当しませんでした。
Kotlin/kotlinx.serialization: Kotlin multiplatform / multi-format serialization
他のアプリで確認
Codelab
やGoogleが公開しているサンプルアプリを探してみましたが、Type Safe Navigation
を使用している物はみつかりませんでしたが、先日投稿した記事で参考にしたサンプルがType Safe Navigation
を使用していたので、そちらのProguard
を有効にして実行してみたところ、エラーは発生せずに起動できました。
原因箇所の特定
Type Safe Navigation
+sealed interface(?)
+Hilt(?)
+Proguard
で、正常に動いているアプリと動かないアプリの設定や実装の違いを比較し、何処が原因なのか調査しました。
結論としては、現在の実装方法では、NavHost
内で、ネストしたナビゲーションnavigation<T>()
を使用せずに、通常のナビゲーションcomposable<T>()
を使用すると、デスティネーションのクラスが見つからなくなるようです。現在の実装方法ではと書いたのは、通常のナビゲーションを使用していても問題無く動作しているアプリもあるのですが、それらのアプリでは、デスティネーションの指定にsealed interface
を使用しなかったり、Hilt
を使用していなかったりするため、そのあたりも影響があるようです。
sealed interface Destination {
// ...
+ @Serializable
+ data object DemoScreen: Destination
}
NavHost(
navController = navController,
startDestination = navigator.startDestination,
modifier = Modifier.padding(innerPadding)
) {
+ composable<Destination.DemoScreen>() {
+ // TODO
+ }
navigation<Destination.AuthGraph>(
startDestination = Destination.LoginScreen
) {
composable<Destination.LoginScreen> {
val viewModel = hiltViewModel<LoginViewModel>()
kotlinx.serialization.SerializationException: Serializer for class 'DemoScreen' is not found.
Please ensure that class is marked as '@Serializable' and that the serialization compiler plugin is applied.
修正方法
その1
デスティネーションを定義しているクラスを最適化の対象から除外する。
+-keep class jp.hamachi.android.img.core.navigation.** {*;}
その2
デスティネーションを定義しているsealed interface
にも@Serializableを付ける。
+@Serializable
sealed interface Destination {
終わりに
今回の場合は上記のどちらの対応でもエラーは発生しなくなりましたが、ネストしたナビゲーションでは元々発生せず、通常のナビゲーションの場合のみ発生する理由がはっきりしないままです。ネストしたナビゲーションを使用することが推奨されていたりするのでしょうか?ご存じの方がいらっしゃいましたらお教えください。
戒め
リリース前には、リリースビルドで動作確認を行いましょう。