-
Notifications
You must be signed in to change notification settings - Fork 87
Open
Description
I have written a plugin with jnigen and when impl the BroadcastReceiver, we need impl it on Android side and then bind the impl with jnigen.
Here is the key codes I used in the plugin.
package dev.zeekr.connectivity
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
interface JBroadcastReceiver {
fun onReceive(context: Context, intent: Intent)
}
class JBroadcastReceiverImpl(private val receiver: JBroadcastReceiver) : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (context == null) throw NullPointerException("context is null")
if (intent == null) throw NullPointerException("intent is null")
this.receiver.onReceive(context, intent)
}
}consumer-rules.pro
-keep class dev.zeekr.connectivity.** { *; }
tool/jnigen.dart
import 'dart:io';
import 'package:jnigen/jnigen.dart';
import 'package:logging/logging.dart';
void main() async {
final root = Platform.script.resolve('../');
final config = Config(
outputConfig: OutputConfig(
dartConfig: DartCodeOutputConfig(
path: root.resolve('lib/src/jni/'),
structure: OutputStructure.packageStructure,
),
),
classes: [
'dev.zeekr.connectivity.JBroadcastReceiver',
'dev.zeekr.connectivity.JBroadcastReceiverImpl',
],
sourcePath: [root.resolve('android/src/main/kotlin/')],
androidSdkConfig: AndroidSdkConfig(
addGradleDeps: true,
androidExample: 'example/',
),
nonNullAnnotations: ['android.annotation.NonNull'],
nullableAnnotations: ['android.annotation.Nullable'],
logLevel: Level.ALL,
);
await generateJniBindings(config);
}wifi_manager.dart
abstract interface class WifiManager$WifiStateChangedListener {
factory WifiManager$WifiStateChangedListener({
required void Function() onWifiStateChanged,
}) => jni.Build$VERSION.SDK_INT >= jni.Build$VERSION_CODES.BAKLAVA
? WifiManager$WifiStateChangedListenerImpl36(
onWifiStateChanged: onWifiStateChanged,
)
: WifiManager$WifiStateChangedListenerImpl(
onWifiStateChanged: onWifiStateChanged,
);
}
abstract interface class WifiManager {
factory WifiManager() => WifiManagerImpl();
void addWifiStateChangedListener(
WifiManager$WifiStateChangedListener listener,
);
void removeWifiStateChangedListener(
WifiManager$WifiStateChangedListener listener,
);
}
final class WifiManager$WifiStateChangedListenerImpl
implements WifiManager$WifiStateChangedListener {
final jni.BroadcastReceiver api;
WifiManager$WifiStateChangedListenerImpl.jni(this.api);
factory WifiManager$WifiStateChangedListenerImpl({
required void Function() onWifiStateChanged,
}) {
final api = jni.JBroadcastReceiverImpl(
jni.JBroadcastReceiver.implement(
jni.$JBroadcastReceiver(
onReceive: (context, intent) {
final action = intent.getAction();
if (action != jni.WifiManager.WIFI_STATE_CHANGED_ACTION) return;
onWifiStateChanged();
},
),
),
);
return WifiManager$WifiStateChangedListenerImpl.jni(api);
}
}
final class WifiManagerImpl implements WifiManager {
final jni.WifiManager api;
WifiManagerImpl.jni(this.api);
factory WifiManagerImpl() {
final apiOrNull = jni.ContextCompat.getSystemService(
jni.context,
jni.WifiManager.type.jClass,
T: jni.WifiManager.type,
);
final api = ArgumentError.checkNotNull(apiOrNull);
return WifiManagerImpl.jni(api);
}
@override
void addWifiStateChangedListener(
WifiManager$WifiStateChangedListener listener,
) => jni.Build$VERSION.SDK_INT >= jni.Build$VERSION_CODES.BAKLAVA
? api.addWifiStateChangedListener(
jni.context.mainExecutor,
listener.api36,
)
: jni.ContextCompat.registerReceiver(
jni.context,
listener.api,
jni.IntentFilter.new$2(jni.WifiManager.WIFI_STATE_CHANGED_ACTION),
jni.ContextCompat.RECEIVER_NOT_EXPORTED,
);
@override
void removeWifiStateChangedListener(
WifiManager$WifiStateChangedListener listener,
) => jni.Build$VERSION.SDK_INT >= jni.Build$VERSION_CODES.BAKLAVA
? api.removeWifiStateChangedListener(listener.api36)
: jni.context.unregisterReceiver(listener.api);
}
extension WifiManager$WifiStateChangedListenerX
on WifiManager$WifiStateChangedListener {
jni.WifiManager$WifiStateChangedListener get api36 {
final impl = this;
if (impl is! WifiManager$WifiStateChangedListenerImpl36) throw TypeError();
return impl.api;
}
jni.BroadcastReceiver get api {
final impl = this;
if (impl is! WifiManager$WifiStateChangedListenerImpl) throw TypeError();
return impl.api;
}
}When I ued the WifiManager to listen the wifi state, it works fine, but sometimes the app crashed when I turn off the wifi.
Here is the crash logs
E/Dart (31269): ../../../flutter/third_party/dart/runtime/vm/runtime_entry.cc: 4637: error: Callback invoked after it has been deleted.
E/DartVM (31269): version=3.9.2 (stable) (Wed Aug 27 03:49:40 2025 -0700) on "android_arm64"
E/DartVM (31269): pid=31269, thread=504513725696, isolate_group=main(0xb4000074a45eb800), isolate=main(0xb4000074bf2ff480)
E/DartVM (31269): os=android, arch=arm64, comp=yes, sim=no
E/DartVM (31269): isolate_instructions=744ba94dc0, vm_instructions=744ba94dc0
E/DartVM (31269): fp=7fe4f44040, sp=7fe4f44010, pc=744b8a001c
E/DartVM (31269): pc 0x000000744b8a001c fp 0x0000007fe4f44040 /data/app/~~fTFhIk566QS-mAuvG4Aisg==/dev.zeekr.connectivity_example-u6JoM-Urj_q77T0PrGUtCw==/base.apk!/lib/arm64-v8a/libflutter.so+0x1fb001c
E/DartVM (31269): pc 0x000000744b78e3f8 fp 0x0000007fe4f441e8 /data/app/~~fTFhIk566QS-mAuvG4Aisg==/dev.zeekr.connectivity_example-u6JoM-Urj_q77T0PrGUtCw==/base.apk!/lib/arm64-v8a/libflutter.so+0x1e9e3f8
E/DartVM (31269): pc 0x00000074294ac6ac fp 0x0000007fe4f442d0 Java_com_github_dart_1lang_jni_PortProxyBuilder__1invoke+0x2fc
E/DartVM (31269): pc 0x00000074294ac6ac fp 0x0000007fe4f44490 Java_com_github_dart_1lang_jni_PortProxyBuilder__1invoke+0x2fc
E/DartVM (31269): pc 0x00000074c7ad7648 fp 0x0000007fe4f444d0 /apex/com.android.art/lib64/libart.so+0x2d7648
E/DartVM (31269): -- End of DumpStackTrace
F/libc (31269): Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 31269 (ctivity_example), pid 31269 (ctivity_example)
Process name is dev.zeekr.connectivity_example, not key_process
keyProcess: 0
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'OnePlus/OnePlus9_CH/OnePlus9:12/RKQ1.211103.002/R.202203260223:user/release-keys'
Revision: '0'
ABI: 'arm64'
Timestamp: 2025-11-20 17:57:19.184345808+0800
Process uptime: 0s
Cmdline: dev.zeekr.connectivity_example
pid: 31269, tid: 31269, name: ctivity_example >>> dev.zeekr.connectivity_example <<<
uid: 10280
signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
Abort message: '../../../flutter/third_party/dart/runtime/vm/runtime_entry.cc: 4637: error: Callback invoked after it has been deleted.'
x0 0000000000000000 x1 0000000000007a25 x2 0000000000000006 x3 0000007fe4f43fa0
x4 0000000000000010 x5 0000000000000010 x6 0000000000000010 x7 7f7f7f7f7f7f7f7f
x8 00000000000000f0 x9 1fe1585bfd33801b x10 0000000000000000 x11 ffffff80fffffbdf
x12 0000000000000001 x13 000000000000001a x14 0000007fe4f427d0 x15 0000000034155555
x16 000000754d10ebc0 x17 000000754d0e9680 x18 00000075772d2000 x19 0000000000007a25
x20 0000000000007a25 x21 00000000ffffffff x22 0000007fe4f44200 x23 0000007fe4f441f8
x24 00000074b5700168 x25 0000007fe4f445f8 x26 0000000018380001 x27 0000000000000000
x28 0000007fe4f444d0 x29 0000007fe4f44020
lr 000000754d09999c sp 0000007fe4f43f80 pc 000000754d0999c8 pst 0000000000001000
backtrace:
#00 pc 00000000000799c8 /apex/com.android.runtime/lib64/bionic/libc.so (abort+168) (BuildId: ef9f41416d59cdf9c6d7529d11b53eff)
#01 pc 0000000001e9e3fc /data/app/~~fTFhIk566QS-mAuvG4Aisg==/dev.zeekr.connectivity_example-u6JoM-Urj_q77T0PrGUtCw==/base.apk!libflutter.so (BuildId: 3309f17d52486f5bf2fc84011621fdd0785efc8e)
#02 pc 0000000001fc80d0 /data/app/~~fTFhIk566QS-mAuvG4Aisg==/dev.zeekr.connectivity_example-u6JoM-Urj_q77T0PrGUtCw==/base.apk!libflutter.so (BuildId: 3309f17d52486f5bf2fc84011621fdd0785efc8e)
#03 pc 000000000001001c [anon:FfiCallbackMetadata::TrampolinePage]
Lost connection to device.
Exited.Seems the callback is deleted when Android calling it. I don't have any idea how this happened, is this a bug or something I missed in my project?