Kotlin Fragment获取类名返回y0而非PairEVFragment问题求助
问题排查:Fragment类名被混淆为
y0而非预期的PairEVFragment 问题背景
在获取Kotlin编写的PairEVFragment类名时,返回结果是y0,而非预期的类名。以下是该Fragment的代码和当前的ProGuard配置:
PairEVFragment代码
package io.o.android.cr.ui import android.content.Context import android.content.Intent import android.net.Uri import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.runtime.getValue import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import androidx.lifecycle.Lifecycle import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import dagger.android.support.AndroidSupportInjection import io.o.android.cr.compose.PairEVScreen import io.o.android.cr.model.crbd import io.o.android.cr.repo.crRepository import io.o.android.cr.viewmodel.PairEVViewModel import io.o.android.navigate.Navigator import io.o.android.navigate.ConfigureEVButtonPressed import io.o.android.theme.oM3Theme import io.o.android.util.extension.getPreviousFragmentName import io.o.android.util.extension.getScreenName import io.o.android.util.extension.hideToolbarIfInLegacyActivity import io.o.android.util.extension.runningInLegacyActivity import io.o.android.util.extension.showToolbarIfInLegacyActivity import io.o.android.util.log.Reporter import io.o.android.util.processScreenName import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject class PairEVFragment : Fragment() { @Inject lateinit var viewModelFactory: ViewModelProvider.Factory @Inject lateinit var navigator: Navigator @Inject lateinit var crRepository: crRepository @Inject lateinit var reporter: Reporter private val viewModel: PairEVViewModel by viewModels { viewModelFactory } override fun onAttach(context: Context) { super.onAttach(context) AndroidSupportInjection.inject(this) val crbd: crbd? = arguments?.getParcelable(cr_bd) val startReason: ConfigureEVButtonPressed = (arguments?.getSerializable(PAIR_EV_START_REASON) as ConfigureEVButtonPressed)!! viewModel.onScreenCreation(crbd, startReason) lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.requestNavigate.collect { navigation -> navigator.navigate(navigation, requireActivity()) viewModel.onHandledNavigationRequest() } } } } override fun onStart() { super.onStart() hideToolbarIfInLegacyActivity() } override fun onStop() { super.onStop() showToolbarIfInLegacyActivity() } override fun onResume() { super.onResume() Timber.d("xxx pairEVFragment javaClass.qualifiedName = ${this::class.qualifiedName}") // returns la.y0 Timber.d("xxx pairEVFragment screenName = ${javaClass.simpleName.processScreenName()}") //returns y0 } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { val contentView = ComposeView(requireContext()).apply { setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setContent { val screenState by viewModel.screenState.collectAsStateWithLifecycle() oM3Theme { PairEVScreen( state = screenState, runningInLegacyActivity = runningInLegacyActivity(), onSkip = { viewModel.onSkip() }, onContinue = { viewModel.onContinue() }, onCancelPairing = { viewModel.onCancelPairing() }, onSnackbarDismissed = { viewModel.onSnackbarDismissed() }, onHowcrApis = { viewModel.onHowcrApis() }, onBack = { navigator.goBack(requireActivity()) }, onBottomSheetClosed = { viewModel.onBottomSheetClosed() }, onPrivacyPolicy = { try { val intent = Intent( Intent.ACTION_VIEW, Uri.parse("https://o-ev.com/privacy-policy/") ) requireActivity().startActivity(intent) } catch (_: Exception) { } }) } } } return contentView } companion object { const val cr_bd = "cr_bd" const val PAIR_EV_START_REASON = "pair_ev_start_reason" fun newInstance(configureEVButtonPressed: ConfigureEVButtonPressed, crbd: crbd?): PairEVFragment { val fragment = PairEVFragment() val bundle = Bundle() bundle.putParcelable(cr_bd, crbd) bundle.putSerializable(PAIR_EV_START_REASON, configureEVButtonPressed) fragment.arguments = bundle return fragment } } }
现有ProGuard配置
# If you keep the line number information, uncomment this to # hide the original source file name. #-renamesourcefileattribute SourceFile # Firebase (see https://firebase.google.com/docs/auth/android/start/) -keepattributes Signature -keepattributes *Annotation* -keep class com.google.firebase.** { *; } -keep class com.google.android.gms.** { *; } -dontwarn com.google.firebase.** -dontwarn com.google.android.gms.** # Picasso (see https://github.com/square/picasso) -dontwarn com.squareup.okhttp.** # Crashlytics (see https://firebase.google.com/docs/crashlytics/get-deobfuscated-reports?platform=android&authuser=1) -keepattributes SourceFile,LineNumberTable -keep public class * extends java.lang.Exception # Skip running ProGuard on Crashlytics (this is not necessary but it will speed up the builds) -keep class com.crashlytics.** { *; } -dontwarn com.crashlytics.** -keep class com.google.firebase.crashlytics.** { *; } -dontwarn com.google.firebase.crashlytics.** # Gson (see https://github.com/google/gson/blob/master/examples/android-proguard-example/proguard.cfg) # Gson uses generic type information stored in a class file when working with fields. Proguard # removes such information by default, so configure it to keep all of it. -keepattributes Signature -dontwarn sun.misc.** -keep class * extends com.google.gson.TypeAdapter -keep class * implements com.google.gson.TypeAdapterFactory -keep class * implements com.google.gson.JsonSerializer -keep class * implements com.google.gson.JsonDeserializer -keepclassmembers,allowobfuscation class * { @com.google.gson.annotations.SerializedName <fields>; } -keep,allowobfuscation,allowshrinking class com.google.gson.reflect.TypeToken -keep,allowobfuscation,allowshrinking class * extends com.google.gson.reflect.TypeToken # Reflection -keep class kotlin.** { *; } -keep class kotlin.Metadata { *; } -dontwarn kotlin.** -assumenosideeffects class kotlin.jvm.internal.Intrinsics { static void checkParameterIsNotNull(java.lang.Object, java.lang.String); } -keep class kotlin.reflect.jvm.internal.impl.builtins.BuiltInsLoader # Classes that are serialized/deserialized using Gson -keep class * implements io.oh.aoid.util.DTO { *; } -keep public interface io.oh.aoid.util.DTO {*;} -keep class * implements io.oh.aoid.util.DBObject { *; } -keep enum * { *; } # Dagger 2 (see https://github.com/google/dagger/issues/645) -dontwarn com.google.errorprone.annotations.* # MPAndroidChart (see https://github.com/PhilJay/MPAndroidChart/wiki/Proguard) -keep class com.github.mikephil.charting.** { *; } # Scarlet for R8 full -if interface * { @com.tinder.scarlet.ws.* <methods>; } -keep,allowobfuscation interface <1> -keepclassmembers,allowshrinking,allowobfuscation interface * { @com.tinder.scarlet.ws.* <methods>; } -keep class com.tinder.scarlet.** { *; } ### Kotlin Coroutine # https://github.com/Kotlin/kotlinx.coroutines/blob/master/README.md # ServiceLoader support -keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {} -keepnames class kotlinx.coroutines.CoroutineExceptionHandler {} -keepnames class kotlinx.coroutines.android.AndroidExceptionPreHandler {} -keepnames class kotlinx.coroutines.android.AndroidDispatcherFactory {} # Most of volatile fields are updated with AFU and should not be mangled -keepclassmembernames class kotlinx.** { volatile <fields>; } # Same story for the standard library's SafeContinuation that also uses AtomicReferenceFieldUpdater -keepclassmembernames class kotlin.coroutines.SafeContinuation { volatile <fields>; } # https://github.com/Kotlin/kotlinx.atomicfu/issues/57 -dontwarn kotlinx.atomicfu.** -dontwarn kotlinx.coroutines.flow.** ### Kotlin #https://stackoverflow.com/questions/33547643/how-to-use-kotlin-with-proguard #https://medium.com/@AthorNZ/kotlin-metadata-jackson-and-proguard-f64f51e5ed32 -keepclassmembers class **$WhenMappings { <fields>; } -keep class kotlin.Metadata { *; } -keepclassmembers class kotlin.Metadata { public <methods>; } -keepattributes InnerClasses -dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations # kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer -keepclassmembers class kotlinx.serialization.json.** { *** Companion; } -keepclasseswithmembers class kotlinx.serialization.json.** { kotlinx.serialization.KSerializer serializer(...); } # Change here com.yourcompany.yourpackage -keep,includedescriptorclasses class io.oh.android.**$$serializer { *; }o-- change package name to your app's -keepclassmembers class io.oh.android.** { # <-- change paco name to your app's *** Companion; } -keepclasseswithmembers class io.oh.android.** { # <-- change packagome to your app's kotlinx.serialization.KSerializer serializer(...); } # uxcam integration (see https://help.uxcam.com/hc/en-us/articles/115000966252-Android-Integration) -keep class com.uxcam.** { *; } -dontwarn com.uxcam.** # Kept for analytic name reporting -keepnames class * extends io.oh.android.dagger.ui.InjectionActivityoepnames class * extends io.oh.android.dagger.ui.InjectionFragmento# Necessary to run instrumented tests on the release build # (see https://medium.com/wix-engineering/configuring-proguard-for-android-instrumentation-tests-120d58cba155) # # Necessary to avoid runtime issues where the following APIs used in tests are not available -keep class kotlin.collections.* { public static java.util.List shuffled(java.lang.Iterable); public static java.util.ArrayList arrayListOf(...); } -keep class kotlin.text.* { public static java.lang.String substring(java.lang.String, kotlin.ranges.IntRange); } -keep class androidx.recyclerview.widget.RecyclerView$ViewHolder -keep class androidx.recyclerview.widget.RecyclerView { public void scrollToPosition(int); public androidx.recyclerview.widget.RecyclerView$ViewHolder findViewHolderForPosition(int); } -dontwarn org.apache.commons.logging.** -keep class org.apache.** { *; } # Fixes issue https://programmersought.com/article/3367502760/ -dontwarn org.xmlpull.v1.XmlPullParser -dontwarn org.xmlpull.v1.XmlSerializer -keep class org.xmlpull.v1.* {*;} # Without this on R8 we on release builds we get a ClassCastException cannot be cast to java.lang.reflect.ParameterizedType -keep,allowobfuscation,allowshrinking class io.o.android.util.network.NetworkTask # Retrofit for R8 -keep,allowobfuscation,allowshrinking interface retrofit2.Call -keep,allowobfuscation,allowshrinking class retrofit2.Response -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
问题原因
这是ProGuard/R8代码混淆导致的:在Release构建中,混淆工具会自动缩短类名、方法名来减小APK体积并提升代码安全性,PairEVFragment被重命名为了y0。
你的ProGuard配置中,现有规则并未覆盖PairEVFragment:现有规则仅尝试保留继承自io.oh.android.dagger.ui.InjectionFragment的类名(还存在拼写错误:末尾多了个o),但PairEVFragment直接继承自androidx.fragment.app.Fragment,不在现有规则的保护范围内。
解决方案
根据需求选择以下一种方式修改proguard-rules.pro:
1. 保留单个Fragment的类名
如果只需要保留PairEVFragment的类名,添加:
-keepnames class io.o.android.cr.ui.PairEVFragment
2. 保留包下所有Fragment类名
如果包下有多个Fragment需要保留类名,添加:
-keepnames class io.o.android.cr.ui.*Fragment
3. 保留所有继承自AndroidX Fragment的类名
如果需要全局保留所有Fragment的类名,添加:
-keepnames class * extends androidx.fragment.app.Fragment
关键说明
-keepnames:仅保留类名(如果类未被代码引用,仍可能被移除),适合仅需要获取类名的场景;- 如果需要确保类不被移除,改用
-keep(但会增加APK体积):-keep class io.o.android.cr.ui.PairEVFragment
另外,修复现有配置中的拼写错误:
将这一行:
-keepnames class * extends io.oh.android.dagger.ui.InjectionFragmento
改为:
-keepnames class * extends io.oh.android.dagger.ui.InjectionFragment
验证
修改后重新构建Release版本,再次打印this::class.qualifiedName或javaClass.simpleName,即可得到正确的PairEVFragment类名。
内容的提问来源于stack exchange,提问作者BRDroid
相关产品推荐
相关产品推荐

