Just do it.

2 NDK开发

Posted on By qfzwy

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项目

image-20250123231641800

在src/main目录下 有一个cpp目录,与java目录同级,cpp目录下有一个 CMakeLists.txt文件和native-lib.cpp CMkeLists.txt就是CMake脚本文件 native-lib.cpp 是C++源文件

image-20250123231929126

1.2.2 设置调试模式

默认自动检测即可

image-20250123232155033

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打印

image-20250124212313129


有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文件提取

image-20250124213059988

add是按c++编译的,c++编译时对函数名有一个name mangling过程

image-20250124213147689

add1按c编译

image-20250124213340588

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来完成调用。

动态注册流程 :

  1. 声明 Java 层 Native 方法
  2. 准备数据 JNINativeMethod methods[] 数组
  3. 编写 JNI_OnLoad 方法
  4. 获取 JNIEnv 指针
  5. 获取 Java 类
  6. 进行动态注册

1.2.5.1 JNI_OnLoad

  1. JNI_Onload 方法在 Java 层执行 System.loadLibrary(“native-lib”) 代码时调用的方法 主要是执行一些 JNI 初始化操作

  2. JNI_Onload 常见操作 :

  • ① 保存 JavaVM 对象 , 使用全局变量记录该 Java 虚拟机对象
  • ② 动态注册 : 动态注册是该方法中最常见的操作
  1. JNI_Onload 参数说明 :
  • JavaVM *vm : 代表了 Java 虚拟机
  • void *r : 一般是 NULL
  1. 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;
}

image-20250126172338527

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

image-20250124214419366

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

image-20250126133826417

打印对应的内容

  1. 访问静态属性不需要传入对象
  2. 访问私有属性需要先设置其权限
  3. 访问非静态属性的内容需要传入实例对象
  4. 通过反射获取构造器再获取实例对象
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);
}

image-20250126135952720

通过反射进行调用

  1. 静态与非静态的区别
  2. 私有与公有的区别
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);

}

image-20250126141831101

在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的获取

  1. 在JNl onLoad中作为参数获得JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reversed)该函数由ART负责自动化查找和传入参数进行调用
  2. 通过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;
}

image-20250126155643312

在主进程中调用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);
}

image-20250126161830640

注意

JNIEnv是当前Java线程的执行环境,一个JVM对应一个JavaVM结构,而一个JM中可能创建多个Java线程,使用pthread create新建的线程当使用AttachCurrentThread(&env,NULL)获取到INIEnv后,该JNIEnv的ClassLoader并不是主线程的ClassLoader,因此也就无法加载app自己的Class