dart-lang / native

Dart packages related to FFI and native assets bundling.
BSD 3-Clause "New" or "Revised" License
114 stars 40 forks source link

App crashing on trying to invoke generated bindings after rendering a native view #656

Closed cah-aamir closed 4 months ago

cah-aamir commented 1 year ago

I am trying to create a plugin for a 3rd party library, somewhat like zoom. I was able to generate bindings for the library and I can even call class methods e.g. I can call initialize method of native sdk in dart and I get the expected response back.

However, once I render a hybrid native view in main.dart using PlatformViewLink, if I try to call anything from the library or even just try to call Jni.getCachedApplicationContext() the app crashes.

Is jnigen/ffigen the right path to go about a plugin like this or should I just use MethodChannel to do it the conventional way? I was hoping it would be easier using jnigen/ffigen to call native sdk methods from the generated bindings while using the native android & iOS view.

Here's the log from the crash:

E/FrameEvents( 7905): updateAcquireFence: Did not find frame.
F/libc    ( 7905): Fatal signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0 in tid 7939 (1.ui), pid 7905 (gen_app_example)
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'google/oriole/oriole:13/TQ1A.230205.002/9471150:user/release-keys'
Revision: 'MP1.0'
ABI: 'arm64'
Timestamp: 2023-04-05 16:52:13.957367348-0500
Process uptime: 9s
Cmdline: com.example.jnigen_app_example
pid: 7905, tid: 7939, name: 1.ui  >>> com.example.jnigen_app_example <<<
uid: 10294
tagged_addr_ctrl: 0000000000000001 (PR_TAGGED_ADDR_ENABLE)
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x0000000000000000
Cause: null pointer dereference
    x0  b4000076e2655a98  x1  00000076bb072a18  x2  0000000000000008  x3  0000000000000010
    x4  00000076bb072a20  x5  b4000076e2655aa0  x6  0000000000000000  x7  0000000000000000
    x8  0000000000000000  x9  8945000000010101  x10 0000000000010000  x11 0000000000000001
    x12 00000000e2655aa0  x13 0000000000000001  x14 0000000000000000  x15 000000764c80aca0
    x16 00000076bb076fa0  x17 0000007984fedf20  x18 000000764a930000  x19 00000076baccc408
    x20 000000764c80b308  x21 000000764c80b338  x22 0000007500008081  x23 000000764c80b348
    x24 0000007500008081  x25 000000764c731000  x26 b4000078226b57e0  x27 00000075013f7b00
    x28 0000000800000075  x29 000000764c80ac80
    lr  00000076bb06e52c  sp  000000764c80ac60  pc  00000076bb06e544  pst 0000000060001000
backtrace:
      #00 pc 0000000000007544  /data/app/~~tyiSw5mMUZcdw1EZGkaG0w==/com.example.jnigen_app_example-qY_LzAgJqQZ3UNfmKRG0Rg==/lib/arm64/libdartjni.so (attach_thread+48) (BuildId: 2d5e47336c19fdbe5e480bde3f2249ee0f66184c)
      dart-lang/jnigen#1 pc 0000000000005fec  /data/app/~~tyiSw5mMUZcdw1EZGkaG0w==/com.example.jnigen_app_example-qY_LzAgJqQZ3UNfmKRG0Rg==/lib/arm64/libdartjni.so (GetApplicationContext+8) (BuildId: 2d5e47336c19fdbe5e480bde3f2249ee0f66184c)
      dart-lang/jnigen#2 pc 0000000000006f64  [anon:dart-code]
Lost connection to device.
mahesh-hegde commented 1 year ago

hybrid native view in main.dart using PlatformViewLink

We haven't yet considered more advanced types of embedding (Flutter views in native app or native views in flutter app).

I suspect this error is double initialization in same thread. JNI has quite a few problems with Flutter's threading model. :(

Is jnigen/ffigen the right path to go about a plugin like this or should I just use MethodChannel to do it.

Unfortunately I think you have to go with methodchannel for now. pigeon may be slightly helpful.

Part of the problem is this library is in alpha, and many build system or runtime quirks still have to be handled. (I take the full blame here).

Thanks for being an early user and reporting bugs.

cah-aamir commented 1 year ago

@mahesh-hegde No problem at all, thanks for the quick response. Perhaps my approach wasn't completely right but as you mentioned there are issues with flutter's threading model and you guys are still ironing out some quirks, I will switch to using MethodChannel and thanks for suggesting "Pigeon", I think it will be helpful. Regardless, jnigen/ffigen is pretty exciting and I can't wait to try out more advanced use-cases once it's more stable, you guys are doing a great job 👍

yanshouwang commented 6 months ago

I think this error can be solved by call the Jni.initDLApi() method. But I found another issue when I call Android native methods that marks with @MainThread, even if I use the runOnUiThread or MainExecutor.execute on dart side:

@override
  void bindToLifecycle() {
    Jni.initDLApi();
    final runnable = jni.Runnable.implement(
      jni.$RunnableImpl(
        run: () => jController.bindToLifecycle(currentActivity),
      ),
    );
    jni.ContextCompat.getMainExecutor(currentActivity).execute(runnable);
  }

The code above will throw errors

Launching lib\main.dart on V2136A in debug mode...
√  Built build\app\outputs\flutter-apk\app-debug.apk.
W/Choreographer( 5359): Frame time is 9.2E-4 ms in the future!  Check that graphics HAL is generating vsync timestamps using the correct timebase.
Connecting to VM Service at ws://127.0.0.1:63961/MYCg_X3gwRA=/ws
I/amera_x_example( 5359): Compiler allocated 5703KB to compile void android.view.ViewRootImpl.performTraversals()
I/CameraManagerGlobal( 5359): Connecting to camera service
D/CameraRepository( 5359): Added camera: 0
I/Camera2CameraInfo( 5359): Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_3
D/CameraRepository( 5359): Added camera: 1
I/Camera2CameraInfo( 5359): Device Level: INFO_SUPPORTED_HARDWARE_LEVEL_3
D/CameraValidator( 5359): Verifying camera lens facing on PD2136, lensFacingInteger: null
D/AndroidRuntime( 5359): Shutting down VM
E/AndroidRuntime( 5359): FATAL EXCEPTION: main
E/AndroidRuntime( 5359): Process: dev.hebei.camera_x_example, PID: 5359
E/AndroidRuntime( 5359): java.lang.reflect.UndeclaredThrowableException
E/AndroidRuntime( 5359):    at $Proxy3.run(Unknown Source)
E/AndroidRuntime( 5359):    at android.os.Handler.handleCallback(Handler.java:942)
E/AndroidRuntime( 5359):    at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime( 5359):    at android.os.Looper.loopOnce(Looper.java:223)
E/AndroidRuntime( 5359):    at android.os.Looper.loop(Looper.java:324)
E/AndroidRuntime( 5359):    at android.app.ActivityThread.main(ActivityThread.java:8546)
E/AndroidRuntime( 5359):    at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime( 5359):    at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:582)
E/AndroidRuntime( 5359):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1061)
E/AndroidRuntime( 5359): Caused by: com.github.dart_lang.jni.PortProxy$DartException: Exception in Java code called through JNI: java.lang.IllegalStateException: Not in application's main thread
E/AndroidRuntime( 5359): 
E/AndroidRuntime( 5359): java.lang.IllegalStateException: Not in application's main thread
E/AndroidRuntime( 5359):    at androidx.core.util.Preconditions.checkState(Preconditions.java:151)
E/AndroidRuntime( 5359):    at androidx.camera.core.impl.utils.Threads.checkMainThread(Threads.java:50)
E/AndroidRuntime( 5359):    at androidx.camera.view.LifecycleCameraController.bindToLifecycle(LifecycleCameraController.java:88)
2
E/AndroidRuntime( 5359): 
V/ActivityThread( 5359): updateVmProcessStateForGc sceneId =1 state=196608
I/amera_x_example( 5359): Close vivo delay for GC JIT .
Lost connection to device.

Exited.

I can resolve this error if I create a kotlin wrapper class and call the runOnUiThread or MainExecutor.execute on the Android side, then use jnigen to generate dart bindings to the wrapper classs. Then call the method through dart bindings is OK.

I don't know why, but It will be excellent if we can run methods on Android UI thread form dart side.

HosseinYousefi commented 6 months ago

Now that https://github.com/flutter/engine/pull/48551 has landed, you can do

runOnPlatformThread(() {
  final runnable = jni.Runnable.implement(
    jni.$RunnableImpl(
      run: () => jController.bindToLifecycle(currentActivity),
    ),
  );
  // ... 
  runnable.run();
});

Edit: It seems to now be runOnPlatformThread.

yanshouwang commented 6 months ago

Wow, I'll look at this and try it right now!

yanshouwang commented 6 months ago

I can't find this api with flutter master channel, seems it will take some time to be available.

HosseinYousefi commented 6 months ago

I can't find this api with flutter master channel, seems it will take some time to be available.

Yes, it was only merged two days ago.

mkustermann commented 6 months ago

I think https://github.com/engine-flutter-autoroll/flutter/commit/f31a81a3185440c862010b4662c756aa26c03cdf was rolled into flutter/flutter in https://github.com/flutter/flutter/commit/b7b289cddb92ea5887dc4e29a6791186e5c6513b and should be available on flutter/flutter's master/main channel

yanshouwang commented 6 months ago

I think engine-flutter-autoroll/flutter@f31a81a was rolled into flutter/flutter in flutter/flutter@b7b289c and should be available on flutter/flutter's master/main channel

I can find the platform_isolate.dart part define in ui.dart file image But I can't find it in the ui folder image