4.3 KiB
A categorized summary of the keep rule examples, including the code patterns to look for (imports/usage) and the corresponding suggested rules.
1. Reflection: Classes Loaded by Name
Scenario: A library or app loads a class dynamically using a string name
-
Look for:
Class.forName("..."),getDeclaredConstructor().newInstance(), or interfaces used for dynamic loading. -
Example Code:
kotlin val taskClass = Class.forName(className) val task = taskClass.getDeclaredConstructor().newInstance() as StartupTask -
Suggested Keep Rule: ```proguard
-keep class * implements com.example.library.StartupTask { (); } ```
2. Reflection: Classes Passed using ::class.java
Scenario: An app passes a class reference directly to a library function.
-
Look for:
::class.java(Kotlin) or.class(Java) passed as an argument. -
Example Code:
kotlin fun <T> register(clazz: Class<T>) { } // Usage: register(MyService::class.java) -
Suggested Keep Rule: ```proguard
Keep the class itself (R8 usually handles this, but explicit rules ensure stability)
-keep class com.example.app.MyService { (); } ```
3. Annotation-Based Reflection (Methods/Classes)
Scenario: Using custom annotations to mark methods or classes for reflective execution.
Look for: Custom @interface definitions and getDeclaredMethods()
filtered by annotation.
Example Code:
kotlin annotation class ReflectiveExecutor // Logic: find methods annotated with @ReflectiveExecutor and invoke them
- Suggested Keep Rule: ```proguard # Keep the annotation itself -keep @interface com.example.library.ReflectiveExecutor
Keep members of any class annotated with this specific annotation
-keepclassmembers class * { @com.example.library.ReflectiveExecutor *; } ```
4. Optional Dependencies (Soft Dependencies)
Scenario: A core library checks if an optional module is present in the classpath.
- Look for:
try-catchblocks aroundClass.forName()used to toggle features. - Example Code: ```kotlin private const val VIDEO_TRACKER_CLASS = "com.example.analytics.video.VideoEventTracker"
try { Class.forName(VIDEO_TRACKER_CLASS).getDeclaredConstructor().newInstance() } catch (e: ClassNotFoundException) { /* skip feature */ } ```
- Suggested Keep Rule: `proguard
Preserve the optional class so the check doesn't fail due to shrinking
-keep class com.example.analytics.video.VideoEventTracker { (); }`
5. Accessing Private Members
Scenario: Using reflection to access internal fields or methods not exposed with public APIs.
-
Look for:
getDeclaredField("...")orgetDeclaredMethod("...")followed byisAccessible = true. -
Example Code:
kotlin val secretField = instance::class.java.getDeclaredField("secretMessage") secretField.isAccessible = true -
Suggested Keep Rule: ```proguard
Specifically keep the private field/method by name and type
-keepclassmembers class com.example.LibraryClass { private java.lang.String secretMessage; } ```
6. Parcelable (Manual Implementation)
Scenario: Implementing Parcelable without using the @Parcelize
annotation.
-
Look for:
implements Parcelableand a staticCREATORfield. -
Example Code:
kotlin class MyData : Parcelable { // Manual implementation with CREATOR field } -
Suggested Keep Rule: (Note: If using
import kotlinx.parcelize.Parcelize, R8/ProGuard rules are generated automatically. If manual, use the following:)proguard -keepclassmembers class * implements android.os.Parcelable { static android.os.Parcelable$Creator CREATOR; }
7. Enums and Obfuscation
Scenario: App uses Enum.valueOf("STRING_NAME") indirectly (e.g.,using JSON
deserialization) and the enum names get obfuscated.
-
Look for: Unnecessary generic Enum keep rules in ProGuard files.
-
Example Code: ```proguard
Unnecessary rule
-keepclassmembers enum * { *; } ```
-
Suggested Keep Rule: *(Note: The default
proguard-android-optimize.txtalready contains the optimal rules for Enums (keepingvalues()andvalueOf(String)). Any additional manual rules for Enums are redundant.) # No manual rule needed. Use default proguard-android-optimize.txt.