Skip to content

Commit 33a33b2

Browse files
authored
[jnigen] migrate in_app_java example to Dart API (#2671)
1 parent 2760e5d commit 33a33b2

File tree

7 files changed

+89
-78
lines changed

7 files changed

+89
-78
lines changed
Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,31 @@
11
# In-App Java Example
22

33
This example shows how to write custom java code in `android/app/src` and call
4-
it using `jnigen` generated bindings.
4+
it using bindings generated by `jnigen`.
55

6-
#### How to run this example:
6+
### How to run this example:
77

88
- Run `flutter run` to run the app.
99

1010
- To regenerate bindings after changing Java code, run
11-
`flutter pub run jnigen --config jnigen.yaml`. This requires at least one APK
12-
build to have been run before, so that it's possible for `jnigen` to obtain
13-
classpaths of Android Gradle libraries. Therefore, once run
14-
`flutter build apk` before generating bindings for the first time, or after a
15-
`flutter clean`.
11+
`dart run tool/jnigen.dart`. This requires at least one APK build to have
12+
been run before, so that `jnigen` can obtain classpaths of Android Gradle
13+
libraries. Therefore, run `flutter build apk` once before generating bindings
14+
for the first time, or after a `flutter clean`.
1615

17-
#### General steps
16+
### General steps
1817

19-
These are general steps to integrate Java code into a flutter project using
18+
These are general steps to integrate Java code into a Flutter project using
2019
`jnigen`.
2120

22-
- Write Java code in suitable package folder, under `android/` subproject of the
23-
flutter app.
24-
- Create A jnigen config like `jnigen.yaml` in this example.
25-
- Generate bindings using jnigen config.
26-
- Add proguard rules to exclude your custom classes from tree shaking, since
27-
they are always accessed reflectively in JNI.
21+
- Place Java code in a suitable package folder under the `android/` subproject
22+
of the flutter app.
23+
- Create a `jnigen` configuration script like `tool/jnigen.dart` in this
24+
example.
25+
- Generate bindings by running that script with `dart run`.
26+
- To prevent tree shaking of your custom classes (which are always accessed
27+
reflectively in JNI) use either the `@Keep` annotation
28+
(`androidx.annotation.Keep`) for code written in the application itself, or
29+
a [proguard-rules file](https://github.com/dart-lang/native/blob/main/pkgs/jnigen/example/in_app_java/android/app/proguard-rules.pro)
30+
for external libraries.
2831
- Build and run the app.

pkgs/jnigen/example/in_app_java/jnigen.yaml

Lines changed: 0 additions & 16 deletions
This file was deleted.
File renamed without changes.

pkgs/jnigen/example/in_app_java/lib/main.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import 'package:jni/jni.dart';
77

88
// The hierarchy created in generated code will mirror the java package
99
// structure.
10-
import 'android_utils.dart';
10+
import 'android_utils.g.dart';
1111

1212
JObject activity = JObject.fromReference(Jni.getCurrentActivity());
1313
JObject context = JObject.fromReference(Jni.getCachedApplicationContext());

pkgs/jnigen/example/in_app_java/pubspec.yaml

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@ dependencies:
1515
sdk: flutter
1616
jni:
1717
path: ../../../jni/
18-
19-
# The following adds the Cupertino Icons font to your application.
20-
# Use with the CupertinoIcons class for iOS style icons.
2118
cupertino_icons: ^1.0.2
2219

2320
dev_dependencies:
@@ -29,39 +26,5 @@ dev_dependencies:
2926
flutter_lints: ^6.0.0
3027

3128
flutter:
32-
33-
# The following line ensures that the Material Icons font is
34-
# included with your application, so that you can use the icons in
35-
# the material Icons class.
3629
uses-material-design: true
3730

38-
# To add assets to your application, add an assets section, like this:
39-
# assets:
40-
# - images/a_dot_burr.jpeg
41-
# - images/a_dot_ham.jpeg
42-
43-
# An image asset can refer to one or more resolution-specific "variants", see
44-
# https://flutter.dev/assets-and-images/#resolution-aware
45-
46-
# For details regarding adding assets from package dependencies, see
47-
# https://flutter.dev/assets-and-images/#from-packages
48-
49-
# To add custom fonts to your application, add a fonts section here,
50-
# in this "flutter" section. Each entry in this list should have a
51-
# "family" key with the font family name, and a "fonts" key with a
52-
# list giving the asset and other descriptors for the font. For
53-
# example:
54-
# fonts:
55-
# - family: Schyler
56-
# fonts:
57-
# - asset: fonts/Schyler-Regular.ttf
58-
# - asset: fonts/Schyler-Italic.ttf
59-
# style: italic
60-
# - family: Trajan Pro
61-
# fonts:
62-
# - asset: fonts/TrajanPro.ttf
63-
# - asset: fonts/TrajanPro_Bold.ttf
64-
# weight: 700
65-
#
66-
# For details regarding fonts from package dependencies,
67-
# see https://flutter.dev/custom-fonts/#from-packages
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import 'dart:io';
2+
3+
import 'package:jnigen/jnigen.dart';
4+
5+
void main(List<String> args) {
6+
final packageRoot = Platform.script.resolve('../');
7+
generateJniBindings(
8+
Config(
9+
outputConfig: OutputConfig(
10+
dartConfig: DartCodeOutputConfig(
11+
path: packageRoot.resolve('lib/android_utils.g.dart'),
12+
structure: OutputStructure.singleFile,
13+
),
14+
),
15+
androidSdkConfig: AndroidSdkConfig(addGradleDeps: true),
16+
sourcePath: [packageRoot.resolve('android/app/src/main/java')],
17+
classes: [
18+
'com.example.in_app_java', // Generate the entire package
19+
'androidx.emoji2.text.EmojiCompat', // From gradle's compile classpath
20+
'androidx.emoji2.text.DefaultEmojiCompatConfig', // From gradle's compile classpath
21+
'android.os.Build', // from gradle's compile classpath
22+
'java.util.HashMap', // from gradle's compile classpath
23+
],
24+
),
25+
);
26+
}

pkgs/jnigen/test/regenerate_examples_test.dart

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ import 'test_util/test_util.dart';
1414
/// Generates bindings using jnigen config in [exampleName] and compares
1515
/// them to provided reference outputs.
1616
///
17-
/// [dartOutput] and [cOutput] are relative paths from example project dir.
17+
/// [dartOutput] is a relative path from the example project dir.
1818
///
1919
/// Pass [isLargeTest] as true if the test will take considerable time.
20-
void testExample(String exampleName, String dartOutput, String? cOutput,
20+
void testExample(String exampleName, String dartOutput,
2121
{bool isLargeTest = false}) {
2222
test(
2323
'Generate and compare bindings for $exampleName',
@@ -37,30 +37,65 @@ void testExample(String exampleName, String dartOutput, String? cOutput,
3737
);
3838
}
3939

40+
void testDartApiExample(
41+
{required String exampleName,
42+
required String generatorScriptPath,
43+
required String outputPath,
44+
bool isLargeTest = false}) {
45+
test(
46+
'Generate and compare bindings for $exampleName',
47+
timeout: const Timeout.factor(3),
48+
() async {
49+
final examplePath = join('example', exampleName);
50+
try {
51+
final generatorResult = await Process.run(
52+
Platform.resolvedExecutable, ['run', generatorScriptPath],
53+
workingDirectory: examplePath);
54+
if ((generatorResult.stderr as String)
55+
.contains('Gradle execution failed.')) {
56+
stderr.writeln('Skip: $exampleName');
57+
return;
58+
}
59+
final processResults = await Process.run(
60+
'git', ['diff', '--exit-code', outputPath],
61+
workingDirectory: examplePath);
62+
if (processResults.exitCode == 1) {
63+
fail('The checked-in bindings of $exampleName are out of date. Run '
64+
'the generator script ($generatorScriptPath) and commit the '
65+
'changes.\n\n${processResults.stdout}');
66+
} else if (processResults.exitCode != 0) {
67+
throw Exception(
68+
'Invocation of "git diff" failed:\n${processResults.stderr}');
69+
}
70+
} on GradleException catch (_) {
71+
stderr.writeln('Skip: $exampleName');
72+
}
73+
},
74+
tags: isLargeTest ? largeTestTag : null,
75+
);
76+
}
77+
4078
void main() async {
4179
await checkLocallyBuiltDependencies();
42-
testExample(
43-
'in_app_java',
44-
join('lib', 'android_utils.dart'),
45-
join('src', 'android_utils'),
80+
testDartApiExample(
81+
exampleName: 'in_app_java',
82+
generatorScriptPath: 'tool/jnigen.dart',
83+
outputPath: join('lib', 'android_utils.g.dart'),
4684
isLargeTest: true,
4785
);
4886
testExample(
4987
'pdfbox_plugin',
5088
join('lib', 'src', 'third_party'),
51-
'src',
5289
isLargeTest: false,
5390
);
5491
testExample(
5592
'notification_plugin',
5693
join('lib', 'notifications.dart'),
57-
'src',
5894
isLargeTest: true,
5995
);
6096
testExample(
6197
'kotlin_plugin',
6298
join('lib', 'kotlin_bindings.dart'),
63-
'src',
6499
isLargeTest: true,
65100
);
66101
}

0 commit comments

Comments
 (0)