jni原理和实现

一、jni原理

主要就是通过数据类型签名和反射来实现java与c/c++方法进行交互的

数据类型签名对应表

javac/c++
booleanZ
byteB
charC
shortS
intI
longL
floatF
doubleD
voidV
objectL开头,然后以/分割包的完整类型,后面再加; 比如String的签名就是Ljava/long/String
Array以[开头,在加上数组元素类型的签名, 比如int[],签名是[I,int[][]的签名是[[I, object[]的签名是[Ljava/lang/Object

数据类型对应表

javac/c++
booleanjboolean
bytejbyte
charjchar
shortjshort
intjint
longjlong
floatjfloat
doublejdouble
voidvoid

引用类型对应表

javac/c++
stringjstring
objectjobject
classjclass
byte[]jbyteArray

二、实现jni

增加这三处

在这里插入图片描述

		ndk{
            // 设置支持的架构
            abiFilters 'armeabi-v7a', 'arm64-v8a'
        }
        externalNativeBuild {
	        cmake {
	            path file('src/main/cpp/CMakeLists.txt')
	            version '3.18.1'
	        }
    	}

CMakeLists.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")
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值