Here's a very simple class:
class MainActivity : AppCompatActivity() {
val prop: String = "test"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("MainActivity", ::prop.name)
}
}
If I run this application with ProGuard using the following rules:
-dontwarn kotlin.**
-dontwarn org.w3c.dom.events.*
-dontwarn org.jetbrains.kotlin.di.InjectorForRuntimeDescriptorLoader
I get this exception:
a.d.g: Property 'prop' not resolved in class com.cypressworks.kotlinreflectionproguard.MainActivity
Which ProGuard rules do I need to apply to make it work?
If ProGuard is removing your attribute, the dontwarn rule will only hide the warning messages. What you need is to actually tell ProGuard to keep it.
One possible way could be:
-keepclassmembers public class com.cypressworks.kotlinreflectionproguard.** {
public * *;
}
Related
I am trying to enable minifyEnabled true in my gradle file for deployment but i am getting the below error:
java.lang.IllegalArgumentException: Unable to create converter for class com.th3pl4gu3.mes.models.MesResponse
for method MesApiService.getServices
My code is as follows
AppContainer.kt
/**
* Dependency Injection container at the application level.
*/
interface AppContainer {
val onlineServiceRepository: ServiceRepository
}
/**
* Implementation for the Dependency Injection container at the application level.
*
* Variables are initialized lazily and the same instance is shared across the whole app.
*/
class DefaultAppContainer(private val context: Context): AppContainer {
private val BASE_URL = "https://..."
/**
* Use the Retrofit builder to build a retrofit object using a kotlinx.serialization converter
*/
#OptIn(ExperimentalSerializationApi::class)
private val retrofit: Retrofit = Retrofit.Builder()
.addConverterFactory(Json.asConverterFactory("application/json".toMediaType()))
.baseUrl(BASE_URL)
.build()
/**
* Retrofit service object for creating api calls
*/
private val retrofitService: MesApiService by lazy {
retrofit.create(MesApiService::class.java)
}
}
ServiceRepository.kt
/**
* Repository that fetch service list from Mes API.
*/
interface ServiceRepository {
/** Fetches list of services from Mes API */
suspend fun getMesServices(): MesResponse
}
MesApiServiceRepository.kt
class MesApiServiceRepository(
private val mesApiService: MesApiService
) : ServiceRepository {
/** Fetches list of Services from Mes API */
override suspend fun getMesServices(): MesResponse = mesApiService.getServices()
}
MesResponse.kt
#kotlinx.serialization.Serializable
data class MesResponse(
val services: List<Service>,
val message: String,
val success: Boolean
)
proguard-rules.pro
#####################################
########## Kotlin Metadata ##########
#####################################
-dontwarn org.jetbrains.annotations.**
-keep class kotlin.Metadata { *; }
##################################
########### MES Models ###########
##################################
-keep class com.th3pl4gu3.mes.models.MesResponse
-keep class com.th3pl4gu3.mes.models.** {*;}
-keep class com.th3pl4gu3.mes.data.** {*;}
-keep class com.th3pl4gu3.mes.api.** {*;}
-keep class com.th3pl4gu3.mes.di.** {*;}
###############################
########## Retrofit2 ##########
###############################
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod
# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
# Keep annotation default values (e.g., retrofit2.http.Field.encoded).
-keepattributes AnnotationDefault
# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
#retrofit2.http.* <methods>;
}
# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit
# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { #retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>
# Keep inherited services.
-if interface * { #retrofit2.http.* <methods>; }
-keep,allowobfuscation interface * extends <1>
# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items).
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
# With R8 full mode generic signatures are stripped for classes that are not
# kept. Suspend functions are wrapped in continuations where the type argument
# is used.
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
Can someone please help me out ?
I was able to fix it by adding #Keep to the MesResponse class.
#Keep
#kotlinx.serialization.Serializable
data class MesResponse(
val services: List<Service>,
val message: String,
val success: Boolean
)
The project working well without enabling proguard, the problem comes when enable it, I am using retrofit with Moshi converter and Coroutines to fetch list of data, and Hilt for DI, and I added all rules and kept all models
This is the error:
Could not compute caller for function: public constructor MovieListEntity(movie_data: kotlin.collections.List<com...domain.entities.MovieData>) defined in com...domain.entities.MovieListEntity[c#dad1eb0] (member = null)
And these are the classes mentioned
data class MovieListEntity(
#field:Json(name = "movie_data")
val movie_data: List<MovieData>
)
data class MovieData(
#field:Json(name = "movie_id")
val movie_id: Int,
#field:Json(name = "sub_title")
val sub_title: String,
#field:Json(name = "title")
val title: String
)
Note: I tried also without annotations, and it didn't help
These are the proguard rules:
-keep class com.***.***.domain.entitie.** { *; }
-keep class com.***.***.domain.entities.*
-keep class com.***.***.domain.entities.MovieListEntity
-keep class com.***.***.domain.entities.MovieData
-keep class com.***.***.DataBinderMapperImpl { *; }
-keep class com.***.***.DataBinderMapperImpl { *; }
-keep class com.***.*****{
public ** component1();
<fields>;
}
Plus other rules for retrofit, OkHttp, hilt .. etc.
How Can I solve this error?
Solved by adding these rules
-keepclassmembers class kotlin.Metadata {
public <methods>;
}
-keepclassmembers class * {
#com.squareup.moshi.FromJson <methods>;
#com.squareup.moshi.ToJson <methods>;
}
-keepnames #kotlin.Metadata class com.******.domain.entities.**
-keep class com.******.domain.entities.** { *; }
-keepclassmembers class com.*****.domain.entities.** { *; }
The problem was in Moshi library, check this
I have a class with a companion object which implements a factory interface.
class GoalInspectorData(
...
) {
companion object : DataClassFactory<GoalInspectorData> {
override fun fromV8Object(v8Object: V8Object): GoalInspectorData {
...
}
}
}
I have some code which examines this class at runtime using reflection to see if the class provides a factory method. It does this by checking to see if the class has a companion object (companionObjectInstance) and, if so, if that companion object implements the factory interface.
internal inline fun <reified T> convert(obj: Any): T {
val companionObject = T::class.companionObjectInstance
#Suppress("UNCHECKED_CAST")
return when {
T::class in builtInClasses -> obj as T
companionObject as? DataClassFactory<T> != null -> companionObject.fromV8Object(obj as V8Object)
else -> throw IllegalArgumentException("No converter for type ${T::class}")
}
}
This all works fine in a debug build.
It fails in a release build with R8 enabled (minifyEnabled true in build.gradle). It fails because companionObjectInstance returns null.
I'm using the don't optimize Proguard config:
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
and in my own proguard-rules.pro I've added just about every -keep rule I can imagine in an attempt to retain this companion object, and added #Keep annotations to everything, but nothing works. R8 is determined to strip it out.
For example:
-keep class my.package.** {
*;
}
-keep interface my.package.** {
*;
}
-if class **$Companion extends **
-keep class <2>
-if class **$Companion implements **
-keep class <2>
Are there any other -keep rules or configuration options which would instruct R8 to retain this companion object?
First of all, the keep rule
-keep class my.package.** {
*;
}
should be sufficient to keep all the classes - including companion classes in your program. You should not need the -dontoptimize flag, so using the configuration proguard-android-optimize.txt should be fine.
However, as you use Kotlin reflection you probably also need to keep the annotation class kotlin.Metadata and runtime visible annotations with these rules:
-keep #interface kotlin.Metadata {
*;
}
-keepattributes RuntimeVisibleAnnotations
If that does still not work could you please file an R8 issue? If you can include a simple reproduction that would be great.
In Debug version it is working fine, but fails on Release version. I guess it is related to Proguard rules.
// Koin
implementation 'org.koin:koin-android:1.0.2'
implementation 'org.koin:koin-android-viewmodel:1.0.2'
Modules:
val appModule = module {
viewModel<IntroVM>()
}
val dataModule = module {
single { NetworkManager(androidContext().applicationContext) }
single { LocalFileManager(androidContext().applicationContext) }
single { ApiModel(get()) }
Class definitions:
class ApiModel(val networkManager: NetworkManager){}
class LocalFileManager(private var appContext: Context) {}
class NetworkManager(private var appContext: Context) {}
class IntroVM(var apiModel: ApiModel) : CommonVM() {
Koin init:
startKoin(this, listOf(appModule, dataModule))
Proguard rules:
-keepattributes *Annotation*
-keepattributes SourceFile,LineNumberTable
-keep public class * extends java.lang.Exception
-keepclassmembernames class kotlinx.** {
volatile <fields>;
}
StackTrace:
Error while resolving instance for class 'com...viewModels.auth.b' -
error: org.koin.error.BeanInstanceCreationException:
Can't create definition for 'Factory [name='b',class='com..viewModels.auth.b', binds~(androidx.lifecycle.r)]' due to error :'binds~(androidx.lifecycle.r)]
No constructor found for class 'class com...viewModels.auth.b'
As you are using androidX
Use below library of viewmodel for koin
implementation 'org.koin:koin-androidx-viewmodel:1.0.2'
This issue provided needed solution
I have the following ViewModel:
class SignInViewModel #Inject constructor(val api: BillingApi) : ViewModel() {
val googleApiClient: MutableLiveData<GoogleApiClient> = MutableLiveData()
}
On my Activity.onCreate(onSavedInstanceState: Bundle?) I have:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
signInViewModel = ViewModelProviders.of(this)
.get(SignInViewModel::class.java)
signInViewModel.googleApiClient.observe(this, Observer<GoogleApiClient?> {
... // here never gets trigged
}
Later on my code I have signInViewModel.googleApiClient.value = it. At this point (which happens after a button click, so I am in a resumed state) the I expected the LiveData to trigger my observer, but it does not.
While debugging I've noticed that my MutableLiveData is never on an active state.
What am I doing wrong? Please, I know I am using a GoogleApiClient instance in the example and that it should be initialized with the Activity with automanage and whatnot, but this is not the issue here. I want to set it dynamically and have my observer be triggered.
Edit: adding the code that calls the setValue
signInViewModel.someMethod(this)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Consumer {
// This gets called but the observe callback does **not**
signInViewModel.googleApiClient.value = it
}, errorCallback)
It turns out minifyEnabled was true. I haven't seen anything about proguard rules for architecture components.
Found this issue which is not resolved yet but has the configuration needed to make it pass:
-keepclassmembers class * implements android.arch.lifecycle.LifecycleObserver {
<init>(...);
}
-keepclassmembers class * extends android.arch.lifecycle.ViewModel {
<init>(...);
}
-keepclassmembers class android.arch.lifecycle.Lifecycle$State { *; }
-keepclassmembers class android.arch.lifecycle.Lifecycle$Event { *; }
-keepclassmembers class * {
#android.arch.lifecycle.OnLifecycleEvent *;
}
-keep class * implements android.arch.lifecycle.LifecycleObserver {
<init>(...);
}