fix(android): keep the JNI callback names through R8
Release builds aborted on the first callback of every test: NoSuchMethodError: no non-static method "Lp0;.onOutput(Ljava/lang/String;)V" R8 had renamed MainThreadCallback to p0 along with its methods, so GetMethodID(env, cls, "onOutput", ...) in iperf_jni.c resolved nothing. The names are a contract between C and Kotlin that the minifier cannot see. Adds keep rules for IperfRunner, the IperfCallback interface and — the part that matters — everything implementing it, since the lookup happens on the concrete object's class rather than on the interface. A general rule keeps any class declaring a native method, so a future one is covered without a new rule. Debug builds do not minify, which is why every on-device check so far passed while the shipping artifact was broken. Found by running the release APK during release preparation. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,13 @@ android {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
|
||||
// proguard-rules.pro keeps the names JNI resolves at runtime.
|
||||
// Without it a release build aborts on the first callback.
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
# JNI reaches these by name. R8 renames by default, so without these rules a
|
||||
# release build resolves nothing: GetMethodID("onOutput", ...) returns NULL,
|
||||
# raises NoSuchMethodError, and the next JNI call aborts the process with
|
||||
# NoSuchMethodError: no non-static method "Lp0;.onOutput(Ljava/lang/String;)V"
|
||||
# Debug builds do not minify, so this only ever appears in release.
|
||||
|
||||
# The plugin object holds the native method declarations and is looked up from
|
||||
# C by its fully qualified name (Java_com_novoyuuparosk_iperf3gui_IperfRunner_*).
|
||||
-keep class com.novoyuuparosk.iperf3gui.IperfRunner { *; }
|
||||
|
||||
# The callback interface and everything implementing it: iperf_jni.c resolves
|
||||
# onOutput/onError/onComplete by name on the concrete object's class, so the
|
||||
# implementors' method names must survive too, not just the interface's.
|
||||
-keep interface com.novoyuuparosk.iperf3gui.IperfRunner$IperfCallback { *; }
|
||||
-keep class * implements com.novoyuuparosk.iperf3gui.IperfRunner$IperfCallback { *; }
|
||||
|
||||
# Belt and braces for any future native method: keep the name of any class
|
||||
# declaring one, and the native methods themselves.
|
||||
-keepclasseswithmembernames,includedescriptorclasses class * {
|
||||
native <methods>;
|
||||
}
|
||||
Reference in New Issue
Block a user