Skip to content

Commit 57c9bcc

Browse files
authored
[jnigen] Initial JNI support (#11)
1 parent 1903aa2 commit 57c9bcc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+8622
-447
lines changed

.github/workflows/test-package.yml

Lines changed: 69 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,27 +72,58 @@ jobs:
7272
uses: coverallsapp/[email protected]
7373
with:
7474
github-token: ${{ secrets.GITHUB_TOKEN }}
75+
flag-name: jni_gen_tests
76+
parallel: true
7577
path-to-lcov: ./jni_gen/coverage/lcov.info
7678

77-
build_jni_example_linux:
79+
## TODO: More minimal test on windows after fixing dev dependency.
80+
## i.e do not rerun analyze and format steps, and do not require flutter.
81+
## IssueRef: https://github.com/dart-lang/jni_gen/issues/15
82+
83+
test_jni:
7884
runs-on: ubuntu-latest
7985
defaults:
8086
run:
81-
working-directory: pkgs/jni/example
87+
working-directory: pkgs/jni
8288
steps:
8389
- uses: actions/checkout@v3
90+
## Requires flutter to analyze example.
91+
## Using dart alone doesn't work.
8492
- uses: subosito/flutter-action@v2
8593
with:
8694
channel: 'stable'
95+
- uses: actions/setup-java@v2
96+
with:
97+
distribution: 'zulu'
98+
java-version: '11'
8799
- run: |
88100
sudo apt-get update -y
89101
sudo apt-get install -y ninja-build libgtk-3-dev
90-
- run: flutter config --enable-linux-desktop
102+
- run: dart pub get
103+
- run: dart run bin/setup.dart
91104
- run: flutter pub get
92-
- run: flutter build linux
105+
- name: Check formatting
106+
run: flutter format --output=none --set-exit-if-changed .
107+
- name: Run lints
108+
run: flutter analyze --fatal-infos
109+
- name: Get dependencies
110+
run: dart pub get
111+
- name: Run tests
112+
run: dart test
113+
- name: Install coverage
114+
run: dart pub global activate coverage
115+
- name: Collect coverage
116+
run: dart pub global run coverage:test_with_coverage
117+
- name: Upload coverage
118+
uses: coverallsapp/[email protected]
119+
with:
120+
github-token: ${{ secrets.GITHUB_TOKEN }}
121+
flag-name: jni_tests
122+
parallel: true
123+
path-to-lcov: ./jni/coverage/lcov.info
93124

94-
build_jni_example_windows:
95-
runs-on: windows-latest
125+
build_jni_example_linux:
126+
runs-on: ubuntu-latest
96127
defaults:
97128
run:
98129
working-directory: pkgs/jni/example
@@ -101,12 +132,24 @@ jobs:
101132
- uses: subosito/flutter-action@v2
102133
with:
103134
channel: 'stable'
104-
- run: flutter config --enable-windows-desktop
135+
- uses: actions/setup-java@v2
136+
with:
137+
distribution: 'zulu'
138+
java-version: '11'
139+
- run: |
140+
sudo apt-get update -y
141+
sudo apt-get install -y ninja-build libgtk-3-dev
142+
- run: dart pub get
143+
working-directory: pkgs/jni
144+
- run: dart run bin/setup.dart
145+
working-directory: pkgs/jni
146+
- run: flutter config --enable-linux-desktop
105147
- run: flutter pub get
106-
- run: flutter build windows
148+
- run: flutter test
149+
- run: flutter build linux
107150

108-
build_jni_example_macos:
109-
runs-on: macos-latest
151+
build_jni_example_windows:
152+
runs-on: windows-latest
110153
defaults:
111154
run:
112155
working-directory: pkgs/jni/example
@@ -115,10 +158,13 @@ jobs:
115158
- uses: subosito/flutter-action@v2
116159
with:
117160
channel: 'stable'
118-
architecture: x64
119-
- run: flutter config --enable-macos-desktop
161+
- uses: actions/setup-java@v2
162+
with:
163+
distribution: 'zulu'
164+
java-version: '11'
165+
- run: flutter config --enable-windows-desktop
120166
- run: flutter pub get
121-
- run: flutter build macos
167+
- run: flutter build windows
122168

123169
build_jni_example_android:
124170
runs-on: ubuntu-latest
@@ -138,3 +184,13 @@ jobs:
138184
- run: flutter build apk
139185
- run: flutter build appbundle
140186

187+
coveralls_finish:
188+
needs: [test_jni_gen, test_jni]
189+
runs-on: ubuntu-latest
190+
steps:
191+
- name: Coveralls finished
192+
uses: coverallsapp/github-action@master
193+
with:
194+
github-token: ${{ secrets.github_token }}
195+
parallel-finished: true
196+

pkgs/jni/README.md

Lines changed: 20 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,37 @@
1-
# jni
1+
# jni (experimental module)
22

3-
A new Flutter FFI plugin project.
3+
This is a utility library to access JNI from Dart / Flutter code, intended as a supplement for `jnigen` code generator, as well as provide the common base components (such as managing the JVM instance) to the code generated by `jni_gen`.
44

5-
## Getting Started
5+
This library contains:
66

7-
This project is a starting point for a Flutter
8-
[FFI plugin](https://docs.flutter.dev/development/platform-integration/c-interop),
9-
a specialized package that includes native code directly invoked with Dart FFI.
7+
* functions to access the JNIEnv and JavaVM variables from JNI, and wrapper functions to those provided by JNI. (`Jni.getEnv`, `Jni.getJavaVM`).
108

11-
## Project stucture
9+
* Functions to spawn a JVM on desktop platforms (`Jni.spawn`).
1210

13-
This template uses the following structure:
11+
* Some utility functions to make it easier to work with JNI in Dart; eg: To convert a java string object to Dart string (mostly as extension methods on `Pointer<JniEnv>`).
1412

15-
* `src`: Contains the native source code, and a CmakeFile.txt file for building
16-
that source code into a dynamic library.
13+
* Some Android-specific helpers (get application context and current activity references).
1714

18-
* `lib`: Contains the Dart code that defines the API of the plugin, and which
19-
calls into the native code using `dart:ffi`.
15+
* Some helper classes and functions to simplify one-off uses (`JniObject` and `JniClass` intended for calling functions by specifying the name and arguments. It will reduce some boilerplate when you're debugging. Note: this API is slightly incomplete).
2016

21-
* platform folders (`android`, `ios`, `windows`, etc.): Contains the build files
22-
for building and bundling the native code library with the platform application.
17+
This is intended for one-off / debugging uses of JNI, as well as providing a base library for code generated by jni_gen.
2318

24-
## Buidling and bundling native code
19+
__To interface a complete java library, look forward for `jni_gen`.__
2520

26-
The `pubspec.yaml` specifies FFI plugins as follows:
21+
## Platform support
22+
The focus of this project is Flutter Android, since Flutter Android apps already have a JVM, and JNI enables interop with existing Java code and Android Platform APIs. This project also (partially) supports Linux desktop by spawning a JVM through JNI.
2723

28-
```yaml
29-
plugin:
30-
platforms:
31-
some_platform:
32-
ffiPlugin: true
33-
```
24+
## Version note
25+
This library is at an early stage of development and we do not provide backwards compatibility of the API at this point.
3426

35-
This configuration invokes the native build for the various target platforms
36-
and bundles the binaries in Flutter applications using these FFI plugins.
27+
## Documentation
28+
The test/ directory contains files with comments explaining the basics of this module, and the example/ directory contains a flutter example which also touches some Android-specifics.
3729

38-
This can be combined with dartPluginClass, such as when FFI is used for the
39-
implementation of one platform in a federated plugin:
30+
Using this library assumes some familiarity with JNI - it's threading model and object references, among other things.
4031

41-
```yaml
42-
plugin:
43-
implements: some_other_plugin
44-
platforms:
45-
some_platform:
46-
dartPluginClass: SomeClass
47-
ffiPlugin: true
48-
```
32+
## jni_gen
4933

50-
A plugin can have both FFI and method channels:
34+
This library is a part of `jni_gen` - a 2022 GSoC project.
5135

52-
```yaml
53-
plugin:
54-
platforms:
55-
some_platform:
56-
pluginClass: SomeName
57-
ffiPlugin: true
58-
```
59-
60-
The native build systems that are invoked by FFI (and method channel) plugins are:
61-
62-
* For Android: Gradle, which invokes the Android NDK for native builds.
63-
* See the documentation in android/build.gradle.
64-
* For iOS and MacOS: Xcode, via CocoaPods.
65-
* See the documentation in ios/jni.podspec.
66-
* See the documentation in macos/jni.podspec.
67-
* For Linux and Windows: CMake.
68-
* See the documentation in linux/CMakeLists.txt.
69-
* See the documentation in windows/CMakeLists.txt.
70-
71-
## Binding to native code
72-
73-
To use the native code, bindings in Dart are needed.
74-
To avoid writing these by hand, they are generated from the header file
75-
(`src/jni.h`) by `package:ffigen`.
76-
Regenerate the bindings by running `flutter pub run ffigen --config ffigen.yaml`.
77-
78-
## Invoking native code
79-
80-
Very short-running native functions can be directly invoked from any isolate.
81-
For example, see `sum` in `lib/jni.dart`.
82-
83-
Longer-running functions should be invoked on a helper isolate to avoid
84-
dropping frames in Flutter applications.
85-
For example, see `sumAsync` in `lib/jni.dart`.
86-
87-
## Flutter help
88-
89-
For help getting started with Flutter, view our
90-
[online documentation](https://flutter.dev/docs), which offers tutorials,
91-
samples, guidance on mobile development, and a full API reference.
36+
The broader aim of jni_gen is making Java APIs accessible from dart in an idiomatic way.
9237

pkgs/jni/analysis_options.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,13 @@
11
include: package:flutter_lints/flutter.yaml
22

3+
analyzer:
4+
exclude: [build/**]
5+
language:
6+
strict-raw-types: true
7+
8+
linter:
9+
rules:
10+
- prefer_final_locals
11+
- prefer_const_declarations
312
# Additional information about this file can be found at
413
# https://dart.dev/guides/language/analysis-options
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package dev.dart.jni;
2+
3+
import androidx.annotation.Keep;
4+
import androidx.annotation.NonNull;
5+
import android.util.Log;
6+
import android.app.Activity;
7+
import io.flutter.plugin.common.PluginRegistry.Registrar;
8+
import io.flutter.embedding.engine.plugins.FlutterPlugin;
9+
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
10+
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
11+
12+
import android.content.Context;
13+
14+
@Keep
15+
public class JniPlugin implements FlutterPlugin, ActivityAware {
16+
17+
@Override
18+
public void
19+
onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
20+
setup(binding.getApplicationContext());
21+
}
22+
23+
public static void registerWith(Registrar registrar) {
24+
JniPlugin plugin = new JniPlugin();
25+
plugin.setup(registrar.activeContext());
26+
}
27+
28+
private void setup(Context context) {
29+
initializeJni(context, getClass().getClassLoader());
30+
}
31+
32+
@Override
33+
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
34+
35+
// Activity handling methods
36+
@Override
37+
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
38+
Activity activity = binding.getActivity();
39+
setJniActivity(activity, activity.getApplicationContext());
40+
}
41+
42+
@Override
43+
public void onDetachedFromActivityForConfigChanges() {}
44+
45+
@Override
46+
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
47+
Activity activity = binding.getActivity();
48+
setJniActivity(activity, activity.getApplicationContext());
49+
}
50+
51+
@Override
52+
public void onDetachedFromActivity() {}
53+
54+
native void initializeJni(Context context, ClassLoader classLoader);
55+
native void setJniActivity(Activity activity, Context context);
56+
57+
static {
58+
System.loadLibrary("dartjni");
59+
}
60+
}
61+

0 commit comments

Comments
 (0)