1 基础知识
1.1 JNI/NDK
1.1.1 JNI
Java Native Interface,它允许Java代码和其他语言写的代码进行交互。JNI不是Android特有,windows、linux等凡是有JVM的地方都支持JNI。JNI是Java和Native世界的桥梁。而背后的一切都由Dalvik/ART虚拟机来驱动。
1.1.2 NDK
Native Development kit,NDK提供了工具链,帮助开发者快速开发以及调试C(或C++)的动态库。
1.1.3 为什么要在Android上使用C/C++代码:
1.不论是Java还是Kotlin,最终都是编译成字节码文件.dex文件,然而字节码文件是Java/Kotlin经过一定语义编译之后的产物,并不是完全可以运行的机器码指令;所以运行应用时还是需要进一步的编译成机器码才能够执行;
2.C/C++是完全的编译型语言,编译之后是机器码文件,运行应用时,可以直接执行的,效率比Java/Kotlin高。比较耗性能的操作,如音视频编解码,图像处理等操作,交由C/C++代码中去执行,能够大大提高应用的执行效率
1.2 NDK开发
1.2.1 新建Native项目
在src/main目录下 有一个cpp目录,与java目录同级,cpp目录下有一个 CMakeLists.txt文件和native-lib.cpp CMkeLists.txt就是CMake脚本文件 native-lib.cpp 是C++源文件
1.2.2 设置调试模式
默认自动检测即可
1.2.3 native-lib.cpp
android_studio提供的样例是静态注册的
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndk_1demo_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
JNIEXPORT
是 JNI 中用来声明本地方法的宏,它告诉编译器该函数是一个可以被 Java 调用的本地方法JNICALL
确保该方法符合 JNI 的调用约定
1.2.4 静态注册
在MainActivity.java
中声明两种JNI函数
public native void myFirstJNI();
public static native void staticJNI();
静态的JNI函数和非静态的第二个参数不一样
extern "C" JNIEXPORT void JNICALL
Java_com_example_ndk_1demo_MainActivity_myFirstJNI(JNIEnv *env, jobject thiz) {
// TODO: implement myFirstJNI()
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_ndk_1demo_MainActivity_staticJNI(JNIEnv *env, jclass clazz) {
// TODO: implement staticJNI()
}
非静态本地方法的第二个参数是对对象的引用,静态本地方法的第二个参数是对其Java类的引用。参考java类中的静态方法,是通过类进行调用,而不是实例对象。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
MainActivity.staticJNI();
this.myFirstJNI();
}
使用C编写一个JNI,extern C的作用是允许C++代码能够调用C语言代码,告诉编译器按照C语言的方式来编译被修饰的代码。
// test.h
#ifndef NDK_DEMO_TEST_H
#define NDK_DEMO_TEST_H
#endif //NDK_DEMO_TEST_H
#include <stdio.h>
int add_c(int a,int b);
实现函数
//test.c
#include "test.h"
int add_c(int a, int b){
return a+b;
}
在native-lib.cpp中引入时,需要添加extern C标识
#include <jni.h>
#include <string>
#include <android/log.h>
extern "C" {
#include "test.h"
};
最后注意要将其添加到CmakeLists.txt中的构建列表
add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
native-lib.cpp
test.c
)
locat打印
有extern “C”和没有该标识的区别
这里在native-lib.cpp中添加了代码,看一下
int add(int a, int b){
return a+b;
}
extern "C" int add1(int a,int b){
return a+b;
}
编译好后将so文件提取
add是按c++编译的,c++编译时对函数名有一个name mangling过程
add1按c编译
1.2.5 动态注册
[【Android NDK 开发】JNI 动态注册 ( 动态注册流程 | JNI_OnLoad 方法 | JNINativeMethod 结构体 | GetEnv | RegisterNatives )-腾讯云开发者社区-腾讯云](https://cloud.tencent.com/developer/article/2246768) |
静态注册 : 使用 Java_包名_类名_方法名(JNIEnv* env, jobject obj, …) 方式进行注册是静态注册 动态注册 : 将 C/C++ 中的本地方法 与 Java 中的方法对应起来 , 就需要使用动态注册
原理:
当执行Syetem.loadLibrary或System.load
public class MainActivity extends AppCompatActivity {
// Used to load the 'reflectiontest' library on application startup.
static {
System.loadLibrary("reflectiontest");
//System.load("so绝对路径")
}
当一个so被加载后,JNI_OnLoad就会被调用,查询so中的导出符号表,注册给JVM来完成调用。
动态注册流程 :
- 声明 Java 层 Native 方法
- 准备数据 JNINativeMethod methods[] 数组
- 编写 JNI_OnLoad 方法
- 获取 JNIEnv 指针
- 获取 Java 类
- 进行动态注册
1.2.5.1 JNI_OnLoad
-
JNI_Onload 方法在 Java 层执行 System.loadLibrary(“native-lib”) 代码时调用的方法 主要是执行一些 JNI 初始化操作
-
JNI_Onload 常见操作 :
- ① 保存 JavaVM 对象 , 使用全局变量记录该 Java 虚拟机对象
- ② 动态注册 : 动态注册是该方法中最常见的操作
- JNI_Onload 参数说明 :
- JavaVM *vm : 代表了 Java 虚拟机
- void *r : 一般是 NULL
- JNI_Onload 返回值说明 :
-
int 类型返回值代表了当前 NDK 使用的 JNI 版本
-
JNI 版本 中可选的有四个值 , 但是只能选择返回后三个 JNI_VERSION_1_2 , JNI_VERSION_1_4 , JNI_VERSION_1_6 。返回上述三个值任意一个没有区别 返回 JNI_VERSION_1_1 会报错
-
#define JNI_VERSION_1_1 0x00010001 #define JNI_VERSION_1_2 0x00010002 #define JNI_VERSION_1_4 0x00010004 #define JNI_VERSION_1_6 0x00010006 这四个值分别代表了 JDK 1.1 , 1.2 , 1.4 , 1.6 对应的 JNI 版本
1.2.5.2 案例
Java层实现
package com.example.dynamicregister;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import com.example.dynamicregister.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
// Used to load the 'dynamicregister' library on application startup.
static {
System.loadLibrary("dynamicregister");
}
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
this.dynamicRegisterJavaMethod();
this.dynamicRegisterJavaMethod1(10);
}
public native String stringFromJNI();
public native void dynamicRegisterJavaMethod();
public native int dynamicRegisterJavaMethod1(int i);
}
C++层实现
#include <jni.h>
#include <string>
#include "android/log.h"
//使用全局变量保存Java vm对象
JavaVM *_vm;
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_dynamicregister_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
// 动态注册对应的 C/C++ 本地方法
void dynamicRegisterCMethod(JNIEnv *env,jobject obj){
__android_log_print(ANDROID_LOG_INFO, "JNI_TAG", "dynamicRegisterCMethod");
}
jint dynamicRegisterCMethod1(JNIEnv *env, jobject obj, jint i){
__android_log_print(4, "JNI_TAG", "dynamicRegisterCMethod2 : %d", i);
return i + 1;
}
// 该数组中 , 每个元素都是一个 JNI 的 Native 方法
static const JNINativeMethod methods[] = {
{"dynamicRegisterJavaMethod", "()V", (void *)dynamicRegisterCMethod},
{"dynamicRegisterJavaMethod1", "(I)I", (void *)dynamicRegisterCMethod1}
};
/*
动态注册的 Java 类名称
注意 : 包名类名之间使用 "/" 分割
*/
static const char* className = "com/example/dynamicregister/MainActivity";
JNIEXPORT jint JNI_OnLoad(JavaVM *vm,void* reversed){
__android_log_print(4, "qfzwy-jni", "JNI_Onload");
//将 Java 虚拟机对象记录到全局变量中
_vm = vm;
//动态注册
//1.获取JNIEnv JNI 环境 , 需要从 JavaVM 获取
JNIEnv *env = nullptr;
if(vm->GetEnv((void **) &env, JNI_VERSION_1_6)!=JNI_OK){
return -1;
}
//2.获取要动态注册的 Java 类的 Class 对象
jclass jclazz = env->FindClass(className);
//3.注册
/*
jint RegisterNatives(
jclass clazz, //要注册的 Java 类
const JNINativeMethod* methods, //JNI注册方法数组
jint nMethods //要注册的 JNI 方法个数
)
* */
env->RegisterNatives(jclazz, methods, sizeof(methods) / sizeof(JNINativeMethod));
return JNI_VERSION_1_6;
}
1.2.6 数据类型
C++ 数据类型 | Java 数据类型 | JNI 数据类型名 |
---|---|---|
jint | int | “I” |
jboolean | boolean | “Z” |
jbyte | byte | “B” |
jchar | char | “C” |
jshort | short | “S” |
jlong | long | “J” |
jfloat | float | “F” |
jdouble | double | “D” |
jobject | Object | “Ljava/lang/Object;” |
jstring | String | “Ljava/lang/String;” |
jarray | Array | “[elementType” |
jobjectArray | Object[] | “[Ljava/lang/Object;” |
jbooleanArray | boolean[] | “[Z” |
jbyteArray | byte[] | “[B” |
jcharArray | char[] | “[C” |
jshortArray | short[] | “[S” |
jintArray | int[] | “[I” |
jlongArray | long[] | “[J” |
jfloatArray | float[] | “[F” |
jdoubleArray | double[] | “[D” |
1.2.6.1 类描述符
在java代码中的java.lang.String类的类描述符就是java/lang/string
jclass intArrCls = env->FindClass("java/lang/String")
jclass intArrCls = env->FindClass("Ljava/lang/String;)"
1.2.6.2 数组描述符
规则:[+其类型的域描述符
int[] [I
String[] [Ljava/lang/String;
1.2.6.3 函数描述符
规则:(参数的域描述符的叠加)
String test() Ljava/lang/String;
int f(int i,Object object) (ILjava/lang/Object;)I
void set(byte[] bytes) ([B)V
1.2.6.4 域描述符
Field Descriptor | Java Language Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
案例
JAVA
public native int intJNI(int a);
CPP
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_ndk_1demo_MainActivity_intJNI(JNIEnv *env, jobject thiz,jint a) {
int result = 0;
for(int i=0;i<a;i++){
result = result+i;
}
return result;
}
调用
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());
// Example of a call to a native method
TextView tv = binding.sampleText;
tv.setText(stringFromJNI());
MainActivity.staticJNI();
this.myFirstJNI();
int result = intJNI(10);
Log.i("qfzwy","result from jni:"+result);
}
2 Java反射
相关类
类名 | 用途 |
---|---|
Class类 | 代表类的实体,在运行的Java应用程序中表示类和接口 |
Field类 | 代表类的成员变量(成员变量也称为类的属性) |
Method类 | 代表类的方法 |
Constructor类 | 代表类的构造方法 |
Class类
获取类相关的方法
方法 | 用途 |
---|---|
getClassLoader() | 获取类的加载器 |
getClasses() | 返回一个数组,数组中包含该类中所有公共类和接口类的对象 |
getDeclaredClasses() | 返回一个数组,数组中包含类中所有类和接口类的对象 |
forName(String className) | 根据类名返回类的对象 |
getName() | 获取类的完整路径名称 |
newInstance() | 创建类的实例 |
getPackage() | 获取类的包 |
getSimpleName() | 获取类的名字 |
getSuperclass() | 获取当前类继承的父类的类名 |
getInterfaces() | 获取当前类实现的类或接口 |
asSubclass(Class clazz) | 把传递进来的类的对象转换成代表子类的对象 |
Cast | 把对象转换成代表类或接口类的对象 |
获取类中属性相关的方法
方法 | 用途 |
---|---|
getField(String name) | 获取某个公开的属性对象 |
getFields() | 获取所有公开的属性对象 |
getDeclaredField(String name) | 获取某个属性对象 |
getDeclaredFields() | 获取所有属性对象 |
获取类中注解相关的方法
方法 | 用途 |
---|---|
getAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的公开注解对象 |
getAnnotations() | 返回该类所有的公开注解对象 |
getDeclaredAnnotation(Class annotationClass) | 返回该类中与参数类型匹配的所有注解对象 |
getDeclaredAnnotations() | 返回该类所有的注解对象 |
获取类中构造器相关的方法
方法 | 用途 |
---|---|
getConstructor(Class<?>… parameterTypes) | 获取该类中与参数类型匹配的公开构造方法 |
getConstructors() | 获取该类的所有公开构造方法 |
getDeclaredConstructor(Class<?>… parameterTypes) | 获取该类中与参数类型匹配的构造方法 |
getDeclaredConstructors() | 获取该类所有构造方法 |
获取类中方法相关的方法
方法 | 用途 |
---|---|
getMethod(String name, Class<?>… parameterTypes) | 获取该类某个公开的方法 |
getMethods() | 获取该类所有公开的方法 |
getDeclaredMethod(String name, Class<?>… parameterTypes) | 获取该类某个方法 |
getDeclaredMethods() | 获取该类所有方法 |
其他
方法 | 用途 |
---|---|
isAnnotation() | 如果是注解类则返回true |
isAnnotationPresent(Class<? extends Annotation> annotationClass) | 如果是指定类型注解类则返回true |
isAnonymousClass() | 如果是匿名类则返回true |
isArray() | 如果是一个数组类则返回true |
isEnum() | 如果是枚举类则返回true |
isInstance(Object obj) | 如果obj是该类的实例则返回true |
isInterface() | 如果是接口类则返回true |
isLocalClass() | 如果是局部类则返回true |
isMemberClass() | 如果是内部类则返回true |
Field类
方法 | 用途 |
---|---|
equals(Object obj) | 属性与obj相等则返回true |
get(Object obj) | 获取obj中对应的属性值 |
set(Object obj, Object value) | 设置obj中对应属性值 |
Method类
方法 | 用途 |
---|---|
invoke(Object obj, Object… args) | 传递object对象及参数调用该对象对应的方法 |
Constructor类
方法 | 用途 |
---|---|
newInstance(Object… initargs) | 根据传递的参数创建类的对象 |
实现案例
新建一个class Test
package com.example.reflectiontest;
import android.util.Log;
public class Test {
public String flag = null;
public Test(){
flag = "Test()";
}
public Test(String arg) {
flag = "Test(String arg)" + arg;
}
public Test(String arg1, int arg2) {
flag = "Test(String arg1,int arg2)"+arg1+arg2;
}
public static String publicStaticField = "staticField";
public String publicField = "publicField";
private static String privateStaticField = "privatestaticField";
private String privateField = "privateField";
public static void publicStaticFunc(){
Log.i("qfzwy","publicStaticFunc");
}
public void publicFunc(){
Log.i("qfzwy","publicFunc");
}
private static void privateStaticFunc(){
Log.i("qfzwy","privateStaticFunc");
}
private void privateFunc(){
Log.i("qfzwy","privateFunc");
}
}
通过反射调用Java类
属性相关测试
两种方法获取类对象
public void testField(){
// loadClass
try {
Class testClazz = MainActivity.class.getClassLoader().loadClass("com.example.reflectiontest.Test");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// forName
try {
Class testClazz2 = Class.forName("com.example.reflectiontest.Test");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
获取属性
Class testClazz3 = Test.class;
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
Field publicStaticField_field1 = testClazz3.getField("privateStaticField");
Log.i("qfzwy1", String.valueOf(publicStaticField_field1)); //此处会产生报错,非Declared方法不能获取私有属性
分别使用两种方式获取所有属性
Class testClazz3 = Test.class;
Field[] publicFields = testClazz3.getFields();
for (Field publicField : publicFields) {
Log.i("qfzwy", "getFields->"+ publicField);
}
Field[] AllFields = testClazz3.getDeclaredFields();
for (Field field : AllFields) {
Log.i("qfzwy", "getAllFields->"+ field);
}
打印对应的内容
- 访问静态属性不需要传入对象
- 访问私有属性需要先设置其权限
- 访问非静态属性的内容需要传入实例对象
- 通过反射获取构造器再获取实例对象
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
//访问静态属性不需要传入对象
String obj1 = (String) publicStaticField_field.get(null);
Log.i("qfzwy", "StaticStr" + obj1);
//访问私有属性需要先设置其权限
Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
privateStaticField_field.setAccessible(true);
String obj2 = (String) privateStaticField_field.get(null);
Log.i("qfzwy", "privateStaticStr" + obj2);
//修改
privateStaticField_field.set(null,"modifyed");
String obj3 = (String) privateStaticField_field.get(null);
Log.i("qfzwy", "modifiedPrivateStaticStr" + obj3);
//访问非静态属性的内容需要传入实例对象
Field publicField_field = testClazz3.getDeclaredField("publicField");
//通过反射获取构造器再获取实例对象
Constructor<Test> constructor = testClazz3.getConstructor();
Test test = constructor.newInstance();
String obj4 = (String) publicField_field.get(test);
Log.i("qfzwy", "nonStaticField" + obj4);
属性相关测试完整代码
public void testField() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
// loadClass
try {
Class testClazz = MainActivity.class.getClassLoader().loadClass("com.example.reflectiontest.Test");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// forName
try {
Class testClazz2 = Class.forName("com.example.reflectiontest.Test");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Class testClazz3 = Test.class;
Field[] publicFields = testClazz3.getFields();
for (Field publicField : publicFields) {
Log.i("qfzwy", "getFields->"+ publicField);
}
Field[] AllFields = testClazz3.getDeclaredFields();
for (Field field : AllFields) {
Log.i("qfzwy", "getAllFields->"+ field);
}
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
//访问静态属性不需要传入对象
String obj1 = (String) publicStaticField_field.get(null);
Log.i("qfzwy", "StaticStr" + obj1);
//访问私有属性需要先设置其权限
Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
privateStaticField_field.setAccessible(true);
String obj2 = (String) privateStaticField_field.get(null);
Log.i("qfzwy", "privateStaticStr" + obj2);
//修改
privateStaticField_field.set(null,"modifyed");
String obj3 = (String) privateStaticField_field.get(null);
Log.i("qfzwy", "modifiedPrivateStaticStr" + obj3);
//访问非静态属性的内容需要传入实例对象
Field publicField_field = testClazz3.getDeclaredField("publicField");
//通过反射获取构造器再获取实例对象
Constructor<Test> constructor = testClazz3.getConstructor();
Test test = constructor.newInstance();
String obj4 = (String) publicField_field.get(test);
Log.i("qfzwy", "nonStaticField" + obj4);
}
public void testField() throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
// loadClass
try {
Class testClazz = MainActivity.class.getClassLoader().loadClass("com.example.reflectiontest.Test");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
// forName
try {
Class testClazz2 = Class.forName("com.example.reflectiontest.Test");
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
Class testClazz3 = Test.class;
Field[] publicFields = testClazz3.getFields();
for (Field publicField : publicFields) {
Log.i("qfzwy", "getFields->"+ publicField);
}
Field[] AllFields = testClazz3.getDeclaredFields();
for (Field field : AllFields) {
Log.i("qfzwy", "getAllFields->"+ field);
}
Field publicStaticField_field = testClazz3.getDeclaredField("publicStaticField");
//访问静态属性不需要传入对象
String obj1 = (String) publicStaticField_field.get(null);
Log.i("qfzwy", "StaticStr" + obj1);
//访问私有属性需要先设置其权限
Field privateStaticField_field = testClazz3.getDeclaredField("privateStaticField");
privateStaticField_field.setAccessible(true);
String obj2 = (String) privateStaticField_field.get(null);
Log.i("qfzwy", "privateStaticStr" + obj2);
//修改
privateStaticField_field.set(null,"modifyed");
String obj3 = (String) privateStaticField_field.get(null);
Log.i("qfzwy", "modifiedPrivateStaticStr" + obj3);
//访问非静态属性的内容需要传入实例对象
Field publicField_field = testClazz3.getDeclaredField("publicField");
//通过反射获取构造器再获取实例对象
Constructor<Test> constructor = testClazz3.getConstructor();
Test test = constructor.newInstance();
String obj4 = (String) publicField_field.get(test);
Log.i("qfzwy", "nonStaticField" + obj4);
}
方法相关测试
Declared作用和Field一样就不再做测试
获取所有方法
Class testClazz = Test.class;
Method[] methods = testClazz.getDeclaredMethods();
for (Method method : methods) {
Log.i("qfzwy", "getDeclaredMethods:"+method);
}
通过反射进行调用
- 静态与非静态的区别
- 私有与公有的区别
Method publicStaticFunc_method = testClazz.getDeclaredMethod("publicStaticFunc");
publicStaticFunc_method.invoke(null);
Method privateFunc_method = testClazz.getDeclaredMethod("privateFunc");
// 私有方法设置属性为true
privateFunc_method.setAccessible(true);
Constructor<Test> constructor = testClazz.getConstructor();
// 非静态方法获取实例进行调用
Test test = constructor.newInstance();
privateFunc_method.invoke(test);
完整代码
public void testMethod() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException {
Class testClazz = Test.class;
Method[] methods = testClazz.getDeclaredMethods();
for (Method method : methods) {
Log.i("qfzwy", "getDeclaredMethods:"+method);
}
Method publicStaticFunc_method = testClazz.getDeclaredMethod("publicStaticFunc");
publicStaticFunc_method.invoke(null);
Method privateFunc_method = testClazz.getDeclaredMethod("privateFunc");
// 私有方法设置属性为true
privateFunc_method.setAccessible(true);
Constructor<Test> constructor = testClazz.getConstructor();
// 非静态方法获取实例进行调用
Test test = constructor.newInstance();
privateFunc_method.invoke(test);
}
构造器相关测试
直接给出完整代码
public void testConstructor() throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchFieldException {
Class testClazz = MainActivity.class.getClassLoader().loadClass("com.example.reflectiontest.Test");
Constructor<Test>[] constructors = testClazz.getConstructors();
for (Constructor<Test> constructor : constructors) {
Log.i("qfzwy", "Constructor:" + constructor);
}
Constructor<Test> constructor1 = testClazz.getDeclaredConstructor();
Test test1 = constructor1.newInstance();
Field field1 = testClazz.getDeclaredField("flag");
String flag = (String) field1.get(test1);
Log.i("qfzwy", "constructor:" + flag);
Constructor<Test> constructor2 = testClazz.getDeclaredConstructor(String.class);
Test test2 = constructor2.newInstance("hello String");
Field field2 = testClazz.getDeclaredField("flag");
String flag2 = (String) field2.get(test2);
Log.i("qfzwy", "constructor:" + flag2);
Constructor<Test> constructor3 = testClazz.getDeclaredConstructor(String.class, int.class);
Test test3 = constructor3.newInstance("hello String and int", 666);
Field field3 = testClazz.getDeclaredField("flag");
String flag3 = (String) field2.get(test3);
Log.i("qfzwy", "constructor:" + flag3);
}
在C++中通过反射调用Java类
属性签名见1.2.6 数据类型
- GetStringUTFChars()函数可用于从给定的Java的jstring创建新的C字符串(char *),如果无法分配内存,则该函数返回NULL。 检查NULL是一个好习惯。在不使用GetStringUTFChars()返回的字符串时,需要来释放内存和引用以便可以对其进行垃圾回收,因此之后应始终调用ReleaseStringUTFChars()。
- NewStringUTF()函数使用给定的C字符串创建一个新的JNI字符串(jstring)
// publicStaticField
jclass TestJclass = env->FindClass("com/example/reflectiontest/Test");
jfieldID publicStaticField_jfieldID = env->GetStaticFieldID(TestJclass,
"publicStaticField",
"Ljava/lang/String;");
jstring publicStaticField_content =(jstring) env->GetStaticObjectField(TestJclass,publicStaticField_jfieldID);
// jstring转为c++字符串
const char *content_ptr = env->GetStringUTFChars(publicStaticField_content, nullptr);
// int __android_log_print(int prio, const char* tag, const char* fmt, ...)
__android_log_print(4, "qfzwy->jni", "jni->%s", content_ptr);
return env->NewStringUTF(hello.c_str());
3 JavaVM和JNIEnv
3.1 JavaVM
- JavaVM 是虚拟机在 JNI 层的代表,一个进程只有一个 JavaVM,所有的线程共用一个 JavaVM
- JNIInvokeInterface_结构封装和JVM相关的功能函数,如销毁JVM,获得当前线程的Java执行环境
JavaVM的获取
- 在JNl onLoad中作为参数获得
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reversed)
该函数由ART负责自动化查找和传入参数进行调用 - 通过JNlEnv的GetJavaVM函数获取,如下:JavaVM* thisjvm=nullptr;env->GetJavaVM(&thisjvm);
JavaVM *globalVM; #定义一个全局变量
#方式一
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reversed){
globalVM = vm;
}
#方式二
Java_com_example_reflectiontest_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
env->GetJavaVM(&globalVM);
3.2 JNIEnv
- 指Java Native Interface Environment,是一个JNI接口指针,指向了本地方法的一个函数表,该函数表中的每一个成员指向了一个JNI函数,本地方法通过JNI函数来访问JVM中的数据结构
- JNIEnv 表示 Java 调用 native 语言的环境,是一个封装了几乎全部 JNI 方法的指针。
- JNIEnv 只在创建它的线程生效,不能跨线程传递,不同线程的 JNIEnv 彼此独立。
- JNIEnv结构也是一个函数表(可以这么理解),在Native代码中通过JNIEnv的函数表来操作Java数据或调用Java方法。
创建子线程
void *threadtest(void *args){
for (int i = 0; i < 10; i++) {
__android_log_print(4, "qfzwy->jni", "jni->%s,%d", "i am from thread",i);
}
pthread_exit(0);
}
JNIEXPORT; jint JNI_OnLoad(JavaVM *vm, void *reversed){
globalVM = vm;
__android_log_print(4, "qfzwy->jni", "jni->%s", "JNI_OnLoad is called");
jint result = 0;
pthread_t thread;
pthread_create(&thread, nullptr,threadtest, nullptr);
pthread_join(thread, nullptr);
result = JNI_VERSION_1_6;
return result;
}
在主进程中调用env
//在主线程获取env
JNIEnv *env = nullptr;
if (vm->GetEnv((void **) &env, JNI_VERSION_1_6)==JNI_OK) {
__android_log_print(4, "qfzwy->jni", "jni->%s", "JNIEnv is get");
}
在子线程中调用env
void *threadtest(void *args){
JNIEnv *threadEnv = nullptr;
if (globalVM->AttachCurrentThread(&threadEnv, nullptr) == JNI_OK){
__android_log_print(4, "qfzwy->jni", "jni->%p", threadEnv);
jstring jstring1 = threadEnv->NewStringUTF("thread jstring");
const char *content = threadEnv->GetStringUTFChars(jstring1, nullptr);
__android_log_print(4, "qfzwy->jni", "jni->%s", content);
//释放
threadEnv->ReleaseStringUTFChars(jstring1, content);
} else{
__android_log_print(4, "qfzwy->jni", "jni->%s", "failed");
}
//退出线程
globalVM->DetachCurrentThread();
pthread_exit(0);
}
注意
JNIEnv是当前Java线程的执行环境,一个JVM对应一个JavaVM结构,而一个JM中可能创建多个Java线程,使用pthread create新建的线程当使用AttachCurrentThread(&env,NULL)获取到INIEnv后,该JNIEnv的ClassLoader并不是主线程的ClassLoader,因此也就无法加载app自己的Class