JNI JNI basics

Preface

This article goes through JNI basics.

1. CMakeList.txt

The gradle script is as follows:

android{
    
    
    defaultConfig {
    
    
        externalNativeBuild {
    
    
            cmake {
    
    
                cppFlags "-std=c++11"
            }
        }
    }
    externalNativeBuild {
    
    
        cmake {
    
    
            path file('src/main/cpp/CMakeLists.txt')
            version "3.6.0"  // 3.6.0版本日志输出,3.10.2无日志输出
        }
    }
}

CMakeList basic syntax:

# 添加动态库
add_library( # Sets the name of the library.
        native-lib

        # Sets the library as a shared library.
        SHARED

        # Provides a relative path to your source file(s).
        native-lib.cpp)

# 从某路径下找到动态库并命名
find_library(
        # Sets the name of the path variable.
        log-lib

        # Specifies the name of the NDK library that you want CMake to locate.
        log)

# 关联动态库,native-lib关联log-lib
target_link_libraries( # Specifies the target library.
        native-lib

        # Links the target library to the log library
        # included in the NDK.
        ${
    
    log-lib})


2. Static registration and dynamic registration

So will call the JNI_OnLoadmethod when loading , which can be achieved by env->RegisterNativesdynamically registering JNI through the method

// Java代码
public class DynamicLoad {
    
    
    static {
    
    
        System.loadLibrary("dynamic-lib");
    }

    public native int sum(int x, int y);  // JNI直接生成该方法的native方法即静态注册

    public native String getNativeString(); // 在JNI_OnLoad时动态注册
}

dynamic.cpp, so named as dynamic-lib in CMakeList

#include <jni.h>

// 静态注册
extern "C"
JNIEXPORT jint JNICALL
Java_com_baiiu_jnitest_dynamicLoad_DynamicLoad_sum(JNIEnv *env, jobject thiz, jint x, jint y) {
    
    
    return x + y;
}

jstring nativeGetString(JNIEnv *env, jobject thiz) {
    
    
    return env->NewStringUTF("message from dynamic loader");
}

// so加载时(dlpen)调用该方法,进行动态注册
JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    
    
    JNIEnv *env;

    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
    
    
        return JNI_FALSE;
    }

    const char *className = "com/baiiu/jnitest/dynamicLoad/DynamicLoad";
    JNINativeMethod methods[] = {
    
    
            {
    
    "getNativeString", "()Ljava/lang/String;", (void *) nativeGetString}
    };

    jclass clazz = env->FindClass(className);
    env->RegisterNatives(clazz, methods, 2);

    return JNI_VERSION_1_6;
}

3. jni data type conversion

Looking at the source code, it was found that typedefkeywords were used to redefine the type in jni.h.

The correspondence between the basic types is as follows:

/* Primitive types that match up with Java equivalents. */
typedef uint8_t  jboolean; /* unsigned 8 bits */
typedef int8_t   jbyte;    /* signed 8 bits */
typedef uint16_t jchar;    /* unsigned 16 bits */
typedef int16_t  jshort;   /* signed 16 bits */
typedef int32_t  jint;     /* signed 32 bits */
typedef int64_t  jlong;    /* signed 64 bits */
typedef float    jfloat;   /* 32-bit IEEE 754 */
typedef double   jdouble;  /* 64-bit IEEE 754 */
Java Native
boolean jboolean
byte jbyte
char jchar
short jshort
int jit
long jlong
float jfloat
double jdouble

The reference type correspondence is as follows:

Java Reference Native
All objects jobject
java.lang.Class jobject
java.lang.String jstring
java.lang.Throwable jthrowable
Object[] jobjectArray
boolean[] jbooleanArray
byte[] jbyteArray
char[] jcharArray
short[] jshortArray
int[] jintArray
long[] jlongArray
float[] jfloatArray
double[] jdoubleArray
class _jobject {
    
    };
class _jclass : public _jobject {
    
    };
class _jstring : public _jobject {
    
    };
class _jarray : public _jobject {
    
    };
class _jobjectArray : public _jarray {
    
    };
class _jbooleanArray : public _jarray {
    
    };
class _jbyteArray : public _jarray {
    
    };
class _jcharArray : public _jarray {
    
    };
class _jshortArray : public _jarray {
    
    };
class _jintArray : public _jarray {
    
    };
class _jlongArray : public _jarray {
    
    };
class _jfloatArray : public _jarray {
    
    };
class _jdoubleArray : public _jarray {
    
    };
class _jthrowable : public _jobject {
    
    };

typedef _jobject*       jobject;
typedef _jclass*        jclass;
typedef _jstring*       jstring;
typedef _jarray*        jarray;
typedef _jobjectArray*  jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray*    jbyteArray;
typedef _jcharArray*    jcharArray;
typedef _jshortArray*   jshortArray;
typedef _jintArray*     jintArray;
typedef _jlongArray*    jlongArray;
typedef _jfloatArray*   jfloatArray;
typedef _jdoubleArray*  jdoubleArray;
typedef _jthrowable*    jthrowable;
typedef _jobject*       jweak;

Pay attention to recycling when using reference types

extern "C"
JNIEXPORT void JNICALL
Java_com_baiiu_jnitest_string_StringTestFragment_callNativeString(JNIEnv *env, jobject thiz, jstring jStr, jintArray jarr) {
    
    

    // jstring需要转化为 nativeString 才能使用
    const char *str = env->GetStringUTFChars(jStr, JNI_FALSE);
    env->ReleaseStringUTFChars(jStr, str); // 回收


	int *arr = env->GetIntArrayElements(jarr, JNI_FALSE);
	env->ReleaseIntArrayElements(jarr, arr, 0); // 回收
}

4. JNI Reference Type

Local reference, global reference, weak reference

env->DeleteLocalRef(temp)Release local references;
env->NewGlobalRef(jclazz)create global references;
env->NewWeakGlobalRef(clazz)create weak references;

extern "C"
JNIEXPORT void JNICALL
Java_com_baiiu_jnitest_reference_ReferenceFragment_localReference(JNIEnv *env, jobject thiz) {
    
    
    // 局部引用,方法执行完后结束
    jclass jclazz = env->FindClass("java/lang/String");
    for (int i = 0; i < 1000; ++i) {
    
    
        jclass jclazzTemp = env->FindClass("java/lang/String");
        env->DeleteLocalRef(jclazzTemp); // 可以手动释放
    }

	// 全局引用
	static jclass stringClass = nullptr;
    if (stringClass == nullptr) {
    
    
        jclass jclazz = env->FindClass("java/lang/String");
        stringClass = static_cast<jclass>(env->NewGlobalRef(jclazz)); // 创建全局引用
        env->DeleteLocalRef(jclazz); // 释放局部引用
    } else {
    
    
        LOGD("cached");
    }
}

5. JNI handling exception

extern "C"
JNIEXPORT void JNICALL
Java_com_baiiu_jnitest_exception_ExceptionFragment_nativeInvokeJavaException(JNIEnv *env, jobject thiz) {
    
    
	// JNI调用Java方法
    jclass jclazz = env->GetObjectClass(thiz);
    jmethodID mid = env->GetMethodID(jclazz, "error", "()I");
    jint result = env->CallIntMethod(thiz, mid);

    // 捕获异常,有异常发生时catch住
    jthrowable thr = env->ExceptionOccurred();
    if (thr) {
    
    
        env->ExceptionDescribe();
        env->ExceptionClear();
    }
	
	// 抛出异常,处理一波后抛出
	jclass clazz = env->FindClass("java/lang/IllegalArgumentException");
    env->ThrowNew(clazz, "native throw exception");

    LOGD("result: %d", result);
}

6. Create a child thread

pthread_createCreate thread
gVm->AttachCurrentThread(&env, nullptr)Create env object of
gVm->DetachCurrentThread();current thread release current thread after use

#include <pthread.h>

JavaVM *gVm; // 保存vm

JNIEXPORT int JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
    
    
    JNIEnv *env;

    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {
    
    
        return JNI_FALSE;
    }

    gVm = vm;

    return JNI_VERSION_1_6;
}

jobject threadObject;
jclass threadClazz;
jmethodID threadMethod;

extern "C"
JNIEXPORT void JNICALL
Java_com_baiiu_jnitest_thread_ThreadFragment_nativeThreadCallBack(JNIEnv *env, jobject thiz, jobject call_back) {
    
    

    threadObject = env->NewGlobalRef(call_back);
    threadClazz = env->GetObjectClass(call_back);
    threadMethod = env->GetMethodID(threadClazz, "onCallBack", "()V");

    // 创建线程
    pthread_t handle;
    pthread_create(&handle, nullptr, threadCallBack, nullptr);
}

/*
    AttachCurrentThread、DetachCurrentThread需要成对调用
 */
void *threadCallBack(void *) {
    
    
    JNIEnv *env;

    // 创建当前线程的env对象
    if (gVm->AttachCurrentThread(&env, nullptr) == JNI_OK) {
    
    


        env->CallVoidMethod(threadObject, threadMethod);

        gVm->DetachCurrentThread();
    }

    return 0;
}

7. Use of thread-safe locks

Two threads open 1, 2...100 sequentially

#include <jni.h>
#include <log.h>
#include <pthread.h>

pthread_mutex_t mutex;
pthread_cond_t cond;
int count;

bool isOdd(int num) {
    
    
    return (num & 1) == 1;
}

void *print1(void *) {
    
    
    while (true) {
    
    
        pthread_mutex_lock(&mutex);

        while (!isOdd(count)) {
    
    
            pthread_cond_wait(&cond, &mutex);
        }
        LOGD("print1 count is: %d", count++);
        pthread_cond_signal(&cond);

        pthread_mutex_unlock(&mutex);

        if (count >= 100) {
    
    
            break;
        }
    }

    LOGD("print1 end");
    return 0;
}

void *print2(void *) {
    
    
    while (true) {
    
    
        pthread_mutex_lock(&mutex);

        while (isOdd(count)) {
    
    
            pthread_cond_wait(&cond, &mutex);
        }
        LOGD("print2 count is: %d", count++);
        pthread_cond_signal(&cond);

        pthread_mutex_unlock(&mutex);

        if (count >= 100) {
    
    
            break;
        }
    }

    LOGD("print2 end");
    return 0;
}

extern "C"
JNIEXPORT void JNICALL
Java_com_baiiu_jnitest_thread_ThreadMutexFragment_print12to100(JNIEnv *env, jobject thiz) {
    
    
    LOGD("count init: %d", count);

    pthread_mutex_init(&mutex, nullptr);
    pthread_cond_init(&cond, nullptr);

    pthread_t handle1;
    pthread_t handle2;
    pthread_create(&handle1, nullptr, print1, nullptr);
    pthread_create(&handle2, nullptr, print2, nullptr);
}

8. gradle command

Again a command, after writing JNI, can use the ./gradlew externalNativeBuildDebugCommand compiled native code, in build/intermediates/cmake/debugor build/intermediates/transforms/mergeJniLibs/generated so below.
You can also find . -name "*.so"find so.



Refer to
NDK and JNI
Android to understand JNI (two) type conversion, method signature and JNIEnv
AndroidDevWithCpp
C++ rookie tutorial
JNI principle

Guess you like

Origin blog.csdn.net/u014099894/article/details/106322537