作者:MiniCode
Android调用C/C++的代码目前比较流行的方式之一便是通过JNI,其中按本地方法的实现有两种方式:静态和动态
创建一个C项目或者C的Module:
创建成功之后会生成如下文件(CMakeLists.txt、nativelib.cpp):
其中:
CMakeLists文件作用是将cpp(c++代码文件),打包成一个so库;
nativelib.cpp主要是编写C++代码的文件
1.静态注入方法的方式
新创建的好的项目或者module就是一个静态方式,会直接将c++代码编译成so库,并打包到apk文件中
本地方法声明:
本地方法调用:
本地方法注入so库:
build之后会生成so库:
静态库的方式编译:
2.动态注入的方式:
CMakeLists里面的配置:
cpp文件:
#include <jni.h>
#include <string>
#include <android/log.h>
#define LOG_TAG "XXX"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
static jint JNICALL cAdd(JNIEnv *env, jobject jobj, jint x, jint y){
LOGI("cAdd x is :%d y is :%d", x, y);
return x + y;
}
static jstring JNICALL cSayHi(JNIEnv *env, jobject jobj, jint x, jint y){
LOGI("cSayHi runs... will return a string in c");
return env->NewStringUTF("hello from cSayHi");
}
/**
第一个参数:javaAdd 是java中的方法名称
第二个参数:(II)I 是java中方法的签名,可以通过javap -s -p 类名.class 查看
第三个参数: (jstring *)cSayHi (返回值类型)映射到native的方法名称
*/
static const JNINativeMethod gMethods[] = {
{"javaAdd", "(II)I", (jint *)cAdd},
{"javaSayHi","()Ljava/lang/String;",(jstring *)cSayHi}
};
static jclass myClass;
// 这里是java调用C的存在Native方法的类路径
static const char* const className="com/example/jni/dynamic/JniOnloadTest";
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
LOGI("jni onload called");
JNIEnv* env = NULL; //注册时在JNIEnv中实现的,所以必须首先获取它
jint result = -1;
if(vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { //从JavaVM获取JNIEnv,一般使用1.4的版本
return -1;
}
// 获取映射的java类
myClass = env->FindClass(className);
if(myClass == NULL)
{
printf("cannot get class:%s\n", className);
return -1;
}
// 通过RegisterNatives方法动态注册
if(env->RegisterNatives(myClass, gMethods, sizeof(gMethods)/sizeof(gMethods[0])) < 0)
{
printf("register native method failed!\n");
return -1;
}
LOGI("jni onload called end...");
return JNI_VERSION_1_4; //这里很重要,必须返回版本,否则加载会失败。
}
注意上边相当于:通过JNI_OnLoad给com/example/jni/dynamic/JniOnloadTest类,动态实现了gMethods里面中的方法,只不过gMethods里面的方法需要按照固定的格式缓存数据,最后看下JniOnloadTest类:
package com.example.jni.dynamic;
public class JniOnloadTest {
public native int javaAdd(int x, int y);
public native String javaSayHi();
static {
System.loadLibrary("test-dynamic");
}
}
Log.e("TAG", "onCreate: " + JniOnloadTest().javaAdd(Random .nextInt(5),Random .nextInt(6)) )
小结:不管是静态还是动态注入本地方法的方式,在Android Studio编译工具中打出的包,都可以通过命令查看里面注入的方法,执行:nm -D xxx.so
动态注入的方式会有JNI_OnLoad方法
静态注入会有Java_开头的代码:
思考:还有没有其他方式可以直接调用so库中的代码呢?
Android 学习笔录
Android 性能优化篇:https://qr18.cn/FVlo89
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo
Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap