An open API service indexing awesome lists of open source software.

https://github.com/wang-bin/jmi

JNI Modern Interface in C++17
https://github.com/wang-bin/jmi

android java jmi jni modern-cpp ndk

Last synced: 9 months ago
JSON representation

JNI Modern Interface in C++17

Awesome Lists containing this project

README

          

# JMI
**_JNI Modern Interface in C++_**

[中文](README_zh_CN.md)

[Some Java Classes Written in JMI](https://github.com/wang-bin/AND.git)

[![Build status github](https://github.com/wang-bin/JMI/workflows/Build/badge.svg)](https://github.com/wang-bin/JMI/actions)

### Features

- Compile time computed signature constant(C++17)
- Support both In & Out parameters for Java methods
- Per class jclass cache, per method jmethodID cache, per field jfieldID cache
- The same C++/Java storage duration: a static java member maps to a static member in C++
- Get rid of local reference leak
- getEnv() at any thread without caring about when to detach
- Signature is generated by compiler only once
- Supports JNI primitive types(jint, jlong etc. but not int, long), JMI's JObject, C/C++ string and array of these types as method parameter type, return type and field type.
- Provide frequently used functions for convenience: `to_string(jstring, JNIEnv*)`, `from_string(std::string, JNIEnv*)`, `android::application()`
- Easy to use. Minimize user code
- Exception handling in every call

### Example:
- Setup java vm in `JNI_OnLoad`: `jmi::javaVM(vm);`

- Create a SurfaceTexture:
```
// define SurfaceTexture tag class in any scope visibile by jmi::JObject
struct SurfaceTexture : jmi::ClassTag { static constexpr auto name() {return JMISTR("android/graphics/SurfaceTexture");}}; // or JMISTR("android.graphics.SurfaceTexture")
...
GLuint tex = ...
...
jmi::JObject texture;
if (!texture.create(tex)) {
// texture.error() ...
}
```

- Create Surface from SurfaceTexture:
```
struct Surface : jmi::ClassTag { static constexpr auto name() {return JMISTR("android.view.Surface");}}; // '.' or '/'
...
jmi::JObject surface;
surface.create(texture);
```

- Call void method:
```
texture.call("updateTexImage");
```

or

```
texture.call("updateTexImage");
```

- Call method with output parameters:
```
float mat4[16]; // or std::array, valarray
texture.call("getTransformMatrix", std::ref(mat4)); // use std::ref() if parameter should be modified by jni method
```

If out parameter is of type `JObject<...>` or it's subclass, `std::ref()` is not required because the object does not change, only some fields may be changed. For example:

```
MediaCodec::BufferInfo bi;
bi.create();
codec.dequeueOutputBuffer(bi, timeout); // bi is of type MediaCodec::BufferInfo&
```

- Call method with a return type:
```
auto t = texture.call("getTimestamp");
```

## jmethodID Cache

`GetMethodID/GetStaticMethodID()` is always called in `call/callStatic("methodName", ....)` every time, while it's called only once in overload one `call/callStatic<...MTag>(...)`, where `MTag` is a subclass of `jmi:MethodTag` implementing `static const char* name() { return "methodName";}`.

```
// GetMethodID() will be invoked only once for each method in the scope of MethodTag subclass
struct UpdateTexImage : jmi::MethodTag { static const char* name() {return "updateTexImage";}};
struct GetTimestamp : jmi::MethodTag { static const char* name() {return "getTimestamp";}};
struct GetTransformMatrix : jmi::MethodTag { static const char* name() {return "getTransformMatrix";}};
...
texture.call(); // or texture.call();
auto t = texture.call();
texture.call(std::ref(mat4)); // use std::ref() if parameter should be modified by jni method
```

### Field API

Field api supports cacheable and uncacheable jfieldID. Field object can be JNI basic types, string, JObject and array of these types.

Cacheable jfieldID through FieldTag

```
JObject obj;
...
struct MyIntField : FieldTag { static const char* name() {return "myIntFieldName";} };
auto ifield = obj.field();
jfieldID ifid = ifield; // or ifield.id()
ifield.set(1234);
jint ivalue = ifield; // or ifield.get();

// static field is the same except using the static function JObject::staticField
struct MyStrFieldS : FieldTag { static const char* name() {return "myStaticStrFieldName";} };
auto& ifields = JObject::staticField(); // it can be an ref
jfieldID ifids = ifields; // or ifield.id()
ifields.set("JMI static field test");
ifields = "assign";
std::string ivalues = ifields; // or ifield.get();
```

Uncacheable jfieldID using field name string directly

```
auto ifield = obj.field("myIntFieldName");
...
```

### Writting a C++ Class for a Java Class

Create a class inherits JObject or stores it as a member, or use CRTP JObject. Each method implementation is usually less then 2 lines of code. See [JMITest](test/JMITest.h) and [Project AND](https://github.com/wang-bin/AND.git)

### Using Signatures Generated by Compiler

The function template `auto signature_of()` returns the signature of type T. T can be JMI supported types (except jobject types because the class is determined at runtime), reference_wrapper, void, and function types whose return type and parameter types are of above types.

example:

```
void native_test_impl(JNIEnv *env , jobject thiz, ...) {}

staitc const JNINativeMethod gMethods[] = {
{"native_test", signature_of(native_test_impl).data(), native_test_impl},
...
};
```

You may find that a macro can simplify above example:

```
#define DEFINE_METHOD(M) {#M, signature_of(M##_impl).data(), M##_impl}
staitc const JNINativeMethod gMethods[] = {
DEFINE_METHOD(native_test),
...
}
```

### Known Issues

- If return type and first n arguments of call/call_static are the same, explicitly specifying return type and n arguments type is required

### Why JObject is a Template?
- To support per class jclass, per method jmethodID, per field jfieldID cache

#### Compilers

c++14/17 is required

- g++ >= 4.9.0(except 8.0~8.3)
- clang >= 3.5
- msvc>= 19.0
- icc >= 17.0

### TODO
- modern C++ class generator script

#### MIT License
>Copyright (c) 2016-2021 WangBin