Our package:jnigens purpose is to produce dart bindings for Java/Kotlin APIs based on .jar files or java/kotlin sources. That is useful in general - but our main motivation for building this is to allow easy interop with Java/Kotlin APIs on Android in the context of Flutter.
There are some specifics to this that we should keep in mind:
Thread restricted APIs
Certain Android APIs are only usable from the platform UI thread or worker threads. The APIs may indicate that via a @UiThread or @WorkerThread annotation.
Once we have a mechanism to run code on the platform UI thread, the bindings generator could take advantage of that mechanism (e.g. by producing asynchronous Future-based APIs that perform the actual call on the platform thread)
APIs requiring Context / Activity / ... objects
Certain Android APIs require callers to pass along special objects. Among them e.g. android.content.Context objects. There's different kinds of context objects (most importantly: application context, activity contexts, service contexts). Those special objects have lifecycles during which they can be used (e.g. while an activity is active).
Flutter code (on UI isolate or background isolate) that wishes to invoke Android APIs may need access to such special objects. We'll probably need a way to obtain those and expose them to Dart so they can be used when invoking the generated Android API bindings. This may require integration into flutter engine and/or making a flutter-plugin with method channels that exposes them.
Normal Flutter apps are structured in the following way: The application code extends FlutterActivity. Once the activitie's onCreate() lifecycle method is invoked, it will create a FlutterEngine object with the activity context. Once plugins are attached to an engine, they will get notified when the engine gets attached/detached to/from activities/services/... The plugins do have - in those lifecycle callbacks - access to e.g. context / activity objects.
We should also think about whether there can be possible races happening in the lifecycle of those objects and the usage of those from Dart.
Testing
Given the nature of method channels - which are similar to e.g. gRPC (i.e. generated message classes and RPC stubs) - one could mock the native side using Dart code rather easily - which may be amenable to unit tests. We should think about how using direct interop with native impacts testability of code.
Our
package:jnigen
s purpose is to produce dart bindings for Java/Kotlin APIs based on.jar
files or java/kotlin sources. That is useful in general - but our main motivation for building this is to allow easy interop with Java/Kotlin APIs on Android in the context of Flutter.There are some specifics to this that we should keep in mind:
Thread restricted APIs
Certain Android APIs are only usable from the platform UI thread or worker threads. The APIs may indicate that via a @UiThread or @WorkerThread annotation.
Once we have a mechanism to run code on the platform UI thread, the bindings generator could take advantage of that mechanism (e.g. by producing asynchronous
Future
-based APIs that perform the actual call on the platform thread)APIs requiring Context / Activity / ... objects
Certain Android APIs require callers to pass along special objects. Among them e.g. android.content.Context objects. There's different kinds of context objects (most importantly: application context, activity contexts, service contexts). Those special objects have lifecycles during which they can be used (e.g. while an activity is active).
Flutter code (on UI isolate or background isolate) that wishes to invoke Android APIs may need access to such special objects. We'll probably need a way to obtain those and expose them to Dart so they can be used when invoking the generated Android API bindings. This may require integration into flutter engine and/or making a flutter-plugin with method channels that exposes them.
Normal Flutter apps are structured in the following way: The application code extends
FlutterActivity
. Once the activitie'sonCreate()
lifecycle method is invoked, it will create aFlutterEngine
object with the activity context. Once plugins are attached to an engine, they will get notified when the engine gets attached/detached to/from activities/services/... The plugins do have - in those lifecycle callbacks - access to e.g. context / activity objects.We should also think about whether there can be possible races happening in the lifecycle of those objects and the usage of those from Dart.
Testing
Given the nature of method channels - which are similar to e.g. gRPC (i.e. generated message classes and RPC stubs) - one could mock the native side using Dart code rather easily - which may be amenable to unit tests. We should think about how using direct interop with native impacts testability of code.
/cc @HosseinYousefi