1 这两个函数,主要是比较在编译为so文件之后,C++的名称粉碎机之
int add(int a,int b){
return a+b;
}
extern "C" int add1(int a,int b){
return a + b;
}
2 非静态成员方法 :第二个参数是 jobject
extern "C" JNIEXPORT void JNICALL
Java_com_example_androidsrcndk_MainActivity_myfirst(JNIEnv *env, jobject thiz) {
}
3 静态成员方法 :第二个参数是 jclass
extern "C" JNIEXPORT void JNICALL
Java_com_example_androidsrcndk_MainActivity_staticFunc(JNIEnv *env, jclass clazz) {
4 使用 .c中实现的函数,导入头文件的时候,需要以 extern “C” { #include “XXX.h” }的方式导入
extern "C" {
#include "testc.h"
}
5 需要把testc.c文件添加到CMakeLists.txt文件中
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
testc.c
# Provides a relative path to your source file(s).
native-lib.cpp )
6 android 自带的日志输出
#include<android/log>
__android_log_print(ANDROID_LOG_INFO,"JNIStudy","jni result : %d",result);
7 JNI 中的基本数据类型
jboolean jboolean1 = true;
jint final = 10 , jintresult =0;
for(int i=0;i<final;i++){
jintresult += i;
}
jdouble jdouble1 = 10;
jfloat jfloat1 = 10;
jchar jchar1 = 'A';
jlong jlong1 = 1;
jshort jshort1 =1;
jbyte jbyte1 = 'A';
8 测试:解释模式下(java层),JIT,quick下运行的速度
// 在纯解释模式下进行测试:在android4.4之前、在android7.0之后第一次运行。JNI是java效率的5倍
// 在quick模式下进行测试:使用android6进行测试。JNI是java效率的1.5倍左右
9 4中引用数据类型
jobject jobject1;
jclass jclass1;
jstring jstring1;
jthrowable jthrowable1;
10 数组
jintArray jintArray1;
jbooleanArray jbooleanArray2;
jshortArray jshortArray1;
jcharArray jcharArray1;
jbyteArray jbyteArray1;
jfloatArray jfloatArray1;
jlongArray jlongArray1;
jdoubleArray jdoubleArray1;
11 JNI下的字符串操作
// C 字符串 转为 jstring
jstring creatrJString = env->NewStringUTF("Create jString from C String");
// jstring 转 C 字符串
const char* getCstringFromJstring = env->GetStringUTFChars(string, nullptr);
// 获取jstring的长度
jsize jString_len = env->GetStringUTFLength(creatrJString);
// 释放 jstring转成的 const char*
env->ReleaseStringUTFChars(string,getCstringFromJstring);
const jchar * jcharString = env->GetStringChars(string, nullptr);
jsize jstringLen = env->GetStringLength(string);
jstring jstringFromjCharString = env->NewString(jcharString,jstringLen);
env->ReleaseStringChars(string,jcharString);
return string;
12 隐藏符号信息
__attribute__ ((visibility ("hidden"))) void notSee(){
__android_log_print(ANDROID_LOG_INFO,"JNIStudy","not see");
}
13获取JavaVM
// 1 通过JNI_OnLoad获取
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved)
// 通过JNIEnv获取
JavaVM *thisvm = nullptr;
env->GetJavaVM(&thisvm);
14 JNIEnv
// 1 通过JavaVM获取主线程的JNIEnv
JNIEnv *threadenv = nullptr;
globalVM->GetEnv((void **) &threadenv
// 2 对于native创建的新线程,需要在线程中使用线程自身的JNIEnv
// c创建新线程
pthread_t thread;
pthread_create(&thread, nullptr, threadtest, nullptr);
pthread_join(thread, nullptr);
// 新线程,需要获取自身的JNIEnv,并附加到JavaVM(这个JNIEnv需要知道自身从属于哪个虚拟机)
void *threadtest(void *args) {
JNIEnv *threadenv = nullptr;
int result = globalVM->AttachCurrentThread(&threadenv, nullptr);//将当前线程附加到JavaVM中,并获取当前JNIEnv
if (result == JNI_OK);
threadenv->ExceptionDescribe();//打印异常
threadenv->ExceptionClear();//清除异常
globalVM->DetachCurrentThread();//重要
pthread_exit(0);
}
15 jni 中新建对象
// 方法1
// 使用函数NewObject来创建Java对象
// 构造方法的方法返回值类型的签名始终为void
jclass TestExtendClass = env->FindClass("com/example/androidsrcndk/TestExtend");
jmethodID mid_date = env->GetMethodID(TestExtendClass,"<init>","(Ljava/lang/String;)V");
jstring arg = env->NewStringUTF("I am From Jni");
jobject testObject = env->NewObject(TestExtendClass,mid_date,arg);
if(testObject != nullptr){
__android_log_print(ANDROID_LOG_INFO,"JNIStudy","create new Object success");
}
// 方法2
// 使用函数AllocObject来创建Java对象
// 使用函数AllocObject可以根据传入的jclass创建一个Java对象,但是该对象的状态是非初始化的,
// 在使用这个对象之前必须要用CallNovirtualVoidMethod来进行初始化,这样可以延迟构造函数的调用,该方式用的很少
jobject testObject2 = env->AllocObject(TestExtendClass);
jstring arg1 = env->NewStringUTF("I am From Jni AllocObject");
env->CallNonvirtualVoidMethod(testObject2,TestExtendClass,mid_date,arg1);
16 JNI访问Java类属性
/*
* ### JNI访问类属性和Java反射访问类属性的对比
1、Java反射中获取属性时只需要传入属性姓名即可,而jni中还需要传入该属性的签名信息
2、Java反射中在对privaate类型属性访问时需要先取消安全检查,即调用setAccessible(true),然后才能够访问,而jni当中则不需要
3、Java反射中对普通函数使用Method,而对构造函数使用Constructor,在jni当中,构造函数也是普通的函数,依然用jmethodID,只不过函数名都是<init>
* */
// 对jni来说,java层只有静态属性和非静态属性,无视private
// TODO 对于静态属性
// jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
// jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
// jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
// jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
// jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
// jint GetStaticIntField(jclass clazz, jfieldID fieldID)
// jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
// jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
// jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
// void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
// void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
// void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
// void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
// void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
// void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
// void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
// void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
// void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
// 设置静态属性
jclass TestJclass = env->FindClass("com/example/androidsrcndk/TestExtend");
jfieldID publicStaticField_fid = env->GetStaticFieldID(TestJclass,
"publicStaticField_TestExtend","Ljava/lang/String;");
jstring setjstring = env->NewStringUTF("modified by jni");
env->SetStaticObjectField(TestJclass, publicStaticField_fid, setjstring);
// 获取静态属性的值
jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,publicStaticField_fid));
const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
__android_log_print(4, "showme->jni", "publicStaticField_TestExtend->%s", publicStaticField_content);
// 获取java类中的private静态属性
jfieldID privateStaticField_fid = env->GetStaticFieldID(TestJclass, "privateStaticField_TestExtend","Ljava/lang/String;");
jstring privateStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,privateStaticField_fid));
const char *privateStaticField_content = env->GetStringUTFChars(privateStaticField_obj,nullptr);
__android_log_print(4, "showme->jni", "privateStaticField_TestExtend->%s", privateStaticField_content);
// TODO 对于非静态的属性
// jobject GetObjectField(jobject obj, jfieldID fieldID)
// jboolean GetBooleanField(jobject obj, jfieldID fieldID)
// jbyte GetByteField(jobject obj, jfieldID fieldID)
// jchar GetCharField(jobject obj, jfieldID fieldID)
// jshort GetShortField(jobject obj, jfieldID fieldID)
// jint GetIntField(jobject obj, jfieldID fieldID)
// jlong GetLongField(jobject obj, jfieldID fieldID)
// jfloat GetFloatField(jobject obj, jfieldID fieldID)
// jdouble GetDoubleField(jobject obj, jfieldID fieldID)
//
// void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
// void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
// void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
// void SetCharField(jobject obj, jfieldID fieldID, jchar value)
// void SetShortField(jobject obj, jfieldID fieldID, jshort value)
// void SetIntField(jobject obj, jfieldID fieldID, jint value)
// void SetLongField(jobject obj, jfieldID fieldID, jlong value)
// void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
// void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
jclass TestExtendClass = env->FindClass("com/example/androidsrcndk/TestExtend");
jmethodID mid_date = env->GetMethodID(TestExtendClass,"<init>","(Ljava/lang/String;)V");
jstring arg = env->NewStringUTF("I am From Jni");
jobject testObject = env->NewObject(TestExtendClass,mid_date,arg);
jfieldID publicNonStaticField_TestExtend_Fid = env->GetFieldID(TestExtendClass, "publicNonStaticField_TestExtend","Ljava/lang/String;");
jstring publicNonStaticField_TestExtend_Fid_string = static_cast<jstring>(env->GetObjectField(testObject,publicNonStaticField_TestExtend_Fid));
const char *publicNonStaticField_TestExtend_Fid_content = env->GetStringUTFChars(publicNonStaticField_TestExtend_Fid_string,nullptr);
__android_log_print(4, "showme->jni", "publicNonStaticField_TestExtend_Fid->%s", publicNonStaticField_TestExtend_Fid_content);
jfieldID privateNonStaticField_TestExtend_fid = env->GetFieldID(TestExtendClass, "privateNonStaticField_TestExtend","Ljava/lang/String;");
jstring privateNonStaticField_TestExtend_fid_String = static_cast<jstring>(env->GetObjectField(testObject,privateNonStaticField_TestExtend_fid));
const char *privateNonStaticField_TestExtend_fid_Content = env->GetStringUTFChars(privateNonStaticField_TestExtend_fid_String,nullptr);
__android_log_print(4, "showme->jni", "privateNonStaticField_TestExtend_fid->%s", privateNonStaticField_TestExtend_fid_Content);
jstring chang_non_static_public = env->NewStringUTF("modified by jni chang_non_static_public");
env->SetObjectField(testObject,publicNonStaticField_TestExtend_Fid,chang_non_static_public);
jstring publicNonStaticField_TestExtend_Fid_string_change = static_cast<jstring>(env->GetObjectField(testObject,publicNonStaticField_TestExtend_Fid));
const char *publicNonStaticField_TestExtend_Fid_content_change = env->GetStringUTFChars(publicNonStaticField_TestExtend_Fid_string_change,nullptr);
__android_log_print(4, "showme->jni", "publicNonStaticField_TestExtend_Fid_string_change->%s", publicNonStaticField_TestExtend_Fid_content_change);
// 数组,类属性
jfieldID publicNonStaticIntArray_FID = env->GetFieldID(TestExtendClass, "intarray","[I");
jintArray publicNonStaticIntArray_FID_obj = static_cast<jintArray>(env->GetObjectField(testObject,publicNonStaticIntArray_FID));
int arraylength=env->GetArrayLength(publicNonStaticIntArray_FID_obj);//获取数组的长度
__android_log_print(4, "showme->jni", "intarray length->%d", arraylength);
int* array=env->GetIntArrayElements(publicNonStaticIntArray_FID_obj, nullptr);//得到数组的指针
for(int i=0;i<arraylength;i++){
__android_log_print(4, "showme->jni", "array[%d]->%d", i,array[i]);
}
jint jni_array[arraylength];
for(int j=0;j<arraylength;j++){
jni_array[j]=10-j;
}
const jint* ptr=jni_array;
env->SetIntArrayRegion(publicNonStaticIntArray_FID_obj,0,arraylength,ptr);//设置数组的值
int* array2=env->GetIntArrayElements(publicNonStaticIntArray_FID_obj, nullptr);//得到数组的指针
for(int i=0;i<arraylength;i++){
__android_log_print(4, "showme->jni", "array[%d]->%d", i,array2[i]);
}
17 jni 访问类中的函数
// 访问构造函数
// 对于构造函数,只能通过NewObject和AllocObject来调用。因为构造函数本身首先不是静态函数,自然不能够通过类名进行调用;
// 其次,如果按照一般的函数进行调用的话,还需要传入对象,而此时还没有对象,因此只能是一种特例的函数调用
jclass TestExtendClass = env->FindClass("com/example/androidsrcndk/TestExtend");
jmethodID mid_date = env->GetMethodID(TestExtendClass,"<init>","(Ljava/lang/String;)V");
jstring arg = env->NewStringUTF("I am From Jni");
jobject testObject = env->NewObject(TestExtendClass,mid_date,arg);
jmethodID publicStaticMethodID = env->GetStaticMethodID(TestExtendClass,"publicStaticFunc_TestExtend","()V");
jmethodID privateNonStaticMethodID = env->GetMethodID(TestExtendClass,"privateNonStaticFunc_TestExtend","(Ljava/lang/String;)V");
// Call Static Boolean Method
// 调用方法 静态方法或者非静态方法 返回值类型
// jboolean (*CallStaticBooleanMethodA)(JNIEnv*, jclass, jmethodID, const jvalue*);
// jbyte (*CallStaticByteMethod)(JNIEnv*, jclass, jmethodID, ...);
// jchar (*CallStaticCharMethod)(JNIEnv*, jclass, jmethodID, ...);
// jshort (*CallStaticShortMethodV)(JNIEnv*, jclass, jmethodID, va_list);
// 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, ...);
// NewObject( ) 将传递给构造函数的所有参数紧跟着放在methodID参数的后面
// NewObjectA( ) 将传递给构造函数的所有参数放在jvalue类型的数组args中
// NewObjectV( ) 将传递给构造函数的所有参数放在va_list类型的参数args中
// jstring arg1 = env->NewStringUTF("i am from jni");
//
// jvalue args[2];
// args[0].i = 200;
// args[1].l = arg1;
// callNonVirtual<>method 是类似于c++里面父类声明的非抽抽象函数
// father继承grandfather,那调用callNonVirtual<>method的时候是调用的father的函数还是grandfather的函数呢
// 经过试验发现,调用哪个函数,是callNonVirtual<>method的第二个参数决定的
// jchar (*CallNonvirtualCharMethod)(JNIEnv*, jobject, jclass, jmethodID, ...);
env->CallStaticVoidMethod(TestExtendClass,publicStaticMethodID);
jstring privateNonStaticMethodIdArg = env->NewStringUTF("privateNonStaticMethodIdArg");
env->CallVoidMethod(testObject,privateNonStaticMethodID,privateNonStaticMethodIdArg);
// 函数调用,参数放在jvalue类型的数组args中
jmethodID privatetest = env->GetMethodID(TestJclass, "privatetest",
"(ILjava/lang/String;)Ljava/lang/String;");
jstring arg1 = env->NewStringUTF("i am from jni");
jvalue args[2];
args[0].i = 200;
args[1].l = arg1;
jstring result_obj = static_cast<jstring>(env->CallObjectMethodA(testobj, privatetest, args));
const char *result_ptr = env->GetStringUTFChars(result_obj, nullptr);
__android_log_print(4, "kanxue->jni", "privatetest->%s", result_ptr);
// 函数调用,返回值是数组
jmethodID privateFunc_array_mid = env->GetMethodID(TestJclass, "privateFunc_array", "(I)[I");
jintArray array_obj = static_cast<jintArray>(env->CallObjectMethod(testobj,
privateFunc_array_mid, 100));
jint *array_ptr = env->GetIntArrayElements(array_obj, nullptr);
for (int i = 0; i < env->GetArrayLength(array_obj); i++) {
__android_log_print(4, "kanxue->jni", "array[%d]->%d", i, array_ptr[i]);
}
18 包装获取JNIEnv
// 全局变量,在JNI_OnLoad给其赋值
JavaVM *gJvm = nullptr;
JNIEnv *getEnv() {
JNIEnv *env;
int status = gJvm->GetEnv((void **) &env, JNI_VERSION_1_6);
if (status < 0) {
status = gJvm->AttachCurrentThread(&env, NULL);
if (status < 0) {
return nullptr;
}
}
return env;
}
19 native层通过pthread创建的线程如何访问非bootClassLoader中的类
// pthread创建的线程,在获取到自身的JNIEnv后,默认的ClassLoader是bootClassLoader
// 全局变量,用于保存从JNI_OnLoad保存的来的PathClassLoad
jobject gClassLoader = nullptr;
JNIEXPORT jint JNI_OnLoad(JavaVM* vm,void* reserved){
jclass classClass = env->FindClass("java/lang/Class");
jmethodID getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
"()Ljava/lang/ClassLoader;");
jobject localClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod);
//gClassLoader=localClassLoader;
gClassLoader = env->NewGlobalRef(localClassLoader);
jclass loadClass(JNIEnv *env, const char *name) {//先尝试用env->loadClass进行加载,失败了再用缓存的classloader进行加载
jclass result = nullptr;
if (env) {
//处理异常
result = env->FindClass(name);
jthrowable exception = env->ExceptionOccurred();//清除异常
if (exception) {
env->ExceptionClear();
classLoader.loadClass("")
jclass ClassLoaderClass = env->FindClass("java/lang/ClassLoader");
jmethodID loadClassMethod = env->GetMethodID(ClassLoaderClass, "loadClass",
"(Ljava/lang/String;)Ljava/lang/Class;");
return static_cast<jclass>(env->CallObjectMethod(gClassLoader, loadClassMethod,
env->NewStringUTF(name)));
}
}
return result;
}
20 内联汇编
https://blog.csdn.net/u014679440/article/details/114173810
21 jni重写java层方法
https://blog.csdn.net/u014679440/article/details/114174520
全局引用、局部应用、弱全局引用
Java中的内存,程序员不需要关心
C/C++中的内存,new之后需要delete,malloc需要free。否则就会有内存泄露
JNI规范中定义了三种引用
- 局部引用
- 通过NewLocalRef和各种JNI杰阔创建(FindClass,NewObject,GetObject和NewCharArray等)。会阻止GC回收所引用的对象。局部引用只能在当前函数中使用,函数返回后局部引用所引用的对象会被JVM自动释放,或调用DeleteLocalRef手动释放。因此,局部引用不能跨函数使用,不能跨线程使用。
- 全局引用
- 调用NewGlobalRef基于局部引用创建,会阻止GC回收所引用的对象。全局引用可以跨函数、跨线程使用。ART不会自动释放,必须调用DeleteGlobalRef手动释放,否则会出现内存泄露
- 弱全局引用
- 调用NewWeakGlobalRef基于局部引用或全局引用创建,不会阻止GC回收所引用的对象,可以跨方法、跨线程使用。但与全局引用很重要不同的一点是,弱引用不会阻止GC回收它引用的对象。但是引用也不会自动释放,在ART认为应该回收它的时候(比如内存紧张的时候)进行回收而被释放,或调用DeleteWeakGlobalRef手动释放
局部引用太多,不释放,比方说是循环,可以造成问题(局部引用表只有512个)
局部引用的管理:
1、函数返回时自动释放
2、手动调用deletelocalRef释放
3、使用JNI提供的一系列函数来管理局部引用的声明周期
22 全局引用(弱全局应用使用差不多)
// 创建全局引用
TestJNiEnvJclass = static_cast<jclass>(env->NewGlobalRef(randomClass));
// 释放全局引用
env->DeleteGlobalRef(TestJNiEnvJclass);
23 管理局部引用
// 创建局部引用 - 默认创建的引用都是局部引用,函数返回时会自动释放
NewLocalRef
// 释放局部引用
DeleteLocalRef
// 确保函数能够创建len个局部引用,返回值为0,表示可以
env->EnsureLocalCapacity(len)
// PushLocalFrame可以为当前函数中需要用到的局部引用创建一个引用堆栈
// PopLocalFrame函数销毁栈中所有的引用
if (env->PushLocalFrame(20) == 0) {
for (int i = 0; i < 18; i++) {
jstring tmp = env->NewStringUTF("showme");
}
result = env->PopLocalFrame(NULL);
// result = env->PopLocalFrame(obj); 除了ojb对象,其他都销毁掉
}
24 动态注册
静态注册与动态注册
对于任意一个jni函数来说,在该函数被调用前,必须要完成java函数与so中地址的绑定。
-
静态注册 :这个绑定过程可以是被动的,即由Dalvik/ART虚拟机在调用前查找并完成地址的绑定;
1、对应的函数名:Java + 包名 + 类名 + 方法名
其中使用下划线将每部分分隔开,包名也使用下划线隔开,如果名称中本来就包含下划线,将使用下划线加数字替换。
示例(包名:com.afei.jnidemo,类名:MainActivity)
// java native method
public native String stringFromJNI();
// JNI method
JNIEXPORT jstring JNICALL
Java_com_afei_jnidemo_MainActivity_stringFromJNI(JNIEnv* env,jobject instance);
2、优点:对于静态注册来说,简单明了,语义清晰
3、缺点:不够安全
必须遵循注册规则从而导致名字过长了;由于要保留符号名,很容易使用IDA等直接定位地址
-
动态注册 :也可以是主动的,即由app自己完成地址的绑定
1、定义
通过RegisterNatives方法手动完成native方法和so中的方法的绑定,这样虚拟机就可以通过这个函数映射关系直接找到相应的方法了。
2、注册过程示例
a.假设有两个native方法如下
public native String stringFromJNI();
public static native int add(int a,int b);
b.通常我们在JNI_Onload方法中完成动态注册,事实上只需要在该jni函数被调用前的任意实际完成注册即可,甚至是多次注册到不同的地址都可以
jni RegisterNatives(JNIEnv* env){
jclass MainActivityjclass = env->FindClass("com/showme/reflectiontest/MainActivity");
JNINativeMethod jniNativeMethod[] = {
{"onCreate","(Landroid/os/Bundle;)V", (void *) onCreate},
{"getNonStaticField", "(Ljava/lang/Object;)V", (void *) cccc}
};
return env->RegisterNatives(MainActivityjclass, jniNativeMethod,
sizeof(jniNativeMethod) / sizeof(JNINativeMethod));
}
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
__android_log_print(4, "showme->jni", "jni->%s",
"vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
}
jint result = RegisterNatives(env);
return JNI_VERSION_1_6;
}
可以通过Frida hook RegisterNatives中函数绑定的过程,找到函数的地址
dvmSetNativeFunc函数中:method->nativeFunc = func 完成地址的绑定
jni_internal.cc RegisterNativeMethoids -> art_method.cc RegisterNative : 完成地址绑定
25 位于.init节中,执行时机比JNI_OnLoad早
extern "C" void _init(void) {
__android_log_print(4, "jni", "%s", "enter _init");
}
26 相当于于so的构造函数,在.init_array中,执行时机比JNI_OnLoad早
// 先定义,先执行
// constructor(4) 数字小的先执行
__attribute__ ((constructor, visibility ("hidden"))) void initarray_3(void) {
__android_log_print(4, "jni", "%s", "enter initarray_8");
}
__attribute__ ((constructor, visibility ("hidden"))) void initarray_4(void) {
__android_log_print(4, "jni", "%s", "enter initarray_9");
}
__attribute__ ((constructor(4), visibility ("hidden"))) void initarray_11(void) {
__android_log_print(4, "jni", "%s", "enter initarray_1");
}
__attribute__ ((constructor(3), visibility ("hidden"))) void initarray_22(void) {
__android_log_print(4, "jni", "%s", "enter initarray_2");
}