The role of the JNI

JNI stands for Java Native Interface. JNI is not proprietary to Android, it inherits from Java, but its role and importance has grown dramatically in Android.

Android, as an embedded operating system, has a large number of drivers and hardware-related functions that must be realized in native layer. Besides, some functions that focus on performance and power consumption are also better to be realized by C/C++ than by Java. Therefore, in the development of Android, whether application level development, or system level development are inseparable from JNI. Android provides strong support for JNI in all aspects of compilation, application packaging and installation, and application loading.

The implementation of the Java language is dependent on the virtual machine, so when you need to call C/C++ layer functions in Java code, you need to tell the virtual machine which method represents the local function and where to find it, and vice versa.

  • fromJavatoC/C++You’re building associations between functions
  • And from aC/C++toJavaYou have to get it firstJava objectTo call the object’s methods

Also, remember that once you use JNI, Java programs lose two of the advantages of the Java platform:

  • Applications are no longer cross-platform. To be cross-platform, you must recompile the native language parts in a different system environment.
  • Programs are no longer perfectly safe, and improper use of native code can cause the entire program to crash.

Introduction to JNI usage

The JNI header file is located at libnativeHelper /include_jni/jni.h

fromJavatoC/C++

loadingJNIThe dynamic library

To use JNI, dynamic libraries compiled by C/C++ code must be loaded into the process’s memory space before the local methods can be called.

The LoadLibrary file uses the LoadLibrary method of the System class as follows:

public static void loadLibrary (String libname)
Copy the code

The parameters to the loadLibrary method are part of the name of the dynamic library file. The Android JNI dynamic library must start with lib, and the argument passed here is to remove the prefix lib and the middle of the suffix.so.

For example, the library file name is libmyjni.so the statement to load the library should be loadLibrary(“myjni”). Why not use full file names?

  • JNIUsed to beJavaThe product ofJavaI want the code to be platform usable
  • Different platformsThe dynamic libraryThe suffix is not the same
    • LinuxNext is.so
    • WindowsNext is.dll
  • Therefore, in order to adapt to different platforms, the parameters passed in here are stripped of the system-specific parts

Calling the loadLibrary method does not specify a path to the library files. Android looks for dynamic libraries in several system directories.

To ensure that the dynamic library required before calling native methods has been loaded, loadLibrary calls are usually placed in the static block of the class, so that the load statement can be executed when the class is loaded.

definenativemethods

To define a native method in a Java class, it is easy to add the native keyword in front of the method, for example:

private native void  init(a);
Copy the code

In native methods, any type can be used as a parameter, including:

  • Underlying data types
  • An array type
  • Complex object

Native methods are no different in use than normal Java methods,

writeJNI dynamic libraries

The difference between JNI dynamic libraries and non-JNI dynamic libraries is that the JNI dynamic library defines a function named JNI_OnLoader, which will be called by the system after the dynamic library is loaded to complete the registration of JNI functions.

The prototype of the JNI_OnLoader function is as follows:

jint JNI_OnLoad(JavaVM* vm, void*)
Copy the code

The most important thing to do in the JNI_OnLoader function is to call the registerNativeMethods function to register the JNI function in the dynamic library.

The so-called registration is to connect native methods defined in Java classes with local C functions through a table, so that the virtual machine can find the corresponding C functions when parsing native methods in Java classes.

Take an example from the source code to deepen the impression:

static JNINativeMethod HdcpJNIMethods[] = {
    /* name, signature, funcPtr */
    { "aml_init_unifykeys"."()Ljava/lang/String;", (void*) aml_init_unifykeys },
    { "aml_key_query_exist"."(Ljava/lang/String;) Ljava/lang/String;", (void*) aml_key_query_exist },
    { "aml_key_get_name"."()Ljava/lang/String;", (void*) aml_key_get_name },
};
JNIEXPORT jint
JNI_OnLoad(JavaVM* vm, void* reserved)
{
    / /...
    ret = android::AndroidRuntime::registerNativeMethods(env,
            FINSKY_CLASS_NAME, HdcpJNIMethods, NELEM(HdcpJNIMethods));
    / /...
}
Copy the code

The prototype of the registerNativeMethods function is

int registerNativeMethods(C_JNIEnv* env, const char* className,
    const JNINativeMethod* gMethods, int numMethods)
Copy the code

In the parameters:

  • The second parameter refers to the fully qualified name of the Java class declaring the native method

  • The third parameter is an array of type JNINativeMethod, which is defined as follows:

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;
Copy the code

JNINativeMethod:

  • nameRefers to theJava classesIn theNative methodThe name of the
  • signatureRefers to theNative methodParameter signature of
  • fnPtrRefers to theNative methodPointer to the corresponding local function
    • Although herefnPtrIs avoidA function pointer to the
    • However,JNIThere are also requirements for the definition of functions in
    • Due to thenativeFunctions must be of variable parameter type
    • So try it outvoidA function pointer to the
    • The call is then cast based on the parameters

Requirements of native method prototype are as follows:

Return type function name (JNIEnv *env,jobject obj,......)Copy the code
  • The first parameter is thetaJNI environment
  • The second argument refers to the calling classJava object
  • The ellipsis is concreteThe method parameters

Here’s an example:

static jstring aml_key_query_exist(JNIEnv *env, jobject obj, jstring keyName)
Copy the code

Parameters of the signature

The parameter signature of the native method uses abbreviations to indicate parameter types, which are specified by the Java language.

A signature consists of parameters and return values:

  • parameterMust be enclosed in parentheses
    • A pair of empty parentheses is used when there are no arguments(a)
    • No delimiters are required between multiple parameters
  • The return valueWith theparameterbehind

In the JNI environment, some objects are defined to match the primitive types in Java. Let’s look at the corresponding table:

Parameters of the signature Java type JNI type
Z boolean jboolean
C char jchar
B byte jbyte
S short jshort
I int jint
J long jlong
F float jfloat
D double jdouble
  • Parameters of the signatureIn theA one-dimensional arrayIs, is, is, isBasic types ofThe symbol is preceded by the symbol[
    • 2 d arrayThat is[[
  • Parameters of the signatureThe signature format for complex types inL, plusFully qualified class name, plus;

For example:

  • (I)V: the parameters forThe integer, no return value
  • ([IZ)I: the parameters forA one-dimensional array of integersandBoolean type, the return value isThe integer
  • (Ljava/lang/String;) Ljava/lang/String;: the parameters forstring, the return value isstring

Use the JNI type

  1. JavaThe basic types of are unsigned, but correspondingJNI typeNot all of them are unsigned. Let’s seeJNI typeDefinition:
// Primitive type definitions can be used directly, just like primitive types in Java
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 */

// Array type definition
/ /... Is just an alias for the defined Jobject
/ / well, impressive
typedef void*           jobject;
typedef jobject         jclass;
typedef jobject         jstring;
typedef jobject         jarray;
typedef jarray          jobjectArray;
typedef jarray          jbooleanArray;
typedef jarray          jbyteArray;
/ /...
Copy the code
  1. JNItheAn array typeAs can be seen from the above definition, in fact, onlyjobjectthroughtypedefThe defined alias.So what do we do if we want to manipulate arrays?

JNIEnv defines many, many array-related manipulation functions. Let’s look at a few examples:

struct _JNIEnv {
    / /...
    jsize GetArrayLength(jarray array){}
    / /...
    jint* GetIntArrayElements(jintArray array, jboolean* isCopy){}
    / /...
    void ReleaseIntArrayElements(jintArray array, jint* elems,jint mode){}}typedef _JNIEnv JNIEnv;
Copy the code

With these methods, for an array, we can:

  • throughGetArrayLengthCheck the length of the array
  • throughGetIntArrayElementsTake the array pointer and convert it tojinttype
  • throughReleaseIntArrayElementsFree up unwanted array memory
  1. jstringIs the essence ofjobjectClass, but frequently used, so separate defined operation functions, related methods include:
    // utF-8 character-related operations
    jsize GetStringUTFLength(jstring string){}
    const char* GetStringUTFChars(jstring string, jboolean* isCopy){}
    void ReleaseStringUTFChars(jstring string, const char* utf){}
    // Unicode character related operations
    jsize GetStringLength(jstring string){}
    const jchar* GetStringChars(jstring string, jboolean* isCopy){}
    void ReleaseStringChars(jstring string, const jchar* chars){}
Copy the code
  1. If you take only one array element at a time, you can use the following function
jobject GetObjectArrayElement(jobjectArray array, jsize index){}
Copy the code

fromC/C++toJava

generateJava object

To generate a Java object in JNIEnv, use the function NewObject

jobject NewObject(jclass clazz, jmethodID methodID, ...){}
Copy the code
  • parameterclazzRefers to theThe Java class objectYou can do it by a functionFindClassget
    • FindClassThe prototype of the function is as follows:
      jclass FindClass(const char* name){}
    Copy the code
    • parameternameRefers to theJava classesThe name of the
      • Such as:java/lang/String
    • jclassA type is used to representJava classes
    • inJNIIn, often to passjclassTo get theJava classesMember variables and methods of
  • parametermethodIDRefers to theJava classesConstructor of
    • To call a Java object’s method or access a Java object’s domain variable, we need to obtain the corresponding Id. The available functions are as follows:
      jmethodID GetMethodID(jclass clazz, const char* name, const char* sig){}
      jfieldID GetFieldID(jclass clazz, const char* name, const char* sig)
    Copy the code
    • If YOU want to get oneJava classesThe constructor of theGetMethodID, the parameter passed in must be<init>Can only be
      • Of a classA constructorThis will be converted to<init>methods

Let’s look at an example of generating an Intent object:

jclass clazz = env->FindClass("android/content/Intent");
jmethodID method = env->GetMethodID(clazz, "<init>"."Ljava/lang/String;");
jstring action = env->NewStringUTF("android.init.action.MAIN");
jobject intent = env->NewObject(clazz,method,action);
Copy the code

Call a method of a Java class

Let’s take a look at the functions JNIEnv provides to call methods in a Java class:

  • Object methods
    jobject CallObjectMethod(JNIEnv*, jobject, jmethodID, ...);
    jboolean CallBooleanMethod(JNIEnv*, jobject, jmethodID, ...);
    jbyte CallByteMethod(JNIEnv*, jobject, jmethodID, ...);
    jchar CallCharMethod(JNIEnv*, jobject, jmethodID, ...);
    jshort CallShortMethod(JNIEnv*, jobject, jmethodID, ...);
    jint CallIntMethod(JNIEnv*, jobject, jmethodID, ...);
    jlong CallLongMethod(JNIEnv*, jobject, jmethodID, ...);
    jfloat CallFloatMethod(JNIEnv*, jobject, jmethodID, ...);
    jdouble CallDoubleMethod(JNIEnv*, jobject, jmethodID, ...);
    void CallVoidMethod(JNIEnv*, jobject, jmethodID, ...);
Copy the code
  • Class method
    jobject CallStaticObjectMethod(JNIEnv*, jobject, jmethodID, ...);
    jboolean CallStaticBooleanMethod(JNIEnv*, jobject, jmethodID, ...);
    jbyte CallStaticByteMethod(JNIEnv*, jobject, jmethodID, ...);
    jchar CallStaticCharMethod(JNIEnv*, jobject, jmethodID, ...);
    jshort CallStaticShortMethod(JNIEnv*, jobject, jmethodID, ...);
    jint CallStaticIntMethod(JNIEnv*, jobject, jmethodID, ...);
    jlong CallStaticLongMethod(JNIEnv*, jobject, jmethodID, ...);
    jfloat CallStaticFloatMethod(JNIEnv*, jobject, jmethodID, ...);
    jdouble CallStaticDoubleMethod(JNIEnv*, jobject, jmethodID, ...);
    void CallStaticVoidMethod(JNIEnv*, jobject, jmethodID, ...);
Copy the code

Access domain variables of Java classes

After obtaining the ID of the member variable, JNIEnv defines the corresponding functions for reading and writing the variable:

  • Non-static member variables that read related parts of the function
    jobject GetObjectField(jobject obj, jfieldID fieldID){}
    jboolean GetBooleanField(jobject obj, jfieldID fieldID){}
    jint GetIntField(jobject obj, jfieldID fieldID){}
    / /...
Copy the code
  • Non-static member variables, write related partial functions
    void SetObjectField(jobject obj, jfieldID fieldID, jobject value){}
    void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value){}
    void SetIntField(jobject obj, jfieldID fieldID, jint value){}
    / /...
Copy the code
  • Static member variables that read related parts of the function
    jobject GetStaticObjectField(jobject obj, jfieldID fieldID){}
    jboolean GetStaticBooleanField(jobject obj, jfieldID fieldID){}
    jint GetStaticIntField(jobject obj, jfieldID fieldID){}
    / /...
Copy the code
  • Static member variables, write related partial functions
    void SetStaticObjectField(jobject obj, jfieldID fieldID, jobject value){}
    void SetStaticBooleanField(jobject obj, jfieldID fieldID, jboolean value){}
    void SetStaticIntField(jobject obj, jfieldID fieldID, jint value){}
    / /...
Copy the code

JNI environment

The structure of the bodyJNIEnv

JNIEnv is a structure that represents a JNI environment and is defined as follows:

struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
Copy the code

In code, JNI’s definition also distinguishes between C and C++. Since __cplusplus is defined in Android, we only need to focus on the C++ part of the code. In C++, JNIEnv is equivalent to _JNIEnv. Let’s look at the _JNIEnv definition:

struct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;

#if defined(__cplusplus)

    jint GetVersion(a)
    { return functions->GetVersion(this); }
    jclass DefineClass(const char *name, jobject loader, const jbyte* buf,jsize bufLen)
    { return functions->DefineClass(this, name, loader, buf, bufLen); }
    jclass FindClass(const char* name)
    { return functions->FindClass(this, name); }
    / /...
    
#endif /*__cplusplus*/
Copy the code

Functions in the structure call functions of member variables, functions are Pointers to JNINativeInterface in the structure. JNINativeInterface is defined as follows:

struct JNINativeInterface {
    void*       reserved0;
    void*       reserved1;
    void*       reserved2;
    void*       reserved3;
    jint        (*GetVersion)(JNIEnv *);
    jclass      (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*, jsize);
    jclass      (*FindClass)(JNIEnv*, const char*);
    / /...
}
Copy the code

JNINativeInterface defines all function Pointers. Where are these function Pointers implemented?

  • The explanations in the book are still usefulDalvik virtual machineSo this part is not recorded
  • throughgrepIn theart/runtime/jni_internal.ccThe function implementation is found in

Let’s take a look at the jni_internal.cc code snippet:

  • Defines aJNIClass, and according toJNINativeInterfaceTo implement the function name
class JNI {
 public:
  static jint GetVersion(JNIEnv*) {
    return JNI_VERSION_1_6;
  }
  static jclass DefineClass(JNIEnv*, const char*, jobject, const jbyte*, jsize) {
    LOG(WARNING) << "JNI DefineClass is not supported";
    return nullptr;
  }
  static jclass FindClass(JNIEnv* env, const char* name) {
    / /... Omit the implementation
  }
  / /... Omit other methods in JNINativeInterface
}
Copy the code
  • To create aJNINativeInterfaceAn instance of thegJniNativeInterface
const JNINativeInterface gJniNativeInterface = {
  nullptr,  // reserved0.
  nullptr,  // reserved1.
  nullptr,  // reserved2.
  nullptr,  // reserved3.
  JNI::GetVersion,
  JNI::DefineClass,
  JNI::FindClass,
  / /... Omit other parameters
 }
Copy the code

So, the implementation of the JNINativeInterface function is found in class JNI.

Creation and initialization of JNIEnv

JNIEnv is the JNI environment. The JNIEnv object is tied to a thread, so let’s consider a few questions:

  • When we areJava threadIn the callNative layertheJNIFunction to which the thread belongsJNIEnvHas the object been generated?
  • If so, when was it generated?

Familiar with JavaVM structure

You learned about the JNI_OnLoad function earlier

  • It’s going to pass oneJavaVMPointer to
  • throughJavaVMthegetEnvThe function can obtainJNIEnvObject pointer
  • JavaVMIn fact, is_JavaVM
    typedef _JNIEnv JNIEnv;
    typedef _JavaVM JavaVM;
    Copy the code

In that case, let’s take a look at the structure of _JavaVM:

struct _JavaVM {
    const struct JNIInvokeInterface* functions;

#if defined(__cplusplus)
    jint DestroyJavaVM(a)
    { return functions->DestroyJavaVM(this); }
    jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
    { return functions->AttachCurrentThread(this, p_env, thr_args); }
    jint DetachCurrentThread(a)
    { return functions->DetachCurrentThread(this); }
    jint GetEnv(void** env, jint version)
    { return functions->GetEnv(this, env, version); }
    jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args)
    { return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};
struct JNIInvokeInterface {
    void*       reserved0;
    void*       reserved1;
    void*       reserved2;

    jint        (*DestroyJavaVM)(JavaVM*);
    jint        (*AttachCurrentThread)(JavaVM*, JNIEnv**, void*);
    jint        (*DetachCurrentThread)(JavaVM*);
    jint        (*GetEnv)(JavaVM*, void**, jint);
    jint        (*AttachCurrentThreadAsDaemon)(JavaVM*, JNIEnv**, void*);
};
Copy the code
  • _JavaVMVirtual machine life cycle defines several methods related to the virtual machine life cycle
  • Methods are implemented by invocationJNIInvokeInterface functionsThe function of

This part is the same as the _JNIEnv function implementation. Similarly, we found the function implementation in art/ Runtime /java_vm_ext.cc as follows:

// Initialize the JNIInvokeInterface section
const JNIInvokeInterface gJniInvokeInterface = {
  nullptr.// reserved0
  nullptr.// reserved1
  nullptr.// reserved2
  JII::DestroyJavaVM,
  JII::AttachCurrentThread,
  JII::DetachCurrentThread,
  JII::GetEnv,
  JII::AttachCurrentThreadAsDaemon
};
// Function implementation section
class JII {
 public:
  static jint DestroyJavaVM(JavaVM* vm) {
    / /... Omit the implementation
  }
  static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
    / /... Omit the implementation
  }
  static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) {
    / /... Omit the implementation
  }
  static jint DetachCurrentThread(JavaVM* vm) {
    / /... Omit the implementation
  }
  static jint GetEnv(JavaVM* vm, void** env, jint version) {
    / /... Omit the implementation}};Copy the code

Initialization under the main thread – the start function of the ART

I’m not going to go into depth here, but I’m just trying to put together some information about JNI.

Android apps are all derived from the Zygote process fork, which will be explained in more detail later. Here’s what we need to know

  • ZygoteProcess will executeARTthestartmethods
  • ARTthestartThe method is initializedJNIEnvobject

Here is the simple analysis process, this is the main thread yo:

  1. ARTThe start function of theframeworks/base/core/jni/AndroidRuntime.cppIn, let’s have a brief look:
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    / /... Omit part of content
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) ! =0) {
        return;
    }
    / /... Omit part of content
}
Copy the code
  1. Calls to thestartVmFunction, let’s look at the implementation:
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote)
{
    // There are about 500 lines of parameter configuration code
    // Thought I was on the wrong set...
    if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
        ALOGE("JNI_CreateJavaVM failed\n");
        return - 1;
    }
    return 0;
}
Copy the code
  1. Call it againJNI_CreateJavaVM, take a look:
extern "C" jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) {
  / /... Omit some code
  Runtime* runtime = Runtime::Current(a);bool started = runtime->Start(a);if(! started) {delete Thread::Current(a)->GetJniEnv(a);
    delete runtime->GetJavaVM(a);LOG(WARNING) << "CreateJavaVM failed";
    return JNI_ERR;
  }
  
  // See here, do you have an assignment
  The JNIEnv object is instantiated in the Thread class
  *p_env = Thread::Current() - >GetJniEnv(a);// JavaVM objects are instantiated in Runtime
  *p_vm = runtime->GetJavaVM(a);return JNI_OK;
}
Copy the code

Here, the main thread is combed almost:

  • forThe main threadFor example, when the virtual machine starts up,JNIEnvIt’s already created

Normal thread initialization

The main Thread initializes the JNIEnv object in the Thread class when the virtual machine starts. What about a newly created thread in Java?

  • We know thatJavaStart the thread throughstart()methods
  • start()The method executesnativefunctionnativeCreate()
  • nativeCreate()Call isThread_nativeCreatefunction

Thread_nativeCreate = art/runtime/native/ java_lang_thread. cc

static void Thread_nativeCreate(JNIEnv* env, jclass, jobject java_thread, jlong stack_size, jboolean daemon) {
  / /... Omit the part
  Thread::CreateNativeThread(env, java_thread, stack_size, daemon == JNI_TRUE);
}
Copy the code

The CreateNativeThread function will use Linux thread functions to create a new thread. The code is as follows:

void Thread::CreateNativeThread(JNIEnv* env, jobject java_peer, size_t stack_size, bool is_daemon) {
    / /... Omit the part
    pthread_create_result = pthread_create(&new_pthread,
                                           &attr,
                                           Thread::CreateCallback,
                                           child_thread);
    / /... Omit the part
}
Copy the code

The new Thread runs on Thread::CreateCallback.

void* Thread::CreateCallback(void* arg) {
  Thread* self = reinterpret_cast<Thread*>(arg);
  Runtime* runtime = Runtime::Current(a);/ /...
  {
    / /...
    CHECK(self->Init(runtime->GetThreadList(), runtime->GetJavaVM(), self->tlsPtr_.tmp_jni_env));
    self->tlsPtr_.tmp_jni_env = nullptr;
   }
   / /...
}
Copy the code

Thread::CreateCallback calls the Init function for Thread

bool Thread::Init(ThreadList* thread_list, JavaVMExt* java_vm, JNIEnvExt* jni_env_ext) {
    / /...
    tlsPtr_.jni_env = JNIEnvExt::Create(this, java_vm, &error_msg);
    thread_list->Register(this);
    / /...
}
Copy the code

I’m going to create a new JNIEnv object.

Initialization summary of the JNIEnv object

There are two things to initialize a JNIEnv object:

  1. In the main threadJNIEnvObjects are already created when the virtual machine is created
  2. Java to create a threadJNIEnvObjects are created while the thread is running and released when the thread terminates

Exception handling in JNI

Check for exceptions generated by the Java layer in JNI

To be clear, the C++ layer of Android does not support try-catch

  • ifJNIcallJava layerAn exception occurred when the method of
    • JNI callsWill return normally
    • However, if you continue to call otherJNI functionMay cause the process to crash

To do this, JNI provides a set of functions to check if a Java method throws an exception, with the following prototype:

    // Check for exceptions
    jthrowable ExceptionOccurred(a);
    // Displays abnormal information
    void ExceptionDescribe(a);
    // Clear the exception
    void ExceptionClear(a);
Copy the code

Throw an exception in JNI

If you want to throw an exception in JNI, JNIEnv also provides two functions as follows:

    jint Throw(jthrowable obj);
    jint ThrowNew(jclass clazz, const char* message);
Copy the code
  • ThrowThe throw () function is used to throw an existing exception, usually after catching it
  • ThrowNewThe function is used to generate a new exception and throw it out
    • parameterclazzRefers to theJavaIn theException classAnd a derived class object

JNIHelp also defines several functions to facilitate throwing common exceptions, including:

/ / throw the Java/lang/NullPointerException null pointer exception
inline int jniThrowNullPointerException(JNIEnv* env, const char* msg);
/ / throw the Java/lang/RuntimeException runtime exception
inline int jniThrowRuntimeException(JNIEnv* env, const char* msg);
// Throws a Java /lang/IOException IO exception
inline int jniThrowIOException(JNIEnv* env, int errnum);
Copy the code

References in JNI

There are three types of references in JNI: LocalReference, GlobalReference, and WeakGlobalReference.

JNI sits between C/C++ and Java, and although the code for JNI functions is written in C/C++, Java objects created through JNI are garbage collected by the virtual machine just as they are created in the Java world.

LocalReference

Java objects generated in JNI native functions whose life cycle should end when the function exits.

How can virtual machines be guaranteed?

  • eachJava threadThere is aLocalReferenceTable. The VM does not reclaim objects in the table during memory reclamation.
  • And when you create an object,JNIEnvI’m going to implicitly put thisJava objectTo join theLocalReferenceIn the table.
  • Remove the local function from the table after it completes execution. This will be released by the virtual machine in the next garbage collection.

We can also do this explicitly:

jobject NewLocalRef(jobject ref);
void DeleteLocalRef(jobject localRef);
Copy the code

GlobalReference

Objects with global references need to be explicitly created and deleted. The VM is not reclaimed.

JNIEnv provides two functions:

jobject (jobject obj);
void DeleteGlobalRef(jobject globalRef);
Copy the code
  • NewGlobalRefHere are a few things:
    • First theJava objectfromLocalReferenceIn the table to delete
    • Then place the object inGlobalReferenceIn the table
  • DeleteGlobalRefTo do:
    • willJava objectfromGlobalReferenceIn the table to delete
    • Objects are not collected immediately and will not be released until the next garbage collection

WeakGlobalReference

An object under a WeakGlobalReference that has no other reference type will be reclaimed in the next garbage collection.

The relevant operation functions are:

    jweak NewWeakGlobalRef(jobject obj);
    void DeleteWeakGlobalRef(jweak obj);
Copy the code

Start the CheckJNI

If an error occurs in the JNI part of the application, it is not usually possible to print too many logs.

Therefore, Android defines a property called debug.checkjni:

  • When the value is 1, of the systemJNI functionMore checks are made on the parameters of the call
    • It also increases resource consumption and reduces execution efficiency
  • The default is 0

Setting Mode:

setprop debug.checkjni 1
Copy the code

conclusion

This article is just a brief summary of the techniques used in JNI. Since the frequency of active use is not very high, just as some supplementary knowledge.

I was thinking that if you dig into the JNI principle things might involve:

  • Dynamic library compilation, loading
  • The execution of a local method
  • The vm local method stack is related

Wait for the knowledge.

JNI: JNI env, JNI env, JNI env

In the next article, learn about Synchronization and messaging on Android.