当前位置:网站首页>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-28 08:44:00 【Pengxurui】
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 !

边栏推荐
- Can‘t connect to server on ‘IP‘ (60)
- HCIP第八天
- leetcode刷题,我推荐B站这个妹子学霸的视频
- (十三)基于51单片机的简单温度报警装置
- CAT1 4g+ Ethernet development board 232 data is sent to the server through 4G module TCP
- How to write a JMeter script common to the test team
- Js继承方法
- Export SQL server query results to excel table
- Let me teach you how to assemble a registration center?
- Blog building 7: Hugo
猜你喜欢

Alibaba internal interview materials

Day112. Shangyitong: Mobile verification code login function

快速搭建一个网关服务,动态路由、鉴权的流程,看完秒会(含流程图)

置顶各大平台,22版面试核心知识解析笔记,强势上榜

Competition: diabetes genetic risk detection challenge (iFLYTEK)

tkMapper的使用-超详细

Day112.尚医通:手机验证码登录功能

思迈特软件Smartbi完成C轮融资,推动国产BI加速进入智能化时代

Shell programming specifications and variables

49-OpenCv深入分析轮廓
随机推荐
谷歌 Material Design 的文本框为什么没人用?
思迈特软件Smartbi完成C轮融资,推动国产BI加速进入智能化时代
Unity中队列(Queue)的简单使用
置顶各大平台,22版面试核心知识解析笔记,强势上榜
Maximum product of leetcode/ word length
我来教你如何组装一个注册中心?
Slice function of JS handwriting function (thoroughly understand the header but not the footer)
Smart software completed round C financing, making Bi truly "inclusive"
'global event bus' &' message subscription and Publishing '
图片批处理|必备小技能
Redis basic knowledge, let's review it
GBase 8s是否支持存储关系型数据和对象型数据?
[soft test software evaluator] 2013 comprehensive knowledge over the years
leetcode/数组中和为0的三个不同数
Source code analysis of linkedblockingqueue
博客搭建九:hugo添加搜索功能
SQL injection - pre Foundation
2022牛客多校第二场解题报告
HCIP第九天_BGP实验
微信小程序----微信小程序浏览pdf文件