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