jni 0.13.0 copy "jni: ^0.13.0" to clipboard
jni: ^0.13.0 copied to clipboard

A library to access JNI from Dart and Flutter that acts as a support library for package:jnigen.

example/lib/main.dart

// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

// ignore_for_file: library_private_types_in_public_api

import 'dart:ffi';
import 'dart:io';

import 'package:ffi/ffi.dart';
import 'package:flutter/material.dart';
import 'package:jni/jni.dart';

extension on String {
  /// Returns a Utf-8 encoded `Pointer<Char>` with contents same as this string.
  Pointer<Char> toNativeChars(Allocator allocator) {
    return toNativeUtf8(allocator: allocator).cast<Char>();
  }
}

// An example of calling JNI methods using low level primitives.
// GlobalJniEnv is a thin abstraction over JNIEnv in JNI C API.
//
// For a more ergonomic API for common use cases of calling methods and
// accessing fields, see next examples using JObject and JClass.
String toJavaStringUsingEnv(int n) => using((arena) {
      final env = Jni.env;
      final cls = env.FindClass("java/lang/String".toNativeChars(arena));
      final mId = env.GetStaticMethodID(cls, "valueOf".toNativeChars(arena),
          "(I)Ljava/lang/String;".toNativeChars(arena));
      final i = arena<JValue>();
      i.ref.i = n;
      final res = env.CallStaticObjectMethodA(cls, mId, i);
      final str = env.toDartString(res);
      env.DeleteGlobalRef(res);
      env.DeleteGlobalRef(cls);
      return str;
    });

int randomUsingEnv(int n) => using((arena) {
      final env = Jni.env;
      final randomCls = env.FindClass("java/util/Random".toNativeChars(arena));
      final ctor = env.GetMethodID(
          randomCls, "<init>".toNativeChars(arena), "()V".toNativeChars(arena));
      final random = env.NewObject(randomCls, ctor);
      final nextInt = env.GetMethodID(randomCls, "nextInt".toNativeChars(arena),
          "(I)I".toNativeChars(arena));
      final res =
          env.CallIntMethodA(random, nextInt, toJValues([n], allocator: arena));
      env.DeleteGlobalRef(randomCls);
      env.DeleteGlobalRef(random);
      return res;
    });
double randomDouble() {
  final math = JClass.forName("java/lang/Math");
  final random =
      math.staticMethodId("random", "()D").call(math, jdouble.type, []);
  math.release();
  return random;
}

int uptime() {
  return JClass.forName("android/os/SystemClock").use(
    (systemClock) => systemClock
        .staticMethodId("uptimeMillis", "()J")
        .call(systemClock, jlong.type, []),
  );
}

String backAndForth() {
  final jstring = '🪓'.toJString();
  final dartString = jstring.toDartString(releaseOriginal: true);
  return dartString;
}

void quit() {
  JObject.fromReference(Jni.getCurrentActivity()).use((ac) =>
      ac.jClass.instanceMethodId("finish", "()V").call(ac, jvoid.type, []));
}

void showToast(String text) {
  // This is example for calling your app's custom java code.
  // Place the Toaster class in the app's android/ source Folder, with a Keep
  // annotation or appropriate proguard rules to retain classes in release mode.
  //
  // In this example, Toaster class wraps android.widget.Toast so that it
  // can be called from any thread. See
  // android/app/src/main/java/com/github/dart_lang/jni_example/Toaster.java
  final toasterClass =
      JClass.forName('com/github/dart_lang/jni_example/Toaster');
  final makeText = toasterClass.staticMethodId(
      'makeText',
      '(Landroid/app/Activity;Landroid/content/Context;'
          'Ljava/lang/CharSequence;I)'
          'Lcom/github/dart_lang/jni_example/Toaster;');
  final toaster = makeText.call(toasterClass, JObject.type, [
    Jni.getCurrentActivity(),
    Jni.getCachedApplicationContext(),
    '😀'.toJString(),
    0,
  ]);
  final show = toasterClass.instanceMethodId('show', '()V');
  show(toaster, jvoid.type, []);
}

void main() {
  if (!Platform.isAndroid) {
    Jni.spawn();
  }
  final examples = [
    Example("String.valueOf(1332)", () => toJavaStringUsingEnv(1332)),
    Example("Generate random number", () => randomUsingEnv(180),
        runInitially: false),
    Example("Math.random()", () => randomDouble(), runInitially: false),
    if (Platform.isAndroid) ...[
      Example("Minutes of usage since reboot",
          () => (uptime() / (60 * 1000)).floor()),
      Example("Back and forth string conversion", () => backAndForth()),
      Example("Device name", () {
        final buildClass = JClass.forName("android/os/Build");
        return buildClass
            .staticFieldId("DEVICE", JString.type.signature)
            .get(buildClass, JString.type)
            .toDartString(releaseOriginal: true);
      }),
      Example(
        "Package name",
        () => JObject.fromReference(Jni.getCurrentActivity()).use((activity) =>
            activity.jClass
                .instanceMethodId("getPackageName", "()Ljava/lang/String;")
                .call(activity, JString.type, [])),
      ),
      Example("Show toast", () => showToast("Hello from JNI!"),
          runInitially: false),
      Example(
        "Quit",
        quit,
        runInitially: false,
      ),
    ]
  ];
  runApp(MyApp(examples));
}

class Example {
  String title;
  dynamic Function() callback;
  bool runInitially;
  Example(this.title, this.callback, {this.runInitially = true});
}

class MyApp extends StatefulWidget {
  const MyApp(this.examples, {Key? key}) : super(key: key);
  final List<Example> examples;

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('JNI Examples'),
        ),
        body: ListView.builder(
            itemCount: widget.examples.length,
            itemBuilder: (context, i) {
              final eg = widget.examples[i];
              return ExampleCard(eg);
            }),
      ),
    );
  }
}

class ExampleCard extends StatefulWidget {
  const ExampleCard(this.example, {Key? key}) : super(key: key);
  final Example example;

  @override
  _ExampleCardState createState() => _ExampleCardState();
}

class _ExampleCardState extends State<ExampleCard> {
  Widget _pad(Widget w, double h, double v) {
    return Padding(
        padding: EdgeInsets.symmetric(horizontal: h, vertical: v), child: w);
  }

  bool _run = false;

  @override
  void initState() {
    super.initState();
    _run = widget.example.runInitially;
  }

  @override
  Widget build(BuildContext context) {
    final eg = widget.example;
    var result = "";
    var hasError = false;
    if (_run) {
      try {
        result = eg.callback().toString();
      } on Exception catch (e) {
        hasError = true;
        result = e.toString();
      } on Error catch (e) {
        hasError = true;
        result = e.toString();
      }
    }
    var resultStyle = const TextStyle(fontFamily: "Monospace");
    if (hasError) {
      resultStyle = const TextStyle(fontFamily: "Monospace", color: Colors.red);
    }
    return Card(
      child: Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
        Text(eg.title,
            softWrap: true,
            style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w600)),
        _pad(
            Text(result.toString(), softWrap: true, style: resultStyle), 8, 16),
        _pad(
            ElevatedButton(
              child: Text(_run ? "Run again" : "Run"),
              onPressed: () => setState(() => _run = true),
            ),
            8,
            8),
      ]),
    );
  }
}
37
likes
160
points
43.2k
downloads

Publisher

verified publisherlabs.dart.dev

Weekly Downloads

A library to access JNI from Dart and Flutter that acts as a support library for package:jnigen.

Repository (GitHub)
View/report issues
Contributing

Topics

#interop #ffi #java #kotlin #jni

Documentation

API reference

License

BSD-3-Clause (license)

Dependencies

args, ffi, meta, package_config, path, plugin_platform_interface

More

Packages that depend on jni