Skip to content

About

Developer-friendly JNI bindings for Android, powered by native AGP code generation and inspired by Chromium JNI Zero—no Python scripts or manual mappings required.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Android JNI Zero

面向 Android/Gradle 的轻量 JNI Zero 生成方案。

本工程沿用 Chromium JNI Zero 的注解、C++ 调用约定和 runtime,但把 Python 生成链路 替换为 Java 17 Gradle 插件。它适合仓库内的 application/library 模块复用,目前不作为 通用 Maven 插件发布。

接入一个模块后,新增 JNI 类通常只改两个地方:

  1. Java 中声明 @NativeMethods / @CalledByNative。
  2. C++ 中实现 JNI_<Class>_<Method>。

插件负责:

  • 扫描 Java AST,生成 <Class>Jni.java 和 C++ headers;
  • 把生成的 Java 加入 AGP,把 headers 接到对应 CMake target;
  • 按模块和 source set 隔离产物,不维护逐类 binding 清单;
  • 用生成宏在 C++ 编译期检查缺失实现和签名错误;
  • 为 Android library 提供 R8 consumer rules;
  • 生成可缓存的 Gradle task,不依赖 Python 或 Chromium checkout。

如果 C++ 实现放在 target 已编译的源码中,同一个 source set 新增 JNI 类时不需要再修改 Gradle 或 CMake。

运行示例

需要 JDK 17、Android SDK 36、NDK 27.0.12077973 和 CMake 3.22.1。

./gradlew :app:assembleDebug

APK 位于 app/build/outputs/apk/debug/app-debug.apk。示例覆盖双向调用、全部支持类型, 以及 application 与多个 Android library 各自加载 .so。

接入一个模块

模块只需要配置一次。完整范例可直接参考 libraries/jni-alpha。

1. 应用插件

plugins {
    id("com.android.application") // Android library 使用 com.android.library
    id("dev.chromium.jni-zero")
}

dependencies {
    implementation(project(":jni-zero-api"))
}

jniZero {
    nativeLibrary("feature_jni")
}

nativeLibrary 是实现该模块 JNI 方法的 CMake target。Android library 通常使用 compileOnly(project(":jni-zero-api")),避免多个 AAR 重复打包注解类。

模块的常规 Android/NDK 配置还需要:

  • 使用 Java 17 和 C++20;
  • 把 third_party/jni_zero 作为 JNI_ZERO_ROOT 传给 CMake;
  • 配置 externalNativeBuild。

仓库内可复制的配置见 libraries/jni-alpha/build.gradle.kts。

2. 声明 Java 边界

package com.example.feature;

import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;

@JNINamespace("feature")
public final class FeatureBridge {
    static {
        System.loadLibrary("feature_jni");
    }

    private FeatureBridge() {}

    public static int add(int left, int right) {
        return FeatureBridgeJni.get().add(left, right);
    }

    public static String greeting(String value) {
        return FeatureBridgeJni.get().greet(value);
    }

    @CalledByNative
    private static String decorate(String value) {
        return "Java callback: " + value;
    }

    @NativeMethods
    interface Natives {
        int add(int left, int right);

        String greet(String value);
    }
}

FeatureBridgeJni 不需要手写,它会在构建时生成。

3. 实现 C++ 边界

#include "third_party/jni_zero/jni_zero.h"

#include "FeatureBridge_jni.h"

namespace feature {

static int32_t JNI_FeatureBridge_Add(int32_t left, int32_t right) {
  return left + right;
}

static jni_zero::ScopedJavaLocalRef<jstring> JNI_FeatureBridge_Greet(
    JNIEnv* env,
    const jni_zero::JavaRef<jstring>& value) {
  return Java_FeatureBridge_decorate(env, value);
}

}  // namespace feature

DEFINE_JNI(FeatureBridge)

extern "C" __attribute__((visibility("default"))) jint JNI_OnLoad(
    JavaVM* vm,
    void*) {
  jni_zero::InitVM(vm);
  return JNI_VERSION_1_6;
}

DEFINE_JNI(FeatureBridge) 会把生成的 JNI boundary 展开,并在编译期检查上面的实现。

4. 连接 CMake target

模块需要一个 jni_zero_runtime target。业务 target 在链接 runtime 后,包含插件生成的 manifest:

add_library(feature_jni SHARED feature_bridge.cc)
target_link_libraries(feature_jni PRIVATE jni_zero_runtime)

# 必须放在相关 add_library() 之后
include("${JNI_ZERO_LIBRARIES_FILE}")

runtime sources、include directory 和 Android log library 的完整配置见 app/src/main/cpp/CMakeLists.txt。

5. 构建

./gradlew :your-module:generateJniZero
./gradlew :your-module:assembleDebug

生成文件和诊断报告位于模块的 build/generated/jniZero/ 与 build/reports/jniZero/,不需要提交。需要审阅的稳定基线放在 src/test/jniZeroGolden/。

多目录和多 native library

main 默认扫描 src/main/java。一个模块需要额外 Java 目录或多个 CMake target 时, 增加 source set:

jniZero {
    nativeLibrary("feature_core_jni")

    sourceSet("media") {
        javaSources("src/mediaJni/java")
        nativeLibrary("feature_media_jni")
    }
}

插件只把 media 生成的 headers 提供给 feature_media_jni。更多约束和任务名称见 JNI source sets。

支持范围

当前支持:

  • nested @NativeMethods interface;
  • static @CalledByNative;
  • @JNINamespace;
  • primitive、String、Object 和 Object[];
  • Java → C++ 与 C++ → Java;
  • 多 Android module、模块内多 source set/native library。

一个类只能有一个 @NativeMethods interface,native 和 callback 方法不能重载。 instance callback、primitive array、任意业务对象转换、variant 专属 JNI 和 JAR/AAR 扫描尚不支持。错误会在生成阶段指出源码路径、行和列。

本工程选择 per-file natives,不实现 Chromium 的全程序 registration、GEN_JNI、短名 和 multiplexing。这样牺牲一部分极限体积优化,换取更简单的 Gradle/CMake 接入。 详细取舍见 设计说明。

验证

# JVM parser/emitter
./gradlew -p build-logic :jni-zero-codegen:check

# Android 构建、C++ 签名检查和 golden
./gradlew -PjniZeroAbis=arm64-v8a :app:assembleDebug :app:check

# Release + R8
./gradlew -PjniZeroAbis=arm64-v8a :app:assembleRelease

设备测试、16 KB 对齐和 CI 覆盖见 测试说明。 生成器内部维护见 Codegen,待办见 Roadmap。

许可证

本仓库自行实现的代码使用 Apache License 2.0。 third_party/jni_zero 保留 Chromium 上游版权和 BSD 许可证,同步方式见 third_party/jni_zero/README.md。

About

Developer-friendly JNI bindings for Android, powered by native AGP code generation and inspired by Chromium JNI Zero—no Python scripts or manual mappings required.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages