一、jni原理
主要就是通过数据类型签名和反射来实现java与c/c++方法进行交互的
数据类型签名对应表
java c/c++ boolean Z byte B char C short S int I long L float F double D void V object L开头,然后以/分割包的完整类型,后面再加; 比如String的签名就是Ljava/long/String Array 以[开头,在加上数组元素类型的签名, 比如int[],签名是[I,int[][]的签名是[[I, object[]的签名是[Ljava/lang/Object
数据类型对应表
java c/c++ boolean jboolean byte jbyte char jchar short jshort int jint long jlong float jfloat double jdouble void void
引用类型对应表
java c/c++ string jstring object jobject class jclass byte[] jbyteArray
二、实现jni
增加这三处
ndk{
// 设置支持的架构
abiFilters 'armeabi-v7a', 'arm64-v8a'
}
externalNativeBuild {
cmake {
path file('src/main/cpp/CMakeLists.txt')
version '3.18.1'
}
}
CMakLists.txt
# 声明并命名项目。
cmake_minimum_required(VERSION 3.18.1)
# 声明并命名项目。
project("myc")
# 创建并命名一个库,设置其类型为静态或共享,并提供其源代码的相对路径。
# 您可以定义多个库,CMake会为您构建它们。
# Gradle会自动将共享库与您的APK一起打包。
add_library( # 设置库的名称。
myc
# 设置库为共享库。
SHARED
# 提供源文件的相对路径。
native-lib.cpp )
# 在NDK中查找指定预构建的库,并将路径存储在变量中。
# 因为CMake默认将系统库添加到搜索路径中,所以您只需要指定公共NDK库的名称。
# CMake验证库是否存在,并在构建完成后完成其构建。
find_library( # 设置路径变量的名称。
log-lib
# 指定要CMake定位的NDK库名称。
log )
# 指定目标库应链接的库。您可以链接多个库,例如在此构建脚本中定义的库、预构建的第三方库或系统库。
target_link_libraries( # 指定目标库。
myc
# 将目标库与log库(在NDK中)链接。
\${log-lib} )
native-lib.cpp里面就可以写C/C++代码了
#include <jni.h>
#include <string>
/**
* jni静态注册native方法
*/
//extern "C" {
// JNIEXPORT jstring JNICALL Java_com_example_myc_MainActivity_stringFromJNI(JNIEnv *env, jobject /* this */) {
// std::string hello = "Hello from C++";
// return env->NewStringUTF(hello.c_str());
// }
//
// JNIEXPORT jint JNICALL Java_com_example_myc_MainActivity_intFromJNI(JNIEnv *env, jobject /* this */, jint a, jint b) {
// return a + b;
// }
//}
/**
* jni动态注册native方法
*/
extern "C" {
JNIEXPORT jstring JNICALL cpp_stringFromJNI(JNIEnv *env, jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
JNIEXPORT jint JNICALL cpp_intFromJNI(JNIEnv *env, jobject obj/* this */, jint a, jint b) {
/**
* c++ 调用java方法
*/
jobject m_object = env->NewGlobalRef(obj);//创建对象的本地变量
jclass myClass = env->FindClass("com/example/myc/MainActivity");//找到类文件
jmethodID myMethod = env->GetMethodID(myClass, "myMethod", "(Ljava/lang/String;)V");//在类文件下找到方法,第三个参数是方法的签名也就是方法的参数类型和返回类型
env->CallVoidMethod(m_object, myMethod, env->NewStringUTF("c数据"));
return a + b;
}
}
//包名+类名字符串定义:
const char *class_name = "com/example/myc/MainActivity";
// 重点:函数表,相当于绑定,如果有多个方法要动态注册,在数组里面定义即可,第一个参数是java定义的函数名,第二个是函数签名,第三个是函数指针(也就是在c++自定义的函数名)
static const JNINativeMethod methods[] = {
{"stringFromJNI", "()Ljava/lang/String;", (void *) cpp_stringFromJNI},
{"intFromJNI", "(II)I", (void *) cpp_intFromJNI},
};
// 定义注册方法
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
// LOGD("动态注册");
JNIEnv *env;
if ((vm)->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
// LOGD("动态注册GetEnv fail");
return JNI_ERR;
}
// 获取类引用
jclass clazz = env->FindClass(class_name);
// 注册native方法,sizeof(methods) 表示 methods 数组占用的总字节数,sizeof(methods[0]) 表示 methods[0] 类型占用的字节数。将这两个值相除,得到 methods 数组中元素的数量。
jint register_result = env->RegisterNatives(clazz, methods,
sizeof(methods) / sizeof(methods[0]));
if (register_result) { // 非零true 进if
// LOGD("动态注册 fail regist_result = %d", regist_result);
} else {
// LOGI("动态注册 success result = %d", regist_result);
}
return JNI_VERSION_1_6;
}
void myMethod(JNIEnv *env, jobject obj) {
jclass stringClass = env->FindClass("java/lang/String");
jmethodID constructMethod = env->GetMethodID(stringClass, "<init>", "()V");
jobject stringObj = env->NewObject(stringClass, constructMethod);
std::string argStr = std::to_string(5);
jstring result = (jstring) env->NewStringUTF(argStr.c_str());
jobjectArray args = env->NewObjectArray(2, stringClass, stringObj);
env->SetObjectArrayElement(args, 0, result);
env->SetObjectArrayElement(args, 1, stringObj);
}
activity中
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private var b = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
// binding.sampleText.text = stringFromJNI()
// java调用c++方法
binding.sampleText.text = intFromJNI(1, b).toString()
findViewById<Button>(R.id.btn_jia).setOnClickListener {
binding.sampleText.text = intFromJNI(1, ++b).toString()
}
}
/**
* c++调用java方法
*/
fun myMethod(str:String){
Toast.makeText(this,"来自c端数据=$str",Toast.LENGTH_LONG).show()
}
/**
* java调用c++方法
*/
external fun stringFromJNI(): String
external fun intFromJNI(a: Int, b: Int): Int
companion object {
// 导入c++库与CMakeLists中的库的命名一致
init {
System.loadLibrary("myc")
}
}
}