10-JNI开发

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");
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值