JNI初步教程

JNI初步教程

创建工程选择引入ndk,再加入构建模块
这里写图片描述
程序跑出来就有一个默认的调用c的方法,
下面是一个完整的c的方法

extern "C"
JNIEXPORT jstring

JNICALL
Java_com_example_administrator_testnative_MainActivity_stringFromJNI(
//        JNIEnv 类型代表了Java环境。通这个JNIEnv指针,就可以对Java断的代码进行操作,例如,创建Java类的对象
//        调用Java对象的方法,获取Java对象的属性等等,JNIEnv的指针会被jni传入到本地方法的实现函数中来对Java断的代码进行操作
        JNIEnv *env,
//        如果在Java中不是静态方法,该方法对应的类的实例;如果是静态方法传入的是该类的.class对象
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

JNIEnv中提供的方法

JNIEnv中提供的方法

Java中类型在c中的对应关系

这里写图片描述

在jni中获取jclass类

这里写图片描述

在jni中访问Java的方法与属性

这里写图片描述

示例:

extern "C"
JNIEXPORT void

JNICALL
Java_com_example_administrator_testnative_MainActivity_test(
        JNIEnv *env,
        jobject obj) {
    // 得到jclass对象
    jclass mainActivity = env->GetObjectClass(obj);
    // 得到方法的id
    jmethodID id = env->GetMethodID(mainActivity,"jniCall","(I)I");
    // 调用方法
    env->CallIntMethod(obj,id,100L);
}

在上面用到了签名,签名是对应的方法的参数和返回值类型设置的,上面的示例中"(I)I"对应的是有int类型的参数返回值为int类型如果有多个类型"(int i, boolean b,String s,int j)"对应的是""(IZLjava/lang/String;I),如果没有返回值没有参数"()V""()V"
这里写图片描述

在jdk中提供了javap工具签名
这里写图片描述

这里写图片描述

为了方便调试在jni中打印安卓日志

#include  <android/log.h>
#define  TAG    "HHH"   // 这是安卓的Tag  ANDROID_LOG_ERROR是日志级别 
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

    // 将java字符串转换成c字符串后再打印
    const char * str =  env->GetStringUTFChars(javaString,NULL);

    LOGE("%s",str);

JNI中获取到java的字符串

// 在java中传入字符串到jni然后再返回java
extern "C"
JNIEXPORT

jstring
JNICALL
Java_com_example_administrator_testnative_MainActivity_callJniMethod(
        JNIEnv *env,
        jobject,jstring javaString){

    // 将java字符串转换成c字符串
    const char * str =  env->GetStringUTFChars(javaString,NULL);

    jstring javaStr = env->NewStringUTF(str);
    return javaStr;
}

jni数组操作

    // 在jni中创建数组
    // 定义c数组
    jint nativeArr[3] = {21,22,23};
    // 定义java数组,可以返回给java使用的数组
    jintArray javaArray = env->NewIntArray(3);
    // 将c数组的内容复制给java
    env->SetIntArrayRegion(javaArray,0,3,nativeArr);


// 复制数组再操作 在java中传入一个数组在c中计算完成后返回值
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_administrator_testnative_MainActivity_getSum(
        JNIEnv *env,
        jobject obj,
        jintArray javaArr) {

    // 得到数组的长度
    jsize len = env->GetArrayLength(javaArr);
    // 定义一个数组
    jint cArr[len];
    // 将java数组复制到c中
    env->GetIntArrayRegion(javaArr,0,len,cArr);
    jint sum = 0;
    for (int i = 0; i < len; i++) {
        sum +=cArr[i];
    }
    return sum;
}


// 第二种中对直接指针的操作
// 在java中传入一个数组在c中计算完成后返回值
extern "C"
JNIEXPORT jint JNICALL
Java_com_example_administrator_testnative_MainActivity_getSum(
        JNIEnv *env,
        jobject obj,
        jintArray javaArr) {

    jint result = 1;
    jsize length = env->GetArrayLength(javaArr);
    // 等到c数组的int指针
    jint* nativeDirectArray = env->GetIntArrayElements(javaArr,NULL);
    for(int i=0;i<length;i++){
        result += nativeDirectArray[i];
//        nativeDirectArray[i] = i;
    }

    // 排序
    std::sort(nativeDirectArray,nativeDirectArray+length);

    // 0:将内容复制回来并释放原生数组 在c中改变数组,java中也会改变
    // JNI_COMMIT:将内容复制回来但是不释放原生数组,一般用于周期性的更新一个Java数组。
    // JNI_ABORT释放原生数组但是不将内容复制回来。
    env->ReleaseIntArrayElements(javaArr,nativeDirectArray,0);
    return result;
}

在c中全局引用、局部引用、弱引用

从java虚拟机创建的对象传到本地c代码是会产生引用。根据java的垃圾回收机制,只要有引用存在就不会触发该引用指向的java对象的垃圾回收。
这些引用在jni中分三种
全局引用(GlobalReference)
局部引用(LocalReference)
弱全局引用(WeakGlobalReference)
这里写图片描述
这里写图片描述
这里写图片描述

由于jfielId和jmethodId在创建时会消耗,所以可以缓存起来
第一种在方法内使用static,在多次调用该方法时只会创建一次id

    static jmethodID initId;
    if (initId==NULL) {
        initId= env->GetMethodID(dateClass,"<init>","()V");
    }

第二种使用全局变量存储,在任何native函数调用之前初始化

// 全局变量
jfieldID gId=0;

extern "C"
JNIEXPORT void JNICALL Java_com_example_administrator_testnative_MainActivity_init(
        JNIEnv* env,
        jobject obj) {
    jclass cl = env->GetObjectClass(obj);
    gId = env->GetFieldID(cl,"age","I");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值