[11] NDK series of interactive Java and c ++ acquaintance Jni


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.hthe 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: _stdcalla 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);


}



Code

https://github.com/ddssingsong/AnyNdk

Guess you like

Origin blog.csdn.net/u011077027/article/details/93172622