R8, minification & obfuscation
The release build enables the works:
// android/app/build.gradle.kts
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
signingConfig = signingConfigs.getByName("debug")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}R8 in full mode: tree-shaking, optimisation, identifier obfuscation, and resource shrinking. It took the APK from ~48 MB to ~37 MB.
Why it almost didn’t work
1. UnsatisfiedLinkError on WebRTC native methods
libwebrtc’s Java classes are bound to native code by exact name. R8 renaming
org.webrtc.SoftwareVideoDecoderFactory.nativeCreateDecoder breaks the JNI
lookup at runtime - UnsatisfiedLinkError the first time a software codec is
needed.
Fix - keep the whole org.webrtc package, names and all:
-keep class org.webrtc.** { *; }
-keepclassmembernames class org.webrtc.** { *; }
-keepnames class org.webrtc.** { *; }
-keepclasseswithmembers class * { native <methods>; }
-keep,allowshrinking class org.webrtc.** { *; }
-dontwarn org.webrtc.**2. Native SIGTRAP in JNI_OnLoad - the hard one
The io.github.webrtc-sdk AAR bundles Chromium’s JNI runtime,
org.jni_zero. libjingle_peerconnection_so.so’s JNI_OnLoad does:
FindClass("org/jni_zero/JniInit"); // and resolves @CalledByNative members by nameR8 has no reason to keep org.jni_zero - nothing in Kotlin references it - so it
strips the package. Then PeerConnectionFactory.initialize() triggers
JNI_OnLoad, FindClass fails, and the process dies with a native SIGTRAP
before any Java exception can be thrown. The AAR ships no consumer ProGuard
rules, so nothing warns you.
Fix - keep org.jni_zero whole and preserve every @CalledByNative member
anywhere:
-keep class org.jni_zero.** { *; }
-keepclassmembers class org.jni_zero.** { *; }
-dontwarn org.jni_zero.**
-keepclasseswithmembers class * { @org.jni_zero.CalledByNative <methods>; }
-keepclasseswithmembers class * { @org.jni_zero.CalledByNativeUnchecked <methods>; }
-keepclasseswithmembers class * { @org.jni_zero.AccessedByNative <fields>; }3. Gson models
Signaling wire types are (de)serialised by field name. R8 renaming the fields breaks the JSON mapping.
-keepattributes *Annotation*,Signature,InnerClasses,EnclosingMethod,SourceFile,LineNumberTable
-keepclassmembers class * { @com.google.gson.annotations.SerializedName <fields>; }
-keep class com.example.videocall.** { *; }-keepattributes …SourceFile,LineNumberTable keeps release stack traces
de-obfuscatable with the mapping.txt.
De-obfuscating a release crash
Every CI release attaches …-mapping.txt. To read an obfuscated stack trace:
# from the Android SDK
$ANDROID_HOME/tools/proguard/bin/retrace.sh mapping.txt stacktrace.txt
# or the newer standalone
java -jar retrace.jar mapping.txt stacktrace.txtKeep the mapping file for every build you distribute - it’s the only way back
from a.b.c(Unknown Source).
The full proguard-rules.pro
Lives at android/app/proguard-rules.pro. Beyond the above it also has the
standard OkHttp/Okio -dontwarns and keeps
okhttp3.internal.publicsuffix.PublicSuffixDatabase by name.