NDK开发学习笔记

21 篇文章 0 订阅
3 篇文章 0 订阅
JNI(Java Native Interface)允许Java代码和其他语言写的代码进行交互。本文详细介绍了JNI的使用,包括如何加载库、调用本地方法、处理字符串、提升性能、访问类属性和函数,以及内存管理等。同时,还涉及到Java反射与JNI的对比,并展示了如何通过JNI动态注册函数。
摘要由CSDN通过智能技术生成

NDK开发简介

ndk代码中特有部分,AS中为JAVA有loadlibrary与native函数

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Example of a call to a native method
        TextView tv = findViewById(R.id.sample_text);
        tv.setText(stringFromJNI());
    }

    /**
     * A native method that is implemented by the 'native-lib' native library,
     *      * which is packaged with this application.
     */
    public native String stringFromJNI();
}

在CPP代码中

//函数名以_分割,最后是返回值
//#define JNIEXPORT  __attribute__ ((visibility ("default"))),含义是告诉编译器在编译后保留函数名称,函数名是按C导出的。
//#define JNICALL是调用规则,这里是空的
//JNIEnv* env这个参数为JNIEnv指针
//JNIEnv* env参数跟JNI中类的属性有关,非静态是jobject,静态是jclass
//JNI函数第三个参数才是本身函数
extern "C" JNIEXPORT jstring JNICALL
Java_com_yrq_ndk01_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

在使用其他语言如C语言,记得在CPP开头extern C,如下:

extern "C"{
    #include "test.h"
}

数据类型

NDK开发提升性能

java函数运行模式:

1、纯解释模式下执行;
2、JIT模式
3、经过dex2oat编译后在quick模式下运行 注意: Android 7.0(代号 Nougat,简称 N)开始结合使用 AOT、即时 (JIT) 编译和配置 文件引导型编译。因此一个java函数可能运行在解释模式、JIT或者quick模式

引用类型

jni中的字符串操作

jstring NewStringUTF(const char* bytes):

函数使用给定的C字符串创建一个新的JNI字符串(jstring),不能为构造的java.lang.String分配足够的内存,NewStringUTF会抛出一个OutOfMemoryError异常,并返回一个NULL

const char* GetStringUTFChars(jstring string, jboolean* isCopy):

函数可用于从给定的Java的jstring创建新的C字符串 (char *)。 如果无法分配内存,则该函数返回NULL。 检查NULL是一个好习惯。不要忘记检查。因为该函数需 要为新诞生的UTF-8字符串分配内存,这个操作有可能因为内存太少而失败。失败时,GetStringUTFChars会返回 NULL,并抛出一个OutOfMemoryError异常,在不使用GetStringUTFChars()返回的字符串时,需要来释放内存和引用以便可以对其进行垃圾回收,因此之后应始终调用ReleaseStringUTFChars()。

jsize GetStringUTFLength(jstring string):

用于获取jstring的长度

java反射思维和NDK开发

类名用途
Class类代表类的实体,在运行的JAVA应用程序中表示类的接口
Field类代表类的成员变量(成员变量也称为类的属性)
Method类代表类的方法
Constructor类代表类的构造方法

Class类

代表类的实体,在运行Java应用程序中表示类和接口。在这个类中提供了很多有用的方法。






Field类

代表类的成员变量

Method类

代表类的方法

Constructor类

Constructor代表类的构造方法


ndk开发当使用ndk对java类进行调用,包含3步。
第一步获取哪个类jclass
第二部获取fielediid,methodid
第三步通过GetObjectFiled/过GetStaticObjectFiled等之类的获取相应变量

##JNI中其他类型:成员ID和方法ID
jfieldID和jmethodID
jvalue是联合体

类描述符和域描述符函数描述符

_JNIEnv和 _JavaVM

在C++中为结构体,C中为指针

/*
 * C++ version.
 */
struct _JavaVM {
    const struct JNIInvokeInterface* functions;

#if defined(__cplusplus)
    jint DestroyJavaVM()
    { return functions->DestroyJavaVM(this); }
    jint AttachCurrentThread(JNIEnv** p_env, void* thr_args)
    { return functions->AttachCurrentThread(this, p_env, thr_args); }
    jint DetachCurrentThread()
    { 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*/
};

C

typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;

区别是c中参数有JNIEnv指针。

javaVM的获取

  1. 在JNI_onLoad中作为参数获得,如下:
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void* reserved),该函数负责自动化查找和传入参数进行调用。
  2. 通过JNIEnv的GetJavaVM函数获取:
    JavaVM* thisjvm=nullptr;
    env->GetJavaVM(&thisjvm);
    对于主线程各种方式获取vm都行,子线程是通过获取的全局变量vm使用,需要先attach到vm上才能获取env。

注意

主线程和子线程各有env,classLoader也不是一样的,如果使用主classLoader,可以保存的全局变量上。

JNI新建对象和访问java中属性

Java_com_kanxue_reflectiontest_MainActivity_newObject(JNIEnv *env, jobject thiz)
{
    //NewObject
    jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
    //public Test(String arg)
//   jmethodID GetMethodID(jclass clazz, const char* name, const char* sig)
    jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;)V");
    jstring arg = env->NewStringUTF("I am From Jni");
    //jobject NewObject(jclass clazz, jmethodID methodID, ...)
    jobject testobj = env->NewObject(TestJclass, con_mid, arg);
    if (testobj != nullptr) {
        __android_log_print(4, "kanxue->jni", "jni->%s", "NewObject success!");
    }

    //AllocObject
    //jclass clazz_str = env->FindClass("java/lang/String");
    //jmethodID methodID_str = env->GetMethodID(clazz_str ,"<init>", "()V");
    jobject testobj2 = env->AllocObject(TestJclass);
    jstring arg1 = env->NewStringUTF("I am From Jni->AllocObject");
    env->CallNonvirtualVoidMethod(testobj2, TestJclass, con_mid, arg1);
    if (testobj2 != nullptr) {

        __android_log_print(4, "kanxue->jni", "jni->%s", "AllocObject success!");
    }
}

JNI访问java属性

JNI属性只有静态非静态属性。

  1. 访问静态类属性
getStaticFieldID

总体大致步骤为:

  • 获取类,env->FindClass
  • 获取属性ID,env->GetStaticFieldID
  • 获取内容,如GetStaticObjectField
    get类似
    大致代码:
Java_com_kanxue_reflectiontest_MainActivity_getStaticField(JNIEnv *env, jobject thiz) {
    // TODO: implement getStaticField()
    //private static
    jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
    //    public static String publicStaticField = "i am a publicStaticField";
    //  jfieldID GetStaticFieldID(jclass clazz, const char* name, const char* sig)
    jfieldID publicStaticField_fid = env->GetStaticFieldID(TestJclass, "publicStaticField",
                                                           "Ljava/lang/String;");
    /*jstring publicStaticField_obj = static_cast<jstring>(env->GetStaticObjectField(TestJclass,
                                                                                   publicStaticField_fid));
    const char *publicStaticField_content = env->GetStringUTFChars(publicStaticField_obj, nullptr);
    __android_log_print(4, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);*/

    jstring setjstring = env->NewStringUTF("modified by jni");
    //    void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
    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, "kanxue->jni", "publicStaticField_obj->%s", publicStaticField_content);

//private static String privateStaticField = "i am a privateStaticField";
    jfieldID privateStaticField_fid = env->GetStaticFieldID(TestJclass, "privateStaticField",
                                                            "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, "kanxue->jni", "privateStaticField_obj->%s", privateStaticField_content);

    //    public static int publicStaticField_int = 100;

    jfieldID publicStaticField_int_fid = env->GetStaticFieldID(TestJclass, "publicStaticField_int",
                                                               "I");


    env->SetStaticIntField(TestJclass, publicStaticField_int_fid, 200);

    jint publicStaticField_int_value = env->GetStaticIntField(TestJclass,
                                                              publicStaticField_int_fid);

    __android_log_print(4, "kanxue->jni", "publicStaticField_int_value->%d",
                        publicStaticField_int_value);

/*    jobject GetStaticObjectField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticObjectField(this, clazz, fieldID); }


    jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticBooleanField(this, clazz, fieldID); }
    jbyte GetStaticByteField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticByteField(this, clazz, fieldID); }
    jchar GetStaticCharField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticCharField(this, clazz, fieldID); }
    jshort GetStaticShortField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticShortField(this, clazz, fieldID); }
    jint GetStaticIntField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticIntField(this, clazz, fieldID); }
    jlong GetStaticLongField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticLongField(this, clazz, fieldID); }
    jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticFloatField(this, clazz, fieldID); }
    jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID)
    { return functions->GetStaticDoubleField(this, clazz, fieldID); }*/



/*    void SetStaticObjectField(jclass clazz, jfieldID fieldID, jobject value)
    { functions->SetStaticObjectField(this, clazz, fieldID, value); }
    void SetStaticBooleanField(jclass clazz, jfieldID fieldID, jboolean value)
    { functions->SetStaticBooleanField(this, clazz, fieldID, value); }
    void SetStaticByteField(jclass clazz, jfieldID fieldID, jbyte value)
    { functions->SetStaticByteField(this, clazz, fieldID, value); }
    void SetStaticCharField(jclass clazz, jfieldID fieldID, jchar value)
    { functions->SetStaticCharField(this, clazz, fieldID, value); }
    void SetStaticShortField(jclass clazz, jfieldID fieldID, jshort value)
    { functions->SetStaticShortField(this, clazz, fieldID, value); }
    void SetStaticIntField(jclass clazz, jfieldID fieldID, jint value)
    { functions->SetStaticIntField(this, clazz, fieldID, value); }
    void SetStaticLongField(jclass clazz, jfieldID fieldID, jlong value)
    { functions->SetStaticLongField(this, clazz, fieldID, value); }
    void SetStaticFloatField(jclass clazz, jfieldID fieldID, jfloat value)
    { functions->SetStaticFloatField(this, clazz, fieldID, value); }
    void SetStaticDoubleField(jclass clazz, jfieldID fieldID, jdouble value)
    { functions->SetStaticDoubleField(this, clazz, fieldID, value); }*/

}


  1. 访问对象属性
//    public native void getNonStaticField(Object m);
extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_getNonStaticField(
        JNIEnv *env,
        jobject obj, jobject testobj) {

    //    private String privateField = "i am a privateField";

    jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");
    jfieldID privateField_fid = env->GetFieldID(TestJclass, "privateField", "Ljava/lang/String;");
    //    jobject GetObjectField(jobject obj, jfieldID fieldID)
    jstring privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));
 /*  const char *privateField_obj_content = env->GetStringUTFChars(privateField_obj,
                                                                    nullptr);
    __android_log_print(4, "kanxue->jni", "privateField_obj->%s", privateField_obj_content);*/

    //
    jstring newString = env->NewStringUTF("Modified by jni");
    env->SetObjectField(testobj, privateField_fid, newString);


    privateField_obj = static_cast<jstring>(env->GetObjectField(testobj, privateField_fid));
    const char *privateField_obj_content = env->GetStringUTFChars(privateField_obj,
                                                                  nullptr);
    __android_log_print(4, "kanxue->jni", "privateField_obj->%s", privateField_obj_content);

/*    void SetObjectField(jobject obj, jfieldID fieldID, jobject value)
    { functions->SetObjectField(this, obj, fieldID, value); }
    void SetBooleanField(jobject obj, jfieldID fieldID, jboolean value)
    { functions->SetBooleanField(this, obj, fieldID, value); }
    void SetByteField(jobject obj, jfieldID fieldID, jbyte value)
    { functions->SetByteField(this, obj, fieldID, value); }
    void SetCharField(jobject obj, jfieldID fieldID, jchar value)
    { functions->SetCharField(this, obj, fieldID, value); }
    void SetShortField(jobject obj, jfieldID fieldID, jshort value)
    { functions->SetShortField(this, obj, fieldID, value); }
    void SetIntField(jobject obj, jfieldID fieldID, jint value)
    { functions->SetIntField(this, obj, fieldID, value); }
    void SetLongField(jobject obj, jfieldID fieldID, jlong value)
    { functions->SetLongField(this, obj, fieldID, value); }
    void SetFloatField(jobject obj, jfieldID fieldID, jfloat value)
    { functions->SetFloatField(this, obj, fieldID, value); }
    void SetDoubleField(jobject obj, jfieldID fieldID, jdouble value)
    { functions->SetDoubleField(this, obj, fieldID, value); }*/

//    private int privateField_int = 200;

    jfieldID privateField_int_fid = env->GetFieldID(TestJclass, "privateField_int", "I");
/*    jint privateField_int_value = env->GetIntField(testobj, privateField_int_fid);
    __android_log_print(4, "kanxue->jni", "privateField_int_value->%d", privateField_int_value);*/

    env->SetIntField(testobj, privateField_int_fid, 300);
    jint privateField_int_value = env->GetIntField(testobj, privateField_int_fid);
    __android_log_print(4, "kanxue->jni", "privateField_int_value->%d", privateField_int_value);

    //    public int[] intarray = null;
    jfieldID  intarray_fid=env->GetFieldID(TestJclass,"intarray","[I");
    jintArray intarray_obj= static_cast<jintArray>(env->GetObjectField(testobj, intarray_fid));

    int arraylength=env->GetArrayLength(intarray_obj);

    __android_log_print(4, "kanxue->jni", "arraylength->%d", arraylength);

/*    int* array=env->GetIntArrayElements(intarray_obj, nullptr);
    for(int i=0;i<arraylength;i++){

        __android_log_print(4, "kanxue->jni", "array[%d]->%d", i,array[i]);
    }*/
    //    void SetIntArrayRegion(jintArray array, jsize start, jsize len,
    //        const jint* buf)
    jint jni_array[arraylength];
    for(int j=0;j<arraylength;j++){

        jni_array[j]=10-j;
    }
    const jint* ptr=jni_array;
    env->SetIntArrayRegion(intarray_obj,0,arraylength,ptr);

    int* array=env->GetIntArrayElements(intarray_obj, nullptr);
    for(int i=0;i<arraylength;i++){

        __android_log_print(4, "kanxue->jni", "array[%d]->%d", i,array[i]);
    }
    //env->SetIntArrayRegion()


}

JNI访问Java类函数

JNI和Java反射访问类属性的对比:

  1. java反射中获取类属性时只需要传入属性名即可,jni还需要传入属性签名信息
  2. java反射中在对private类型属性访问时要先取消安全检查,即调用setAccessible(true),然后才能够访问,而jni中则不需要。
  3. java反射中对普通函数使用Method,而对构造函数使用Constructor,在jni中构造函数也是普通函数,使用jmethodID,只不过函数名都是。

构造函数的访问

对于构造函数调用,只能通过NewObject和AllocObject来调用,因为构造函数本身手下不是静态函数,不能通过类名调用,其次如果按照一般函数进行调用,还需要传入对象,此时还没有对象,只能是一种特例。
###参考代码

extern "C" JNIEXPORT jobject JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callInit(
        JNIEnv *env,
        jobject /* this */) {

    jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

    jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;I)V");

    jstring arg0 = env->NewStringUTF("i am from callInit");

    jobject obj = env->NewObject(TestJclass, con_mid, arg0, 100);
    jmethodID privatetest_mid = env->GetMethodID(TestJclass, "privatetest",
                                                 "(ILjava/lang/String;)Ljava/lang/String;");
    jstring arg2 = env->NewStringUTF("i am from jni-CallObjectMethodA");

    return obj;

}

访问静态类函数

代码

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callStaticFunc(
        JNIEnv *env,
        jobject /* this */) {
    //public static void publicStaticFunc()

    jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

    jmethodID publicStaticFunc_mid = env->GetStaticMethodID(TestJclass, "publicStaticFunc", "()V");

    env->CallStaticVoidMethod(TestJclass, publicStaticFunc_mid);


    //private static void privateStaticFunc()

    jmethodID privateStaticFunc_mid = env->GetStaticMethodID(TestJclass, "privateStaticFunc",
                                                             "()V");

    env->CallStaticVoidMethod(TestJclass, privateStaticFunc_mid);

/*    public static int publicStaticFunc_int(int a) {
        Log.i("kanxue", "i am from publicStaticFunc_int");
        return 100+a;
    }*/
    jmethodID publicStaticFunc_int_mid = env->GetStaticMethodID(TestJclass, "publicStaticFunc_int",
                                                                "(I)I");

    jint result_int = env->CallStaticIntMethod(TestJclass, publicStaticFunc_int_mid, 200);
    __android_log_print(4, "kanxue->jni", "publicStaticFunc_int->%d", result_int);
    /*public static String publicStaticFunc_string(String arg) {
        Log.i("kanxue", "i am from publicStaticFunc_int");
        return "publicStaticFunc_string->"+arg;
    }*/
    jmethodID publicStaticFunc_string_mid = env->GetStaticMethodID(TestJclass,
                                                                   "publicStaticFunc_string",
                                                                   "(Ljava/lang/String;)Ljava/lang/String;");

    jstring arg_string = env->NewStringUTF("i am from jni");
    jstring result_string = static_cast<jstring>(env->CallStaticObjectMethod(TestJclass,
                                                                             publicStaticFunc_string_mid,
                                                                             arg_string));

    const char *content = env->GetStringUTFChars(result_string, nullptr);
    __android_log_print(4, "kanxue->jni", "publicStaticFunc_string->%s", content);


}

访问对象函数

代码

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_callNonStaticFunc(
        JNIEnv *env,
        jobject obj/* this */) {

    jclass TestJclass = env->FindClass("com/kanxue/reflectiontest/Test");

    jmethodID con_mid = env->GetMethodID(TestJclass, "<init>", "(Ljava/lang/String;I)V");

    jstring arg0 = env->NewStringUTF("i am from callInit");

    jobject testobj = env->NewObject(TestJclass, con_mid, arg0, 100);
    
    jmethodID publicFunc_mid = env->GetMethodID(TestJclass, "publicFunc", "()V");
    env->CallVoidMethod(testobj, publicFunc_mid);
    
    
    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]);
    }


}

访问父类函数

参考代码


extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_onCreate(
        JNIEnv *env,
        jobject thiz, jobject bundle) {

/*    super.onCreate(savedInstanceState);
    Log.i("kanxue","onCreate is Called!");
    setContentView(R.layout.activity_main);
    TextView tv = findViewById(R.id.sample_text);
    Test testobj = (Test) callInit();
    Log.i("kanxue", testobj.flag);*/
    jclass AppCompatActivity_jclass1 = env->FindClass("androidx/appcompat/app/AppCompatActivity");

    jclass MainActivity_jclass1 = env->FindClass("com/kanxue/reflectiontest/MainActivity");

    jclass MainActivity_jclass2 = env->GetObjectClass(thiz);

    jclass AppCompatActivity_jclass2 = env->GetSuperclass(MainActivity_jclass2);

    jmethodID superClassOnCreate_mid = env->GetMethodID(AppCompatActivity_jclass2, "onCreate",
                                                        "(Landroid/os/Bundle;)V");

    env->CallNonvirtualVoidMethod(thiz, AppCompatActivity_jclass2, superClassOnCreate_mid, bundle);
    jstring arg1 = env->NewStringUTF("kanxue");
    jstring arg2 = env->NewStringUTF("native onCreate is called!");
    jclass Logjclass = env->FindClass("android/util/Log");
    jmethodID Log_i_mid = env->GetStaticMethodID(Logjclass, "i",
                                                 "(Ljava/lang/String;Ljava/lang/String;)I");

    jint result0 = env->CallStaticIntMethod(Logjclass, Log_i_mid, arg1, arg2);
    __android_log_print(4, "kanxue->jni",
                        "jint result0=env->CallStaticIntMethod(Logjclass,Log_i_mid,arg1,arg2);->%d",
                        result0);

}

如下代码是彻底实现了之前onCreate代码

extern "C" JNIEXPORT void JNICALL
Java_com_kanxue_reflectiontest_MainActivity_onCreate(
        JNIEnv *env,
        jobject thiz, jobject bundle) {

/*    super.onCreate(savedInstanceState);
    Log.i("kanxue","onCreate is Called!");
    setContentView(R.layout.activity_main);
    TextView tv = findViewById(R.id.sample_text);
    Test testobj = (Test) callInit();
    Log.i("kanxue", testobj.flag);*/


    //1.super.onCreate(savedInstanceState);
    //获取父类方法1.已知类名2。通过子类然后调用父类
    //获取父类全名
    jclass AppCompatActivity_jclass1 = env->FindClass("androidx/appcompat/app/AppCompatActivity");
    jclass MainActivity_jclass1 = env->FindClass("com/kanxue/reflectiontest/MainActivity");
    //根据现有类获取父类
    jclass MainActivity_jclass2 = env->GetObjectClass(thiz);
    jclass AppCompatActivity_jclass2 = env->GetSuperclass(MainActivity_jclass2);
    //获取函数
    jmethodID superClassOnCreate_mid = env->GetMethodID(AppCompatActivity_jclass2, "onCreate",
                                                        "(Landroid/os/Bundle;)V");
    //调用父类函数所以不能用一般的callXX,要用callnonvirtual
    env->CallNonvirtualVoidMethod(thiz, AppCompatActivity_jclass2, superClassOnCreate_mid, bundle);

    //2.Log.i("kanxue","onCreate is Called!");
    jstring arg1 = env->NewStringUTF("kanxue");
    jstring arg2 = env->NewStringUTF("native onCreate is called!");
    jclass Logjclass = env->FindClass("android/util/Log");
    jmethodID Log_i_mid = env->GetStaticMethodID(Logjclass, "i",
                                                 "(Ljava/lang/String;Ljava/lang/String;)I");

    jint result0 = env->CallStaticIntMethod(Logjclass, Log_i_mid, arg1, arg2);
    __android_log_print(4, "kanxue->jni",
                        "jint result0=env->CallStaticIntMethod(Logjclass,Log_i_mid,arg1,arg2);->%d",
                        result0);
    //3.setContentView(R.layout.activity_main);
    jmethodID setContentView_mid = env->GetMethodID(MainActivity_jclass2, "setContentView", "(I)V");
    jclass R_layoutjclass = env->FindClass("com/kanxue/reflectiontest/R$layout");
    jfieldID activity_main_fieldid = env->GetStaticFieldID(R_layoutjclass, "activity_main", "I");
    jint activity_main_value = env->GetStaticIntField(R_layoutjclass, activity_main_fieldid);

    env->CallVoidMethod(thiz, setContentView_mid, activity_main_value);
    jmethodID findViewById_mid = env->GetMethodID(MainActivity_jclass2, "findViewById",
                                                  "(I)Landroid/view/View;");
    //4. TextView tv = findViewById(R.id.sample_text);
    jclass R_idjclass = env->FindClass("com/kanxue/reflectiontest/R$id");
    jfieldID sample_text_fieldid = env->GetStaticFieldID(R_idjclass, "sample_text", "I");
    jint sample_text_value = env->GetStaticIntField(R_idjclass, sample_text_fieldid);
    jobject tvobj = env->CallObjectMethod(thiz, findViewById_mid, sample_text_value);

    //5. Test testobj = (Test) callInit();
    jmethodID callInit_mid = env->GetMethodID(MainActivity_jclass2, "callInit",
                                              "()Ljava/lang/Object;");
    jobject testobj = env->CallObjectMethod(thiz, callInit_mid);

    //6. Log.i("kanxue", testobj.flag);
    jclass testjclass=env->FindClass("com/kanxue/reflectiontest/Test");
    jfieldID flagjfield = env->GetFieldID(testjclass, "flag", "Ljava/lang/String;");
    jstring flagvalue = static_cast<jstring>(env->GetObjectField(testobj, flagjfield));

    jint result1 = env->CallStaticIntMethod(Logjclass, Log_i_mid, arg1, flagvalue);
    __android_log_print(4, "kanxue->jni",
                        " jint result1=env->CallStaticIntMethod(Logjclass,Log_i_mid,arg1,flagvalue);->%d",
                        result1);

   // env->DeleteGlobalRef(testjclass);

}

全局引用、局部引用、弱全局引用

在Java中内存管理:透明的,当新建类的新实例时,值需要在创建完这个类的实例后,拿着这个引用就访问它所有数据成员了(属性、方法),事实上对于Java,有一个垃圾回收器线程GC负责将不再使用的对象回收。 c/c++的内存管理:需要编码人员自己进行内存管理。

三种引用简介和区别

局部引用:
通过NewLocalRef和各种JNI接口创建(FindClass,NewObject,GetObjectClass和NewCharArray等)。会阻止GC回收所引用的对象。局部引用只能在当前函数中使用,函数返回后局部引用所引用的对象会被JVM自动释放,或调用DeleteLocalRef手动释放,故局部引用不能跨函数使用,不能跨线程使用。

全局引用:
调用NewGlobalRef,会阻止GC回收所引用的对象。可以跨函数线程,不会被ART自动释放,或调用DeleteGlobalRef手动释放,否则内存泄露。

#include <jni.h>
#include <string>
#include <android/log.h>
#include <pthread.h>
#include <thread>

JavaVM *globalVM = nullptr;
jclass testjclass = nullptr;

  jclass tmpjclass = env->FindClass("com/kanxue/reflectiontest/Test");
    testjclass = static_cast<jclass>(env->NewGlobalRef(tmpjclass));

弱全局引用:
调用NewWeakGlobalRef基于局部引用或全局引用创建,不会阻止GC回收所引用的对象,可以跨方法线程使用,但与全局引用很重要不同是,弱引用不会阻止GC回收它所引用的内存对象。但是引用也不会自动释放,再ART任务应该回收他的时候回收(比如内存紧张),或调用DeleteWeakGlobalRef手动释放。

JNI提供了一些列函数管理局部引用生命周期包括:
EnsureLocalCapacity
NewLocalRef
PushLocalFrame
PopLocalFrame
DeleteLocalRef

Dalvik下动态注册原理追踪

对于任意JNI函数,被调用前,必须完成java函数与so中地址的绑定,绑定国测可以是被动的,即由Dalvik、ART虚拟机再调用前查找并完成地址的绑定,也可以是主动的,由APP自己完成绑定。

静态注册

静态注册JNI函数,函数名导出,不安全。

动态注册




相关代码:

public class MainActivity extends AppCompatActivity {

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
        //System.load("/data/data/xxxx/test.so");
        Test testobj = new Test();
        getNonStaticField(testobj);
        //System.load("");
    }
NIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    globalVM = vm;
    __android_log_print(4, "kanxue->jni", "JNI_OnLoad(JavaVM *vm, void *reserved)->%p", vm);
    __android_log_print(4, "kanxue->jni", "jni->%s", "JNI_OnLoad is called");
    jint result = 0;
    //获取env
    JNIEnv *env = nullptr;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) == JNI_OK) {
        __android_log_print(4, "kanxue->jni", "jni->%s",
                            "vm->GetEnv((void**)&env,JNI_VERSION_1_6) success");
    }


    JNINativeMethod jniNativeMethod[] = {{"onCreate",          "(Landroid/os/Bundle;)V", (void *) onCreate},
                                         {"getNonStaticField", "(Ljava/lang/Object;)V",  (void *) cccc}
    };
    jclass MainActivityjclass = env->FindClass("com/kanxue/reflectiontest/MainActivity");

    env->RegisterNatives(MainActivityjclass, jniNativeMethod,
                         sizeof(jniNativeMethod) / sizeof(JNINativeMethod));




    result = JNI_VERSION_1_6;
    return result;


}

如下图已经看不见了

动态注册原理

static jint RegisterNatives(JNIEnv* env, jclass jclazz,
    const JNINativeMethod* methods, jint nMethods)
{
    ScopedJniThreadState ts(env);

    ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(ts.self(), jclazz);

    if (gDvm.verboseJni) {
        ALOGI("[Registering JNI native methods for class %s]",
            clazz->descriptor);
    }

    for (int i = 0; i < nMethods; i++) {
        if (!dvmRegisterJNIMethod(clazz, methods[i].name,
                methods[i].signature, methods[i].fnPtr))
        {
            return JNI_ERR;
        }
    }
    return JNI_OK;
    }

然后一层层调用如dvmResolveNativeMethod等。

在ART下。初始化类。
获取artmethod,artmethon指针,registerNative,调用共SetEntryPointFromJni(完成具体绑定工作),最后一个函数是SetNativePointer。
ART下获取函数名,PrettyMethod函数。

参考资料

看雪视频

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值