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的获取
- 在JNI_onLoad中作为参数获得,如下:
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm,void* reserved),该函数负责自动化查找和传入参数进行调用。 - 通过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属性只有静态非静态属性。
- 访问静态类属性
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); }*/
}
- 访问对象属性
// 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反射访问类属性的对比:
- java反射中获取类属性时只需要传入属性名即可,jni还需要传入属性签名信息
- java反射中在对private类型属性访问时要先取消安全检查,即调用setAccessible(true),然后才能够访问,而jni中则不需要。
- 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函数。
参考资料
看雪视频