Article Directory
JNI is a native programming interface. It allows the code to run JAVA JAVA virtual machine and other programming languages, such as the interaction between the C language, C ++, Assembler, applications and libraries. Not just something unique to Android
1. Java method call c ++
Static load so
static {
System.loadLibrary("native-lib");
}
c ++ and corresponding method java
public static native void native11();
extern "C"
JNIEXPORT jstring JNICALL
Java_com_dds_anyndk_AnyNdk_native11(JNIEnv *env, jclass type, jint a, jstring str_, jfloat f) {
// 获得c字符串
const char *str = env->GetStringUTFChars(str_, JNI_FALSE);
char returnStr[100];
//格式化字符串
sprintf(returnStr, "C++ string:%d,%s,%f", a, str, f);
//释放掉内存
env->ReleaseStringUTFChars(str_, str);
return env->NewStringUTF(returnStr);
}
CmakeLists.txt配置
cmake_minimum_required(VERSION 3.4.1)
add_library(
native-lib
SHARED
native11.cpp)
target_link_libraries(
native-lib
log)
2. JNI data types
JNIEXPORT and JNICALL, defined in jni.h
the header file.
JNIEXPORT:
In Windows, defined as __declspec(dllexport)
. Because Windows compiled dll dynamic library provides that if the dynamic library function to be called external, need to add this function to identify in a statement, expressed the export function can be called outside.
In the Linux / Unix / Mac os / Android This Like Unix systems, defined as__attribute__ ((visibility ("default")))
JNICALL:
No definition in class Unix, Windows, defined as: _stdcall
a function calling convention
Unix-like systems which can be omitted without two macros.
Java type | Local Type | description |
---|---|---|
boolean | jboolean | C / C ++ 8-bit integer |
byte | jbyte | Integer 8 C / C ++ unsigned |
char | Jcr | C / C ++ 16-bit unsigned integer |
short | jshort | C / C ++ 16-bit signed integer |
int | jint | 32-bit integer C / C ++ unsigned |
long | jlong | C / C ++ 64-bit unsigned integer |
float | jfloat | C / C ++ 32-bit floating point |
double | jdouble | C / C ++ 64-bit floating point |
Object | jobject | Any Java object, or does not correspond to an object of type java |
Class | jclass | Class object |
String | jstring | String object |
Object[] | jobjectArray | An array of any object |
boolean[] | jbooleanArray | Boolean arrays |
byte[] | jbyteArray | Bit array |
char[] | jcharArray | Character array |
short[] | jshortArray | Short integer array |
int[] | jintArray | Integer array |
long[] | jlongArray | Long integer array |
float[] | jfloatArray | Float array |
double[] | jdoubleArray | Double floating-point array |
Gets an array of types of data
extern "C"
JNIEXPORT jstring JNICALL
Java_com_dds_anyndk_AnyNdk_native11_12(JNIEnv *env, jclass type, jobjectArray strs,
jintArray ints_) {
//1、 获得字符串数组
int32_t str_length = env->GetArrayLength(strs);
LOGD("字符串 数组长度:%d", str_length);
for (int i = 0; i < str_length; ++i) {
jstring str = jstring(env->GetObjectArrayElement(strs, i));
const char *c_str = env->GetStringUTFChars(str, JNI_FALSE);
LOGD("字符串有:%s", c_str);
//使用完释放
env->ReleaseStringUTFChars(str, c_str);
}
//2、获得基本数据类型数组
int32_t int_length = env->GetArrayLength(ints_);
LOGD("int 数组长度:%d", int_length);
jint *ints = env->GetIntArrayElements(ints_, nullptr);
for (int i = 0; i < int_length; i++) {
LOGD("int 数据有:%d", ints[i]);
}
env->ReleaseIntArrayElements(ints_, ints, 0);
return env->NewStringUTF("hello");
}
3. C / C ++ Java reflection
The method of Java reflection to create objects in C / C ++, call the Java
Basic data types of signatures uses a series of uppercase letters represented in the following table:
Java type | signature |
---|---|
boolean | WITH |
short | S |
float | F |
byte | B |
int | I |
double | D |
char | C |
long | J |
void | V |
Reference types | + L + fully qualified name; |
Array | [+ Type signature |
Reflection method requires
public class JavaHelper {
private static final String TAG = "dds_native11";
//private和public 对jni开发来说没任何区别 都能反射调用
public void instanceMethod(String a, int b, boolean c) {
Log.e(TAG, "instanceMethod a=" + a + " b=" + b + " c=" + c);
}
public static void staticMethod(String a, int b, boolean c) {
Log.e(TAG, "staticMethod a=" + a + " b=" + b + " c=" + c);
}
}
Reflection method call
extern "C"
JNIEXPORT jstring JNICALL
Java_com_dds_anyndk_AnyNdk_native11_13(JNIEnv *env, jclass type) {
jclass class_helper = env->FindClass("com/dds/anyndk/JavaHelper");
// 反射调用静态方法
jmethodID method_staticMethod = env->GetStaticMethodID(class_helper, "staticMethod",
"(Ljava/lang/String;IZ)V");
jstring staticStr = env->NewStringUTF("C++调用静态方法");
env->CallStaticVoidMethod(class_helper, method_staticMethod, staticStr, 1, true);
// 反射调用构造方法
jmethodID constructMethod = env->GetMethodID(class_helper,"<init>","()V");
jobject helper = env->NewObject(class_helper,constructMethod);
jmethodID instanceMethod = env->GetMethodID(class_helper,"instanceMethod","(Ljava/lang/String;IZ)V");
jstring instanceStr= env->NewStringUTF("C++调用实例方法");
env->CallVoidMethod(helper,instanceMethod,instanceStr,2,0);
// 释放资源
env->DeleteLocalRef(class_helper);
env->DeleteLocalRef(staticStr);
env->DeleteLocalRef(instanceStr);
env->DeleteLocalRef(helper);
return env->NewStringUTF("dds");
}
When the signature can be used to obtain the reflection method javap
javap -s com.dds.anyndk.JavaHelper
Reflecting modify variables
Reflection method requires
public class JavaHelper {
int a = 10;
static String b = "java字符串";
public void testReflect(JavaHelper javaHelper) {
Log.e(TAG, "修改前 : a = " + a + " b=" + b);
AnyNdk.native11_4(javaHelper);
Log.e(TAG, "修改后 : a = " + a + " b=" + b);
}
}
reflection
#define LOG_TAG "dds_native4"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__ )
extern "C"
JNIEXPORT void JNICALL
Java_com_dds_anyndk_AnyNdk_native11_14(JNIEnv *env, jclass type, jobject javaHelper) {
jclass clazz = env->GetObjectClass(javaHelper);
//获得int a的标示
jfieldID a = env->GetFieldID(clazz, "a", "I");
// 获取a的值
int value = env->GetIntField(javaHelper, a);
LOGD("获得java属性a:%d", value);
env->SetIntField(javaHelper, a, 100);
// 获取String b 的标示
jfieldID b = env->GetStaticFieldID(clazz, "b", "Ljava.lang.String;");
// 获取静态变量的值
auto bStr = jstring(env->GetStaticObjectField(clazz, b));
const char *bc_str = env->GetStringUTFChars(bStr, JNI_FALSE);
LOGD("获得java属性b:%s", bc_str);
jstring new_str = env->NewStringUTF("C++字符串");
env->SetStaticObjectField(clazz, b, new_str);
// 释放资源
env->ReleaseStringUTFChars(bStr, bc_str);
env->DeleteLocalRef(new_str);
env->DeleteLocalRef(clazz);
}
4. JNI_OnLoad
Call System.loadLibrary () function, the internal will to find so in JNI_OnLoad function, if this function exists is called.
JNI_OnLoad will:
Tell VM JNI native version of this component.
It corresponds to the Java version, android only support JNI_VERSION_1_2, JNI_VERSION_1_4, JNI_VERSION_1_6
In JDK1.8 have JNI_VERSION_1_8.
jint JNI_OnLoad(JavaVM* vm, void* reserved){
// 2、4、6都可以
return JNI_VERSION_1_4;
}
Dynamic registration
Before Java_PACKAGENAME_CLASSNAME_METHODNAME we have been using in the jni java to match the method, in this way we call static registration.
The dynamic registration means that the method name can not be so long in the android aosp source code to use a lot of dynamic registration form
//Java:
native void dynamicNative();
native String dynamicNative(int i);
//C++:
void dynamicNative1(JNIEnv *env, jobject jobj){
LOGE("dynamicNative1 动态注册");
}
jstring dynamicNative2(JNIEnv *env, jobject jobj,jint i){
return env->NewStringUTF("我是动态注册的dynamicNative2方法");
}
//需要动态注册的方法数组
static const JNINativeMethod mMethods[] = {
{"dynamicNative","()V", (void *)dynamicNative1},
{"dynamicNative", "(I)Ljava/lang/String;", (jstring *)dynamicNative2}
};
//需要动态注册native方法的类名
static const char* mClassName = "com/dds/anyndk/AnyNdk";
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
//获得 JniEnv
int r = vm->GetEnv((void **) &env, JNI_VERSION_1_4);
if (r != JNI_OK) {
return -1;
}
jclass activityCls = env->FindClass(mClassName);
// 注册 如果小于0则注册失败
r = env->RegisterNatives(activityCls, mMethods, 2);
if (r != JNI_OK) {
return -1;
}
return JNI_VERSION_1_4;
}
5. c ++ thread calls Java
native call java need JNIEnv this structure, while JNIEnv by Jvm incoming variables associated with the thread.
JNIEnv but can be acquired by the current thread pointer AttachCurrentThread method of the JavaVM.
JavaVM* _vm = 0;
jobject _instance = 0;
jint JNI_OnLoad(JavaVM* vm, void* reserved){
_vm = vm;
return JNI_VERSION_1_4;
}
void *task(void *args) {
JNIEnv *env;
_vm->AttachCurrentThread(&env, 0);
jclass clazz = env->GetObjectClass(_instance);
jmethodID methodId = env->GetStaticMethodID(clazz, "staticMethod", "(Ljava/lang/String;IZ)V");
jstring staticStr = env->NewStringUTF("C++调用静态方法");
env->CallStaticVoidMethod(clazz, methodId, staticStr, 1, true);
env->DeleteLocalRef(clazz);
env->DeleteLocalRef(staticStr);
_vm->DetachCurrentThread();
return 0;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_dds_anyndk_AnyNdk_native11_15(JNIEnv *env, jclass type, jobject javaHelper) {
pthread_t pid;
_instance = env->NewGlobalRef(javaHelper);
// 开启线程
pthread_create(&pid, 0, task, 0);
}