All Projects → LanderlYoung → Jenny

LanderlYoung / Jenny

Licence: apache-2.0
JNI glue code generator

Programming Languages

java
68154 projects - #9 most used programming language
kotlin
9241 projects
cpp
1120 projects

Projects that are alternatives of or similar to Jenny

Android Api Securekeys
Store data in a simple and secure way
Stars: ✭ 372 (+601.89%)
Mutual labels:  annotation-processor, jni
Learn
📚 codes written while learning them.
Stars: ✭ 44 (-16.98%)
Mutual labels:  jni
Jpype
JPype is cross language bridge to allow python programs full access to java class libraries.
Stars: ✭ 685 (+1192.45%)
Mutual labels:  jni
Hamcrest Pojo Matcher Generator
Autogenerated java hamcrest matchers for pojo with help of AnnotationProcessor
Stars: ✭ 31 (-41.51%)
Mutual labels:  annotation-processor
Android State
A utility library for Android to save objects in a Bundle without any boilerplate.
Stars: ✭ 857 (+1516.98%)
Mutual labels:  annotation-processor
Anyndk
🔥 Android native library, make your development faster and easier. Android各种native库,让你的开发更快更简单
Stars: ✭ 35 (-33.96%)
Mutual labels:  jni
Dart native
Write iOS&Android Code using Dart. This package liberates you from redundant glue code and low performance of Flutter Channel.
Stars: ✭ 564 (+964.15%)
Mutual labels:  jni
Hellomello
Experiments with writing Android apps in Nim
Stars: ✭ 47 (-11.32%)
Mutual labels:  jni
Jnativehook
Global keyboard and mouse listeners for Java.
Stars: ✭ 1,015 (+1815.09%)
Mutual labels:  jni
Javassembly
💾 Calling Assembly from Java: simple example using the JNI and NASM.
Stars: ✭ 28 (-47.17%)
Mutual labels:  jni
Kiss Fft
A compact FFT library in C with an Android JNI wrapper
Stars: ✭ 27 (-49.06%)
Mutual labels:  jni
Showkase
🔦 Showkase is an annotation-processor based Android library that helps you organize, discover, search and visualize Jetpack Compose UI elements
Stars: ✭ 873 (+1547.17%)
Mutual labels:  annotation-processor
Rucaja
Calling the JVM from Rust via JNI
Stars: ✭ 35 (-33.96%)
Mutual labels:  jni
Jep
Embed Python in Java
Stars: ✭ 759 (+1332.08%)
Mutual labels:  jni
Mezzanine
An annotation processor that reads files at compile time
Stars: ✭ 45 (-15.09%)
Mutual labels:  annotation-processor
Kotshi
An annotation processor that generates Moshi adapters from immutable Kotlin data classes.
Stars: ✭ 656 (+1137.74%)
Mutual labels:  annotation-processor
Koloboke
Java Collections till the last breadcrumb of memory and performance
Stars: ✭ 909 (+1615.09%)
Mutual labels:  annotation-processor
Metasra Pipeline
MetaSRA: normalized sample-specific metadata for the Sequence Read Archive
Stars: ✭ 33 (-37.74%)
Mutual labels:  annotation-processor
Zerocell
Simple, efficient Excel to POJO library for Java
Stars: ✭ 53 (+0%)
Mutual labels:  annotation-processor
Trime
同文安卓輸入法平臺3.x/Android-rime/Rime Input Method Engine for Android
Stars: ✭ 1,032 (+1847.17%)
Mutual labels:  jni

Jenny -- the JNI helper

CI Publish Download GitHub code size in bytes GitHub


Intro

Jenny is a java annotation processor, which helps you generate C/C++ code for JNI calls according to your java native class.

Jenny comes with two main part:

  1. NativeGlueGenerator: which generate skeleton C++ code for your native class/method.
  2. NativeProxyGenerator: which generate helper C++ class for you to call java APIs through JNI interface, including create new instance, call method, get/set fields, define constants.

Glue stands for c++ code to implement Java native method. (Glue java and C++.)

Proxy stands for c++ class to provide calls to java from c++. (c++ side proxy for the java class.)

And there is an extra bonus -- jnihelper.h that uses C++ RAII technology to simplify JNI APIs. When opt-in (with 'jenny.useJniHelper'=true), the generated proxy class will also add methods using jnihelper, which makes life even happier!

Why Jenny?

When writing JNI code, people usually come across APIs where java method/field/type signatures are required, some of them like JNIEnv::RegisterNatives, JNIEnv::FindClass, JNIEnv::GetMethodID, etc. It is very hard to hand-craft those signatures correctly and efficiently, so programmers often waste much time writing and debugging those boilerplate.

Jenny is now your JNI code maid, who takes care of all those boilerplate so you can be even more productive.

At a glance

Let's see what the generated code is.

You can find full code in sample-gen.

Glue

Java class.

@NativeClass
public class NativeTest {
    public static final int RUNTIME_TYPE_MAIN = 1;
    public native int add(int a, int b);
    public native void cpp_magic(String s, byte[] data);
}

The generated Glue code.

// NativeTest.h

namespace NativeTest {
static constexpr auto FULL_CLASS_NAME = u8"io/github/landerlyoung/jennysampleapp/NativeTest";
static constexpr jint RUNTIME_TYPE_MAIN = 1;

jint JNICALL add(JNIEnv* env, jobject thiz, jint a, jint b);
void JNICALL cpp_magic(JNIEnv* env, jobject thiz, jstring s, jbyteArray data);

inline bool registerNativeFunctions(JNIEnv* env) { ... }

}

// NativeTest.cpp

jint NativeTest::add(JNIEnv* env, jobject thiz, jint a, jint b) {
    // TODO(jenny): generated method stub.
    return 0;
}

void NativeTest::cpp_magic(JNIEnv* env, jobject thiz, jstring s, jbyteArray data) {
    // TODO(jenny): generated method stub.
}

Jenny generate:

  1. constant defines
  2. JNI register function
  3. native method declare with the same name as java methods
  4. native method implementation stubs

You just need to fill the stubs with real code.

Proxy

The following code is a show case that C++ uses OkHttp to perfomr a HTTP get operation through JNI APIs.

jstring func(jstring _url) {
    jenny::LocalRef<jstring> url(_url, false);

    OkHttpClientProxy client = OkHttpClientProxy::newInstance();
    BuilderProxy builder = BuilderProxy::newInstance().url(url);
    RequestProxy request = builder.build();
    CallProxy call = client.newCall(request.getThis());
    ResponseProxy response = call.execute();
    ResponseBodyProxy body = response.body();
    return body.string().release();
}

And here is the equivlent java code.

String run(String url) throws IOException {
    OkHttpClient client = new OkHttpClient();
    Request request = new Request.Builder()
        .url(url)
        .build();

    Response response = client.newCall(request).execute();
    return response.body().string();
}

If you are femiliar with JNI, you'd be surprised! The C++ code using Jenny just as clean as the Java code. Without Jenny it would be a nightmare.

And here is another real world comparesion using URLConnection api with vs without jenny. 🔗Follow the link to see the nightmare!

And also, here is another example without jnihelper.

void NativeDrawable::draw(JNIEnv *env, jobject thiz, jobject _canvas) {
    auto bounds = GraphicsProxy::drawableGetBounds(env, thiz);

    GraphicsProxy::setColor(env, state->paint, state->color());
    GraphicsProxy::drawableCircle(
        env, _canvas,
        RectProxy::exactCenterX(env, bounds),
        RectProxy::exactCenterY(env, bounds),
        std::min(RectProxy::exactCenterX(env, bounds),
                 RectProxy::exactCenterY(env, bounds)) * 0.7f,
        state->paint
    );
}

How to

Use in gradle

Jenny comes with two component

  1. the annotation library
  2. the annotation-processor

Download 👈👈👈 click here for latest version on jcenter.

dependencies {
    compileOnly 'io.github.landerlyoung:jenny-annotation:1.0.0'
    kapt 'io.github.landerlyoung:jenny-compiler:1.0.0'
    // for non-kotlin project use:
    // annotationProcessor 'io.github.landerlyoung:jenny-compiler:1.0.0'
}

For kotlin project, you gonna need the kotlin-kapt plugin.

That's it!

The generated code directory depends on your compiler config, typically, for android project it's inside build/generated/source/kapt/debug/jenny, for java project it's build/generated/sources/annotationProcessor/java/main/jenny. Also, you can use your own directory with simple config, see below.

You can use the generated code as you like, copy-past manually, or use gradle to copy them automatically (see sample in sample-android/guild.gradle).

Use annotations

Annotations for glue

Add @NativeClass() annotation to you native class in order to let Jenny spot you class, and then generate corresponding cpp source.

Then Jenny would generate code for you. sample-gen contains samples for Jenny generated code.

Note: There is a config field in NativeClass.dynamicRegisterJniMethods, when true (the default value) will generate code registering JNI function dynamically on the JNI_OnLoad callback by JNIEnv::RegisterNatives, instead of using JNI function name conversions (what javah/javac does).

Annotations for proxy

Add @NativeProxy to your normal java/kotlin class, need to cooperate with @NativeMethodProxy and @NativeFieldProxy, please read the doc.

Also, you can tell Jenny to generate code for libray classes by using the @NativeProxyForClasses annotation. (note)

(note): Use this feature with caution. When your compile-class-path and runtime-class-path have different version of the same class, it's easy to crash because the proxy can't find some method which appears in compile-time but not on runtime. For instance to generate proxy for java.net.http.HttpRequest compiled with java-11, ran with java-8, your code crashes because that class just don't exist before java-11.

In this case, the recommanded way is to write your own helper class, and generate proxy for it.

Configurations

Jenny annotation processor arguments:

name default value meaning
jenny.threadSafe true The proxy class supports lazy init, this flag controls if the lazy init is thread safe or not.
jenny.errorLoggerFunction null When proxy failed to find some method/class/field use the given function to do log before abort. The function must be a C++ function on top namespace with signature as void(JNIEnv* env, const char* error)
jenny.outputDirectory null By default, Jenny generate filed to apt dst dir, use this argument to control where the generated files are.
jenny.fusionProxyHeaderName jenny_fusion_proxies.h The fusionProxyHeader is a header file that include all generated proxy files and gives you a jenny::initAllProxies function to init all proxies at once, this flag changes the file name.
jenny.headerOnlyProxy true The generated proxy file use header only fusion or not.
jenny.useJniHelper false Turn on/off jnihelper

And also, there are some config in Jenny's annotations, please read the doc.

FAQ

1. How to passing arguments to annotation processor

  1. For kotlin project, it simple
kapt {
    // pass configurations to jenny
    arguments {
        arg("jenny.threadSafe", "false")
        arg("jenny.errorLoggerFunction", "jennySampleErrorLog")
        arg("jenny.outputDirectory", project.buildDir.absolutePath+"/test")
        arg("jenny.headerOnlyProxy", "true")
        arg("jenny.useJniHelper", "true")
        arg("jenny.fusionProxyHeaderName", "JennyFisonProxy.h")
    }
}
  1. For Android, you can also do this.

  2. For Java Project, do this:

compileJava {
    options.compilerArgs += [
            "-Ajenny.threadSafe=false",
            "-Ajenny.useJniHelper=false",
    ]
}

2. My JNI code crashes saying some class not found while the are really there?!

When using JNI with multi-thread in C++, please be noticed the pure native thread (that is create in C++ then attached to jvm) has its class loader as the boot class loader, so on such thread you can only see java standard library classes. For more info, please refer to here.

To solve this problem, please init proxy classes on the JNI_OnLoad callback, and there is a jenny_fusion_proxies.h may by helpful.

License

Open sourced under the Apache License Version 2.0.

If you like or are using this project, please start!

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].