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.
- from
Java
toC/C++
You’re building associations between functions - And from a
C/C++
toJava
You have to get it firstJava object
To 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
fromJava
toC/C++
loadingJNI
The 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?
JNI
Used to beJava
The product ofJava
I want the code to be platform usable- Different platforms
The dynamic library
The suffix is not the sameLinux
Next is.so
Windows
Next 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.
definenative
methods
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:
name
Refers to theJava classes
In theNative method
The name of thesignature
Refers to theNative method
Parameter signature offnPtr
Refers to theNative method
Pointer to the corresponding local function- Although here
fnPtr
Is avoid
A function pointer to the - However,
JNI
There are also requirements for the definition of functions in - Due to the
native
Functions must be of variable parameter type - So try it out
void
A function pointer to the - The call is then cast based on the parameters
- Although here
Requirements of native method prototype are as follows:
Return type function name (JNIEnv *env,jobject obj,......)Copy the code
- The first parameter is theta
JNI environment
- The second argument refers to the calling class
Java object
- The ellipsis is concrete
The 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:
parameter
Must be enclosed in parentheses- A pair of empty parentheses is used when there are no arguments
(a)
- No delimiters are required between multiple parameters
- A pair of empty parentheses is used when there are no arguments
The return value
With theparameter
behind
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 signature
In theA one-dimensional array
Is, is, is, isBasic types of
The symbol is preceded by the symbol[
2 d array
That is[[
Parameters of the signature
The 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 integers
andBoolean type
, the return value isThe integer
(Ljava/lang/String;) Ljava/lang/String;
: the parameters forstring
, the return value isstring
Use the JNI type
Java
The basic types of are unsigned, but correspondingJNI type
Not all of them are unsigned. Let’s seeJNI type
Definition:
// 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
JNI
theAn array type
As can be seen from the above definition, in fact, onlyjobject
throughtypedef
The 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:
- through
GetArrayLength
Check the length of the array - through
GetIntArrayElements
Take the array pointer and convert it tojint
type - through
ReleaseIntArrayElements
Free up unwanted array memory
jstring
Is the essence ofjobject
Class, 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
- 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
- parameter
clazz
Refers to theThe Java class object
You can do it by a functionFindClass
getFindClass
The prototype of the function is as follows:
jclass FindClass(const char* name){} Copy the code
- parameter
name
Refers to theJava classes
The name of the- Such as:
java/lang/String
- Such as:
jclass
A type is used to representJava classes
- in
JNI
In, often to passjclass
To get theJava classes
Member variables and methods of
- parameter
methodID
Refers to theJava classes
Constructor 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 one
Java classes
The constructor of theGetMethodID
, the parameter passed in must be<init>
Can only be- Of a class
A constructor
This will be converted to<init>
methods
- Of a class
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 useful
Dalvik virtual machine
So this part is not recorded - through
grep
In theart/runtime/jni_internal.cc
The function implementation is found in
Let’s take a look at the jni_internal.cc code snippet:
- Defines a
JNI
Class, and according toJNINativeInterface
To 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 a
JNINativeInterface
An 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 are
Java thread
In the callNative layer
theJNI
Function to which the thread belongsJNIEnv
Has 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 one
JavaVM
Pointer to - through
JavaVM
thegetEnv
The function can obtainJNIEnv
Object pointer JavaVM
In 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
_JavaVM
Virtual machine life cycle defines several methods related to the virtual machine life cycle- Methods are implemented by invocation
JNIInvokeInterface functions
The 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
Zygote
Process will executeART
thestart
methodsART
thestart
The method is initializedJNIEnv
object
Here is the simple analysis process, this is the main thread yo:
ART
The start function of theframeworks/base/core/jni/AndroidRuntime.cpp
In, 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
- Calls to the
startVm
Function, 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
- Call it again
JNI_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:
- for
The main thread
For example, when the virtual machine starts up,JNIEnv
It’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 that
Java
Start the thread throughstart()
methods start()
The method executesnative
functionnativeCreate()
nativeCreate()
Call isThread_nativeCreate
function
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:
- In the main thread
JNIEnv
Objects are already created when the virtual machine is created - Java to create a thread
JNIEnv
Objects 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
- if
JNI
callJava layer
An exception occurred when the method ofJNI calls
Will return normally- However, if you continue to call other
JNI function
May 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
Throw
The throw () function is used to throw an existing exception, usually after catching itThrowNew
The function is used to generate a new exception and throw it out- parameter
clazz
Refers to theJava
In theException class
And a derived class object
- parameter
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?
- each
Java thread
There is aLocalReference
Table. The VM does not reclaim objects in the table during memory reclamation. - And when you create an object,
JNIEnv
I’m going to implicitly put thisJava object
To join theLocalReference
In 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
NewGlobalRef
Here are a few things:- First the
Java object
fromLocalReference
In the table to delete - Then place the object in
GlobalReference
In the table
- First the
DeleteGlobalRef
To do:- will
Java object
fromGlobalReference
In the table to delete - Objects are not collected immediately and will not be released until the next garbage collection
- will
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 system
JNI function
More 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.