NDK-JNI

AndroidMManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" >

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.NdkDemo1"
        android:extractNativeLibs="true"    // NDK版本高于33 需要加上这个
        tools:targetApi="31" >
        <activity
            android:name=".MainActivity"
            android:exported="true" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

CMakeLists.txt

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.

# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)

# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("ndkdemo1")

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(${CMAKE_PROJECT_NAME} SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        # 当 native-lib.cpp main.cpp 中需要用到 hellofox.cpp的函数时,需要加进去
        hellofox.cpp native-lib.cpp main.cpp)

# 创建编译第二个so
add_library(ndkdemo2test SHARED
        # List C/C++ source files with relative paths to this CMakeLists.txt.
        hellofox.cpp)


# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.

target_link_libraries(${CMAKE_PROJECT_NAME}
        # List libraries link to the target library
        android
        log)

target_link_libraries(ndkdemo2test
        # List libraries link to the target library
        android
        log)

hellofox.cpp

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

#define TAG "Raptor"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);

void test(){
    LOGD("hellofox test");
}

main.cpp

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

#define TAG "Raptor"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);

void mainTest(){
    LOGD("main test");
}

native-lib.cpp

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

#define TAG "Raptor"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);

extern void test();

struct abc {
    std::string name;
    int age;
};


JavaVM *globalVM = nullptr;


// * 定义全局引用 测试子线程使用
jclass GlobalNDKDemoClazz;
jobject ClassLoaderObj;

void *threadFunc(void *a) {
//    int *num = static_cast<int *>(a);
//    for (int i = 0; i < *num; ++i) {
//        LOGD("threadFunc: %d", i)
//    }
//    pthread_exit(0);
    abc *ac = static_cast<abc *>(a);
    LOGD("threadFunc: %s", ac->name.c_str());
    LOGD("threadFunc: %d", ac->age);

    // * 子线程获取Env
    JNIEnv *env = nullptr;
    if (globalVM->AttachCurrentThread(reinterpret_cast<JNIEnv **>(&env), nullptr) != JNI_OK) {
        LOGD("threadFunc GetEnv failed");
    }
    LOGD("threadFunc JNIEnv: %p", env);

    // * 子线程获取 Java公有类  可以使用 FindClass 直接获取
    jclass ClassClazz = env->FindClass("java/lang/Class");
    LOGD("threadFunc ClassClazz: %p", ClassClazz);

    // * 子线程获取 Java自定义类   在主线程中获取类,使用全局引用传递到子线程中
    LOGD("threadFunc GlobalNDKDemoClazz: %p", GlobalNDKDemoClazz);

    // * 在jni的主线程中获取 ClassLoader, 在jni的子线程中 loadClass
    jclass ClassLoaderClazz = env->FindClass("java/lang/ClassLoader");
    jmethodID loadClassID = env->GetMethodID(ClassLoaderClazz, "loadClass",
                                             "(Ljava/lang/String;)Ljava/lang/Class;");
    jclass NDKDemoClazz = static_cast<jclass>(env->CallObjectMethod(ClassLoaderObj, loadClassID,
                                                                    env->NewStringUTF(
                                                                            "com.del123.ndkdemo1.NDKDemo")));
    LOGD("threadFunc ClassLoaderObj NDKDemoClazz: %p", NDKDemoClazz);


    pthread_exit((void *) "这是一段字符串常量返回值");

}

extern "C" JNIEXPORT jstring JNICALL
Java_com_del123_ndkdemo1_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";

    // * 主线程获取并赋值 Java自定义 NDKDemo 类
    jclass NDKDemoClazz9 = env->FindClass("com/del123/ndkdemo1/NDKDemo");
    GlobalNDKDemoClazz = static_cast<jclass>(env->NewGlobalRef(NDKDemoClazz9));

    // * 在jni的主线程中获取 ClassLoader, 在jni的子线程中 loadClass
    // Java中获取ClassLoader -> Demo.class.getClassLoader()
    // 类字节码 Demo 在这里 等同于 NDKDemoClazz9
    // getClassLoader 方法 是在 java/lang 中的Class类下面的
    jclass ClassClazz9 = env->FindClass("java/lang/Class");
    jmethodID getClassLoaderID = env->GetMethodID(ClassClazz9, "getClassLoader",
                                                  "()Ljava/lang/ClassLoader;");
    // * 类实例.getClassLoader() 返回该类的加载器, 哪个类调用 就返回哪个类的加载器
    jobject tempClassLoaderObj = env->CallObjectMethod(NDKDemoClazz9, getClassLoaderID);
    ClassLoaderObj = env->NewGlobalRef(tempClassLoaderObj);



    // * 多线程相关操作
    pthread_t thread;
//    pthread_create(&thread, nullptr, threadFunc, nullptr);


//    // 传int参数
//    int num = 5;
//    pthread_create(&thread, nullptr, threadFunc, &num);
//    pthread_join(thread, nullptr);

    // * 传结构体参数
    abc p;
    p.name = "Hello,XXX";
    p.age = 20;
    pthread_create(&thread, nullptr, threadFunc, &p);
//    pthread_join(thread, nullptr);


    // * 函数取返回值,
    // 在C语言函数中,不能将函数的局部变量地址作为函数返回值,
    // 因为在函数执行完毕后,变量会被回收,变量指针会变成野指针
    void *resPtr;
    pthread_join(thread, &resPtr);
    LOGD("threadFunc 返回值: %s", (char *) resPtr)

    // * 打印 JNIEnv
    LOGD("stringFromJNI JNIEnv: %p", env);

    // * 日志
    __android_log_print(3, TAG, "cStr: %s", hello.c_str());

    LOGD("cStr: %s", hello.c_str());
    return env->NewStringUTF(hello.c_str());
}

// * C层函数
jstring encodeFromC(JNIEnv *env, jobject obj, jint a, jbyteArray b, jstring c) {
    const char *soPath = env->GetStringUTFChars(c, nullptr);
    // RTLD_NOW 加载以后直接初始化
    void *soinfo = dlopen(soPath, RTLD_NOW);
    // void (*)(); 函数指针的类型
    // def 函数指针的名字
    void (*def)() = nullptr;
    // _Z4testv 传入的是C++符号修饰符,而不是hellofox.cpp中的 test
    // 将so文件拖入ida64 查看test函数的修饰符
    // reinterpret_cast<指针类型>() 将 dlsym 函数返回的指针转换为void * 类型的函数指针
    def = reinterpret_cast<void (*)()>(dlsym(soinfo, "_Z4testv"));
    def();
    test();
    return env->NewStringUTF("encodeFromC");
}


// * 定义 全局引用变量
jclass NDKDemoClazzTest;
// * 定义 弱全局引用变量
jclass NDKDemoClazzTest2;

// * so加载完毕最后一步执行的是 JNI_OnLoad,他在MainActivity.java加载静态资源时加载
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    // 将 vm 赋值给 globalVM
    globalVM = vm;

    JNIEnv *env = nullptr;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        LOGD("GetEnv failed");
        return -1;
    }


    // * jni函数的动态注册
    // 通过 JNI 查找并返回 Java 类 com.del123.ndkdemo1.MainActivity 的引用
    jclass MainActivityClazz = env->FindClass("com/del123/ndkdemo1/MainActivity");

    // * 赋值 全局引用变量
    NDKDemoClazzTest = static_cast<jclass>(env->NewGlobalRef(MainActivityClazz));
    // * 赋值 弱全局引用变量, 弱全局引用并不需要手动释放,而是在 Java 对象被回收时自动失效
    NDKDemoClazzTest2 = static_cast<jclass>(env->NewWeakGlobalRef(MainActivityClazz));


    // 定义本地方法数组
    // typedef struct {
    //    const char* name;
    //    const char* signature;
    //    void*       fnPtr;    // 函数指针
    // } JNINativeMethod;

    JNINativeMethod methods[] = {
            // 声明一个本地方法
            // 原生方法的声明:public native String encode(int i, String str, byte[] byt);
            // {"Java方法名", "方法签名", "本地方法指针"}
            // 签名 () 中代表参数, I int类型、[B byte数组、Ljava/lang/String; Java的String对象
            // Ljava/lang/String; 代表返回值
            {"stringFromJNI2", "(I[BLjava/lang/String;)Ljava/lang/String;", (void *) encodeFromC}
    };

    // * 注册本地方法
    // jint RegisterNatives(jclass clazz, const JNINativeMethod* methods, jint nMethods)
    // 参数:1,Java 类的引用 2,包含要注册的本地方法的名称、签名和实现函数的指针。3,告诉 JNI 有多少个本地方法需要注册。
    env->RegisterNatives(MainActivityClazz, methods, sizeof(methods) / sizeof(JNINativeMethod));


    // * JavaVM 每个进程只有一份,两种形式输出地址查看
    LOGD("JNI_OnLoad JavaVM: %p", vm);
    JavaVM *vm1;
    env->GetJavaVM(&vm1);
    LOGD("JNI_OnLoad JavaVM1: %p", vm1);
    LOGD("JNI_OnLoad GetEnv: %p", env);

    return JNI_VERSION_1_6;
}

// * initarray 中可以存放多个函数,也有执行先后顺序
// * initarray 的执行顺序在 JNI_OnLoad 之前,_init 之后
// * 多个 initarray 的执行顺序,数字越小 优先级越高, 没有定义数字 最后执行

// * visibility("hidden") 隐藏函数名
__attribute__((constructor, visibility("hidden"))) void initArrayTest5() {
    LOGD("initarray Test5 >>>")
}


__attribute__((constructor(200))) void initArrayTest1() {
    LOGD("initarray Test1 >>>")
}

__attribute__((constructor(190))) void initArrayTest3() {
    LOGD("initarray Test3 >>>")
}

__attribute__((constructor)) void initArrayTest2() {
    LOGD("initarray Test2 >>>")
}



// * init构造函数 函数名必须是 _init
// * 他的执行顺序高于 JNI_OnLoad
// * 编译后的名字EXPORT   .init_proc
extern "C" void _init() {
    LOGD("_init >>>")
}




extern "C"
JNIEXPORT jstring JNICALL
Java_com_del123_ndkdemo1_MainActivity_newJavaObjJNI(JNIEnv *env, jobject thiz) {
    // TODO: implement newJavaObjJNI()

    // * 使用 全局引用变量
    LOGD("全局引用变量NDKDemoClazzTest: %p", NDKDemoClazzTest);
    // * 使用 全局引用变量
    LOGD("弱全局引用变量NDKDemoClazzTest2: %p", NDKDemoClazzTest2);

    // * NewObject 创建对象
//    // 在 JNI 中查找 Java 类。注意包名中的斜杠要用 '/' 而不是 '.'
//    jclass NDKDemoClazz = env->FindClass("com/del123/ndkdemo1/NDKDemo");
//
//    // 获取 Java 类的构造方法 ID,方法签名为 ()V,表示无参数构造函数
//    jmethodID init_MethodID = env->GetMethodID(NDKDemoClazz, "<init>", "()V");
//
//    // 创建 Java 类的新对象,调用无参数构造函数
//    jobject ndkObj = env->NewObject(NDKDemoClazz, init_MethodID);
//
//    // 打印新创建的对象指针地址
//    LOGD("ndkObj %p", ndkObj);


    // * `AllocObject 创建对象`
    jclass NDKDemoClazz = env->FindClass("com/del123/ndkdemo1/NDKDemo");
    jmethodID init_MethodID = env->GetMethodID(NDKDemoClazz, "<init>", "(Ljava/lang/String;I)V");
    jobject ndkObj = env->AllocObject(NDKDemoClazz);
    LOGD("ndkObj %p", ndkObj);      // 这一步只是分配了一个空间
    jstring rap = env->NewStringUTF("rapt");
    env->CallNonvirtualVoidMethod(ndkObj, NDKDemoClazz, init_MethodID, rap, 100);
    LOGD("ndkObj %p", ndkObj);      // 经过上面两步到现在才是初始化


    // * Ljava/lang/String;  表示一个 java.lang.String 对象。
    // * "(Ljava/lang/String;I)V" 表示一个方法,该方法接受两个参数(一个 java.lang.String 对象和一个 int),并且返回 void



    // * 通过jni 获取java类的私有静态字段
    // 获取 Java 类 `NDKDemo` 的私有静态字段 `privateStaticStringField` 的 Field ID
    jfieldID privateStaticStringFieldID = env->GetStaticFieldID(NDKDemoClazz,
                                                                "privateStaticStringField",
                                                                "Ljava/lang/String;");

    // * 设置静态字段的值
    env->SetStaticObjectField(NDKDemoClazz, privateStaticStringFieldID,
                              env->NewStringUTF("privateStaticStringField_putValue..."));

    // 通过 Field ID 获取该静态字段的值,结果是一个 `jstring`
    jstring jstr = static_cast<jstring>(env->GetStaticObjectField(NDKDemoClazz,
                                                                  privateStaticStringFieldID));
    // 打印获取到的 `jstring` 对象的指针
    LOGD("jstrStaticField: %p", jstr);

    // 将 `jstring` 转换为 C 风格的字符串
    const char *cstr = env->GetStringUTFChars(jstr, nullptr);
    LOGD("cstrStatic: %s", cstr);
    env->ReleaseStringUTFChars(jstr, cstr);

    // * 通过jni 获取java类的对象字段ID
    jfieldID privateStringFieldID = env->GetFieldID(NDKDemoClazz, "privateStringField",
                                                    "Ljava/lang/String;");

    // * 设置对象字段的值
    env->SetObjectField(ndkObj, privateStringFieldID,
                        env->NewStringUTF("privateStringField_putValue..."));

    // * 读取java类的对象字段
    jstring jstr2 = static_cast<jstring>(env->GetObjectField(ndkObj, privateStringFieldID));
    LOGD("jstrField: %p", jstr2);
    const char *cstr2 = env->GetStringUTFChars(jstr2, nullptr);
    LOGD("cstr: %s", cstr2);
    env->ReleaseStringUTFChars(jstr2, cstr2);



    // * 通过jni 访问Java数组
    // 获取 Java 类 `NDKDemo` 的 `byteArray` 字段的 Field ID
    jfieldID byteArrayID = env->GetFieldID(NDKDemoClazz, "byteArray", "[B");

    // 从 Java 对象 `ndkObj` 中获取 `byteArray` 字段的值,这里返回的是一个 `jbyteArray` 对象
    jbyteArray originalByteArray = static_cast<_jbyteArray *>(env->GetObjectField(ndkObj,
                                                                                  byteArrayID));

    // 获取 `originalByteArray` 的长度
    jsize originalByteArrayLength = env->GetArrayLength(originalByteArray);

    // * 修改数组
    // 定义并初始化一个 C 风格的字节数组
    jbyte arr1[] = {10, 20, 30, 40};
    jsize newLength = sizeof(arr1) / sizeof(jbyte);

    // 创建一个新的 Java 字节数组,其长度为新数组的长度
    jbyteArray newByteArray = env->NewByteArray(newLength);

    // 将 C 风格的字节数组的内容复制到新的 Java 字节数组中
    env->SetByteArrayRegion(newByteArray, 0, newLength, arr1);

    // 设置 Java 对象 `ndkObj` 的 `byteArray` 字段为新的数组
    env->SetObjectField(ndkObj, byteArrayID, newByteArray);

    // * 获取 `jbyteArray` 中的元素,并将其转换为 C 风格的字节数组
    jbyte *cByteArray = env->GetByteArrayElements(newByteArray, nullptr);
    for (int i = 0; i < newLength; ++i) {
        LOGD("jbyteArray[%d]=%d", i, cByteArray[i]);
    }

    // 释放获取到的字节数组
    env->ReleaseByteArrayElements(newByteArray, cByteArray, 0);




    // * 通过jni 访问Java方法
    // * 调用静态函数     CallStaticVoidMethod
    jclass ReflectDemoClazz = env->FindClass("com/del123/ndkdemo1/NDKDemo");
    jmethodID publicStaticFuncID = env->GetStaticMethodID(ReflectDemoClazz, "publicStaticFunc",
                                                          "()V");
    env->CallStaticVoidMethod(ReflectDemoClazz, publicStaticFuncID);

    // * 调用对象函数     CallVoidMethod
    jmethodID publicFuncID = env->GetMethodID(ReflectDemoClazz, "publicFunc", "()V");
    env->CallVoidMethod(ndkObj, publicFuncID);

    // * 调用对象函数 传参 String返回值 CallObjectMethod
    jmethodID privateFuncID = env->GetMethodID(ReflectDemoClazz, "privateFunc",
                                               "(Ljava/lang/String;I)Ljava/lang/String;");
    jstring jstr3 = static_cast<jstring>(env->CallObjectMethod(ndkObj, privateFuncID,
                                                               env->NewStringUTF(
                                                                       "This is a static pass"),
                                                               100));
    const char *cstr3 = env->GetStringUTFChars(jstr3, nullptr);
    LOGD("cstr3: %s", cstr3);
    env->ReleaseStringUTFChars(jstr3, cstr3);

    // * CallVoidMethod、 CallVoidMethodV、 CallVoidMethodA
//    void CallVoidMethod(jobject obj, jmethodID methodID, ...)
    // ...  接收多个参数

//    void CallVoidMethodV(jobject obj, jmethodID methodID, va_list args)
    // va_list args  需要整合参数为一个参数

//    void CallVoidMethodA(jobject obj, jmethodID methodID, const jvalue* args)
    // const jvalue* args

    // * CallObjectMethodA 用法
    // 设置参数
    jint i = 42;
    jstring str = env->NewStringUTF("Hello JNI");

    jvalue args[2];
    // 如果你的函数接受的参数是整数类型,那么就使用 jvalue 结构体的 .i 属性;
    // 如果是长整型或者指针类型,就使用 .l 属性。
    args[0].l = str;
    args[1].i = i;

    // 调用方法
    jstring jstr4 = static_cast<jstring>(env->CallObjectMethodA(ndkObj, privateFuncID, args));

    const char *cstr4 = env->GetStringUTFChars(jstr4, nullptr);

    LOGD("cstr4: %s", cstr4);
    env->ReleaseStringUTFChars(jstr4, cstr4);



    // * jni调用 java函数  参数: String[]  返回值: int[]
    jmethodID privateStaticFuncID = env->GetStaticMethodID(ReflectDemoClazz, "privateStaticFunc",
                                                           "([Ljava/lang/String;)[I");

    jclass stringClass = env->FindClass("java/lang/String");
    jobjectArray strArray = env->NewObjectArray(3, stringClass, nullptr);
    env->SetObjectArrayElement(strArray, 0, env->NewStringUTF("Hello"));
    env->SetObjectArrayElement(strArray, 1, env->NewStringUTF(" "));
    env->SetObjectArrayElement(strArray, 2, env->NewStringUTF("World"));
    // 调用 Java 方法
    jintArray result = static_cast<jintArray>(env->CallStaticObjectMethod(ReflectDemoClazz,
                                                                          privateStaticFuncID,
                                                                          strArray));

    jsize len = env->GetArrayLength(result);
    jint *arr2 = env->GetIntArrayElements(result, nullptr);
    for (int i = 0; i < len; ++i) {
        LOGD("privateStaticFunc->result[%d]: %d", i, arr2[i]);
    }
    env->ReleaseIntArrayElements(result, arr2, JNI_ABORT);


    // * jni 访问 java父类方法





    // * 将 C 字符串转换为 Java 字符串并返回
    return env->NewStringUTF("");
}


// * 通过jni调用 java父类方法 CallNonvirtualVoidMethod

//extern "C"
//JNIEXPORT void JNICALL
//Java_com_del123_ndkdemo1_MainActivity_onCreate(JNIEnv *env, jobject thiz,
//                                               jobject saved_instance_state) {
//    // TODO: implement onCreate()
//    // * so层实现:   super.onCreate(savedInstanceState);
//    jclass FragmentActivityClazz = env->FindClass("androidx/fragment/app/FragmentActivity");
//    jmethodID onCreateID = env->GetMethodID(FragmentActivityClazz, "onCreate",
//                                            "(Landroid/os/Bundle;)V");
//    env->CallNonvirtualVoidMethod(thiz, FragmentActivityClazz, onCreateID, saved_instance_state);
//
//
//
//    // * so层实现: binding = ActivityMainBinding.inflate(getLayoutInflater());
//
//    // -> getLayoutInflater()
//
//    // 获取 getLayoutInflater 所在的类
//    jclass ActivityClazz = env->FindClass("android/app/Activity");
//
//    // 获取 getLayoutInflater 方法 ID
//    jmethodID getLayoutInflaterID = env->GetMethodID(ActivityClazz, "getLayoutInflater",
//                                                     "()Landroid/view/LayoutInflater;");
//
//    // 调用 getLayoutInflater 方法  传入 调用的类 和 该方法ID
//    jobject LayoutInflater = env->CallObjectMethod(thiz, getLayoutInflaterID);
//
//
//    // -> ActivityMainBinding
//    //  com.del123.ndkdemo1.databinding
//    //  public final class com.del123.ndkdemo1.databinding.ActivityMainBinding
//    //  extends androidx.viewbinding.ViewBinding
//
//    // -> inflate()
//    //  com.del123.ndkdemo1.databinding.ActivityMainBinding
//    //  public static @NonNull com.del123.ndkdemo1.databinding.ActivityMainBinding inflate(
//    //      @NonNull android.view.LayoutInflater inflater
//    //  )
//
//    // ActivityMainBinding 是编译后产生和界面绑定的类 当前不存在
//    jclass ActivityMainBindingClazz = env->FindClass(
//            "com/del123/ndkdemo1/databinding/ActivityMainBinding");
//
//    // 传入参数: 类的字节码, 方法名, 方法签名JNI中以 / 分割,Java以 . 分割  签名格式: (L参数类型;)L返回值类型;
//    jmethodID inflateID = env->GetMethodID(ActivityMainBindingClazz, "inflater",
//                                           "(Landroid/view/LayoutInflater;)Lcom/del123/ndkdemo1/databinding/ActivityMainBinding;");
//
//    LOGD("ActivityMainBindingClazz: %p", ActivityMainBindingClazz);
//    LOGD("inflateID: %p", inflateID);
//
//    // 调用 静态方法 返回值是类对象 使用 CallStaticObjectMethod
//    // 传入参数: 调用的类,方法ID,方法参数
//    // 返回值 ActivityMainBinding  类的返回值使用 jobject 代替就完事了
//    jobject ActivityMainBindingObj = env->CallStaticObjectMethod(ActivityMainBindingClazz,
//                                                                 inflateID, LayoutInflater);
//
//
//
//    // * so层实现: setContentView(binding.getRoot());
//
//    // -> getRoot()
//    //  com.del123.ndkdemo1.databinding.ActivityMainBinding
//    //  public androidx.constraintlayout.widget.ConstraintLayout getRoot()
//    //  Specified by:
//    //  getRoot in interface ViewBinding
//    jmethodID getRootID = env->GetMethodID(ActivityMainBindingClazz, "getRoot",
//                                           "()Landroidx/constraintlayout/widget/ConstraintLayout;");
//
//    // 调用 getRoot 方法  非静态方法使用 CallObjectMethod
//    jobject ConstraintLayout = env->CallObjectMethod(ActivityMainBindingObj, getRootID);
//
//    // -> setContentView()
//    //  androidx.appcompat.app.AppCompatActivity
//    //  public void setContentView(
//    //      android.view.View view
//    //  )
//    jclass AppCompatActivityClazz = env->FindClass("androidx/appcompat/app/AppCompatActivity");
//    jmethodID setContentViewID = env->GetMethodID(AppCompatActivityClazz, "setContentView",
//                                                  "(Landroid/view/View;)V");
//    // 调用 setContentView 方法 非静态 返回值 void  使用 CallVoidMethod
//    env->CallVoidMethod(thiz, setContentViewID, ConstraintLayout);
//
//
//    // * 实现 TextView tv = binding.sampleText;
//    // -> sampleText
//    //  com.del123.ndkdemo1.databinding.ActivityMainBinding
//    //  @NonNull
//    //  public final android.widget.TextView sampleText
//
//    // 获取 sampleText 字段的 ID
//    jfieldID sampleTextFieldID = env->GetFieldID(ActivityMainBindingClazz, "sampleText",
//                                                 "Landroid/widget/TextView;");
//
//    // 获取 sampleText 字段
//    jobject sampleTextObject = env->GetObjectField(ActivityMainBindingObj, sampleTextFieldID);
//    LOGD("sampleText: %p", sampleTextObject);
//
//
//    // * tv.setText(stringFromJNI());
//    // -> stringFromJNI
//    //  com.del123.ndkdemo1.MainActivity
//    //  public String stringFromJNI()
//
//    jclass MainActivityClazz = env->FindClass("com/del123/ndkdemo1/MainActivity");
//    jmethodID stringFromJNIID=env->GetMethodID(MainActivityClazz,"stringFromJNI", "()Ljava/lang/String;");
//    jstring stringFromJNIResult = static_cast<jstring>(env->CallObjectMethod(thiz,
//                                                                             stringFromJNIID));
//
//    // -> setText
//    //  android.widget.TextView
//    //  public final void setText( CharSequence text )
//    jclass TextViewClazz = env->FindClass("android/widget/TextView");
//    jmethodID setTextID=env->GetMethodID(TextViewClazz,"setText", "(Ljava/lang/CharSequence;)V");
//    env->CallVoidMethod(sampleTextObject,setTextID,stringFromJNIResult);
//
//}


Java层

MainActivity

package com.del123.ndkdemo1;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

import com.del123.ndkdemo1.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

//    // 将 onCreate 方法 在so层实现 native 化
//    protected native void onCreate(Bundle savedInstanceState);

    @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());
        // so路径的动态获取 + so包名
        String soPath = new Utils().getPath(getApplicationContext()) + "/libndkdemo2test.so";

        Log.d("Raptor Java stringFromJNI2: ", stringFromJNI2(10, new byte[]{1, 2}, soPath));

        newJavaObjJNI();
    }

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

    public native String newJavaObjJNI();

    public native String stringFromJNI2(int a, byte[] b, String c);

    // Used to load the 'ndkdemo1' library on application startup.
    // * 静态注册,要加载的so
    static {
        System.loadLibrary("ndkdemo1");
        System.loadLibrary("ndkdemo2test");
    }
}

NDKDemo

package com.del123.ndkdemo1;

import android.util.Log;

public class NDKDemo {
//    static {
//        ClassLoader classLoader = NDKDemo.class.getClassLoader().loadClass("com.del123.ndkdemo1.NDKDemo");
//    }
    public static String publicStaticStringField = "this is publicStaticStringField";
    public String publicStringField = "this is publicStringField";

    private static String privateStaticStringField = "this is privateStaticStringField";
    private String privateStringField = "this is privateStringField";
    private byte[] byteArray = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

    public NDKDemo() {
        Log.d("Raptor", "this is ReflectDemo()");
    }

    public NDKDemo(String str) {
        Log.d("Raptor", "this is ReflectDemo(String str)");
    }

    public NDKDemo(String str, int i) {
        Log.d("Raptor", i + " " + str);
        Log.d("Raptor", "this is ReflectDemo(String str, int i)");
    }

    public static void publicStaticFunc() {
        Log.d("Raptor", "this is publicStaticFunc");
    }

    public void publicFunc() {
        Log.d("Raptor", "this is publicFunc");
    }

    private static int[] privateStaticFunc(String[] str) {
        StringBuilder retval = new StringBuilder();
        for (String i : str) {
            retval.append(i);
        }
        Log.d("Raptor", "this is privateStaticFunc: " + retval.toString());
        return new int[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    }

    private String privateFunc(String str, int i) {
        Log.d("Raptor", i + " this is privateFunc: " + str);
        return "this is from java";
    }

}

Utils

package com.del123.ndkdemo1;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.util.Log;

import java.util.List;

public class Utils {
    public String getPath(Context cxt) {
        PackageManager pm = cxt.getPackageManager();
        List<PackageInfo> pkgList = pm.getInstalledPackages(0);
        if (pkgList == null || pkgList.size() == 0) {
            return null;
        }
        for (PackageInfo pi : pkgList) {
            if (pi.applicationInfo.nativeLibraryDir.startsWith("/data/app/")
                    && pi.packageName.startsWith("com.del123.ndkdemo1")) {
                 Log.e("Raptor so路径的动态获取:", pi.applicationInfo.nativeLibraryDir);
                return pi.applicationInfo.nativeLibraryDir;
            }
        }
        return null;

    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值