当前位置:网站首页>NDK series (6): let's talk about the way and time to register JNI functions
NDK series (6): let's talk about the way and time to register JNI functions
2022-07-27 23:29:00 【Peng Chouchou】
Please like and pay attention , Your support means a lot to me .
Hi, I'm Xiao Peng . This paper has included GitHub · Android-NoteBook in . Here you are Android Advanced growth knowledge system , Have like-minded friends , Official account [ Peng XURUI ] Take you to build your core competitiveness .
Preface
In the last article , We mentioned registration JNI function ( establish Java native Methods and JNI Mapping of functions ) There are two ways : Static registration and dynamic registration . Today, let's talk about this in detail 2 The usage and implementation principle of three registration methods .

This article is NDK Series article No 6 piece , Column list :
One 、 Language foundation :
1、NDK Learning route : How to learn & My experience 2、C Language foundation 3、C ++ Language foundation 4、C/C++ The build process : From source code to program running
Two 、NDK Development :
1、JNI Basics :Java And Native Interaction 2、 register JNI function : Static registration & Dynamic registration ( this paper ) 3、NDK Basics :ndk-build & CMake 4、so File loading process analysis : understand Android in loadLibrary() The implementation process of 5、so File adaptation 64 An architecture :Gradle The plug-in retrieves unadapted items with one click 6、so File dynamic : Dynamic download 7、so File volume optimization : File reduction
3、 ... and 、 The basic theory
1、 Basic theory of video 2、 Basic audio theory 3、H.264 Video compression coding 4、 Audio compression coding 5、FFMPEG Basics 6、OPENSL ES Basics 7、PNG picture : Lossless compression coding
Four 、 Fundamentals of computer
1. Static registration JNI function
1.1 Static registration usage
Static registration is based on 「 Appointment 」 Naming rules , adopt javah Can be generated automatically native Function declaration corresponding to method (IDE Intelligent generation , There is no need to execute commands manually ). for example :
HelloWorld.java
package com.xurui.hellojni;
public class HelloWorld {
public native void sayHi();
}
Carry out orders :javac -h . HelloWorld.java( take javac and javah Merge ), Corresponding JNI function :
com_xurui_hellojni_HelloWorld.h
...
JNIEXPORT void JNICALL Java_com_xurui_hellojni_HelloWorld_sayHi
(JNIEnv *, jobject);
...
The naming rules of static registration are divided into 「 No overload 」 and 「 There are heavy loads 」2 In this case : When there is no heavy load 「 Short name 」 The rules , When there is heavy load 「 long name 」 The rules .
Short name rule (short name): Java_[ The fully qualified name of the class ( Underlined )]_[ Method name ], Among the fully qualified names of classes.Change it to_;Long name rule (long name): Add two underscores to the short name ( __) And parameter descriptor , To distinguish function overloading .
Here we explain why we need to splice parameter descriptors when overloading ? because C Language has no function overloading , Function overloads cannot be distinguished by parameters , That's why you need to splice suffixes to eliminate overloading .
1.2 Principle analysis of static registration
Now? , Let's analyze the static registration matching JNI The execution of the function . Because there is no directly related information and function call entry , I was doing loadLibrary() load so The execution process of the library is to analyze clues , Finally, it is oriented to FindNativeMethod() This method , Judging from the content, it should be right .
// List of shared libraries
std::unique_ptr<Libraries> libraries_;
// Search for native Corresponding JNI Function pointer to function
void* FindNativeMethod(Thread* self, ArtMethod* m, std::string& detail) {
// 1、 obtain native The short name and long name corresponding to the method
std::string jni_short_name(m->JniShortName());
std::string jni_long_name(m->JniLongName());
// 2、 In the loaded so Search in the library
void* native_code = FindNativeMethodInternal(self,
declaring_class_loader_allocator,
shorty,
jni_short_name,
jni_long_name);
return native_code;
}
// 2、 In the loaded so Search in the library
void* FindNativeMethodInternal(Thread* self,
void* declaring_class_loader_allocator,
const char* shorty,
const std::string& jni_short_name,
const std::string& jni_long_name) {
for (const auto& lib : libraries_) {
SharedLibrary* const library = lib.second;
// 2.1 Check whether it is the same ClassLoader
if (library->GetClassLoaderAllocator() != declaring_class_loader_allocator) {
continue;
}
// 2.2 Search the short name first
const char* arg_shorty = library->NeedsNativeBridge() ? shorty : nullptr;
void* fn = dlsym(library, jni_short_name)
// 2.3 Then search for long names
if (fn == nullptr) {
fn = dlsym(library, jni_long_name)
}
if (fn != nullptr) {
return fn;
}
}
return nullptr;
}
// 1、 obtain native The short name and long name corresponding to the method
// Short name
std::string ArtMethod::JniShortName() {
return GetJniShortName(GetDeclaringClassDescriptor(), GetName());
}
// long name
std::string ArtMethod::JniLongName() {
std::string long_name;
long_name += JniShortName();
long_name += "__";
std::string signature(GetSignature().ToString());
signature.erase(0, 1);
signature.erase(signature.begin() + signature.find(')'), signature.end());
long_name += MangleForJni(signature);
return long_name;
}
std::string GetJniShortName(const std::string& class_descriptor, const std::string& method) {
// Here is the calculation logic of short name
}
The code above is very simplified , The main process is as follows :
1、 Calculation native Short and long names of methods ; 2、 Define native Class loader of method class , In the loaded so library libraries_Mid search JNI function . If it is not loaded in advance so library , Naturally, you cannot search , Will throw outUnsatisfiedLinkErrorabnormal ;3、 Establish internal data structure , establish Java native Methods and JNI Mapping relationship of function pointer of function ; 4、 Subsequent calls native Method , Then directly call the recorded function pointer .
About loading so The process of the library is 4、so File loading process analysis This article talks about , The loaded shared library is stored in libraries_ In the table .

2. Dynamic registration JNI function
Static registration is called for the first time Java native Method to search for the corresponding JNI function , Dynamic registration is to manually establish the mapping relationship in advance , And do not need to comply with static registration JNI Function naming rules .
2.1 Dynamic registration usage
Dynamic registration requires RegisterNatives(...) function , Its definition is jni.h In file :
jni.h
struct JNINativeInterface {
// register
// Parameter two :Java Class Object representation
// Parameter 3 :JNINativeMethod Array of structs
// Parameter 4 :JNINativeMethod Structure array length
jint (*RegisterNatives)(JNIEnv*, jclass, const JNINativeMethod*, jint);
// Cancellation
// Parameter two :Java Class Object representation
jint (*UnregisterNatives)(JNIEnv*, jclass);
};
typedef struct {
const char* name; // Java Method name
const char* signature; // Java Method Descriptor
void* fnPtr; // JNI A function pointer
} JNINativeMethod;
The sample program
// Need to register Java Layer class name
#define JNIREG_CLASS "com/xurui/MainActivity"
// JNINativeMethod Array of structs
static JNINativeMethod gmethod[] = {
{"onStart","()I",(void*)onStart}, // JNINativeMethod Structure
};
// load so Library callback
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_6) != JNI_OK) {
return -1;
}
assert(env != NULL);
// Perform dynamic registration
if (!registerNatives(env)) {
return -1;
}
result = JNI_VERSION_1_6;
return result;
}
// Dynamic registration
static int registerNatives(JNIEnv* env)
{
// Call the tool method to complete the registration
if (!registerNativeMethods(env,JNIREG_CLASS, gmethod, sizeof(gmethod) / sizeof(gmethod[0])))
return JNI_FALSE;
// JNINativeMethod Array of structs
JNINativeMethod methods[] = {
{"onStop","()V",(void*)onStop}, // JNINativeMethod Structure
};
// Call the tool method to complete the registration
if (!registerNativeMethods(env,JNIREG_CLASS, smethod,sizeof(smethod) / sizeof(smethod[0])))
return JNI_FALSE;
return JNI_TRUE;
}
// A tool method
static int registerNativeMethods(JNIEnv* env, const char* className, JNINativeMethod* gmethod, int numMethods)
{
// Get... Based on the class name jclass object
jclass clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
// call RegisterNatives()
if (env->RegisterNatives(clazz, gmethod, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
The code is not complicated , A brief explanation :
1、 registerNativeMethodsIt's just a utility function , To simplify the FindClass This step ;2、 methodsIt's a JNINativeMethod Array of structs , Defined Java native Methods and JNI Mapping of functions , Need record Java Method name 、Java Method Descriptor 、JNI A function pointer ;3、 RegisterNativesFunction is the final registration function , Delivery required jclass、JNINativeMethod Structure array and array length .
2.2 Principle analysis of dynamic registration
RegisterNatives The essence of the method is to specify the mapping relationship directly through the structure , Instead of waiting for the call native Method when searching JNI A function pointer , Therefore, dynamically registered native Method calls are more efficient . Besides , It can also reduce generation so Number of exported symbols in the library file , Can optimize so Volume of library file . For more information see Android Yes so Exploration and practice of volume optimization in “ Compact dynamic symbol table ” chapter .
3. register JNI The timing of the function
To sum up, register JNI The timing of the function , It is mainly divided into 3 Kind of :
| When to register | Registration method | describe |
|---|---|---|
| 1、 In the first call to this native When the method is used | Static registration | The virtual opportunity is JNI Search the function pointer in the function library and record it , Subsequent calls do not require repeated search |
| 2、 load so library | Dynamic registration | load so The library will automatically call back JNI_OnLoad function , Call in it RegisterNatives register |
| 3、 Register in advance | Dynamic registration | In the load so Behind the library , Call the native Before the method , Registered statically native Function trigger RegisterNatives register . For example, in App Startup time , Many system source codes will be registered in advance |
With Android Virtual machine source code as an example : stay App Process startup process , It will be executed once after the virtual machine is created JNI Function registration . We're in a lot of Framework You can see... In the source code native Method , But the call was not found System.loadLibrary(...) The place of , In fact, the registration is completed when the virtual machine starts .
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote) {
...
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\\n");
}
...
}
// start -> startReg:
int AndroidRuntime::startReg(JNIEnv* env) {
androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);
env->PushLocalFrame(200);
// perform JNI register
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
env->PopLocalFrame(NULL);
return 0;
}
// startReg->register_jni_procs:
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env) {
// Traversing a two-dimensional array
for (size_t i = 0; i < count; i++) {
// perform JNI register
if (array[i].mProc(env) < 0) {
return -1;
}
}
return 0;
}
// JNINativeMethod Two dimensional array of structure array
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
...
}
struct RegJNIRec {
int (*mProc)(JNIEnv*);
}
// The return value is JNINativeMethod Array of structs
int register_com_android_internal_os_RuntimeInit(JNIEnv* env)
{
const JNINativeMethod methods[] = {
{ "nativeFinishInit", "()V",
(void*) com_android_internal_os_RuntimeInit_nativeFinishInit },
{ "nativeSetExitWithoutCleanup", "(Z)V",
(void*) com_android_internal_os_RuntimeInit_nativeSetExitWithoutCleanup },
};
return jniRegisterNativeMethods(env, "com/android/internal/os/RuntimeInit",
methods, NELEM(methods));
}
4. summary
Summarize the difference between static registration and dynamic registration :
1、 Static registration establishes mapping relationships based on naming conventions , Dynamic registration passes JNINativeMethodStructure to establish mapping relationship ;2、 Static registration is the first time the native Method to search and establish mapping relationship , Dynamic registration will call this native Establish mapping relationship before method ; 3、 Static registration requires all JNI Function exposed to dynamic symbol table , Dynamic registration does not need to be exposed to the dynamic symbol table , Can be streamlined so The file size .
Reference material
JNI Programming Guide JNI Tips —— Android Official documents Java Native interface specification —— Java Official documents Android Yes so Exploration and practice of volume optimization —— Hong Kai Chang Qiang ( Meituan technical team ) Writing
Your praise means a lot to me ! WeChat search official account [ Peng XURUI ], I hope you can discuss technology , Find like-minded friends , I'll see you next time !

边栏推荐
- 8000 word explanation of OBSA principle and application practice
- 【数字识别】基于知识库实现手写体数字识别附matlab代码
- [image defogging] image defogging based on dark channel and non-mean filtering with matlab code
- Cron expression
- Cloud native enthusiast weekly: a complete collection of client go examples
- See how Gan controls the image generation style step by step? Explain the evolution process of stylegan in detail
- Security-001
- Tita 的OKR系统与其他同类型产品,或者是共享文档等相比,有什么优势?
- Desai wisdom number - other charts (parallel coordinate chart): family's willingness to allocate assets in the future
- Three consecutive high-frequency interview questions of redis online celebrity: cache penetration? Cache breakdown? Cache avalanche?
猜你喜欢

【GNN报告】加拿大蒙特利尔唐建:Geometric Deep Learning For Drug Discovery

常用泰勒展开

置信区间之正态

Excel VBA finds out the maximum and minimum values of a column of time, and repeatedly pastes multiple values according to the actual situation

Fluorescence imaging of cle19 polypeptide in cells preparation of fluorescence quenching quantum dots of bovine serum albumin
![[soft test software evaluator] 2014 comprehensive knowledge over the years](/img/85/46c8fc696ae5249a9aca22b50493f0.jpg)
[soft test software evaluator] 2014 comprehensive knowledge over the years

Exercise --- BFS

Visual display method of machine learning project

Convnext:a convnet for the 2020s - model Brief

Cron expression
随机推荐
回Mixlab三天,“创造力团队”治好了我的精神内耗
图基本知识代码
Excel only wants to visualize charts and make data move? Yes, come and watch (with a large number of templates to download)
Security-001
【GNN报告】加拿大蒙特利尔唐建:Geometric Deep Learning For Drug Discovery
网络开发套接字以及UDP、TCP协议
Excel VBA finds out the maximum and minimum values of a column of time, and repeatedly pastes multiple values according to the actual situation
C language explanation series -- understanding of functions (5) function recursion and iteration
Safety foundation 2
XML 外部实体 (XXE) 漏洞及其修复方法
Node-RED系列(三十):使用持久化ui-table 刷新页面不清空上一次的table数据
Visual display method of machine learning project
【数字识别】基于知识库实现手写体数字识别附matlab代码
用户画像在科技期刊微信公众号精准推送中的应用
NDK 系列(6):说一下注册 JNI 函数的方式和时机
营收、利润两位数增长,华润怡宝悄悄打造了这些过亿新品
Main security risks and Countermeasures of cloud computing services
China Internet Security Report 2021: API threat soared by more than 200%, and the security risk of software supply chain increased
What is the b+tree index of MySQL? How does the cluster index grow?
cron 表达式