jni小细节操作

  1. 数组的细节处理
#include <jni.h>
#include <string>
#include <stdlib.h>

int compare(const jint *a, const jint *b) {
    return *a - *b;
}


extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_sort(JNIEnv *env, jclass type, jintArray array_) {
    jint *intArray = env->GetIntArrayElements(array_, NULL);
    jsize length = env->GetArrayLength(array_);

    //排序
    //void *ptr	指向待排序的数组的指针
    //size_t count	数组的元素数目
    //size_t size  数组每个元素的字节大小
    // int (*comp)(const void *, const void *)  比较函数。若首个参数小于第二个,则返回负整数值,若首个参数大于第二个,
    // 则返回正整数值,若两参数相等,则返回零。
//    void qsort( void *ptr, size_t count, size_t size,int (*comp)(const void *, const void *) );

    qsort(intArray, static_cast<size_t>(length), sizeof(int),
          reinterpret_cast<int (*)(const void *, const void *)>(compare));

    //mode =
    //0 表示 既要同步数据给jArray,又要释放intArray
    //JNI_COMMIT : copy content, do not free buffer ,会同步数据给jArray,但是不会释放intArray
    //JNI_ABORT : free buffer w/o copying back ,不会同步数据给jArray,但是会释放intArray
    env->ReleaseIntArrayElements(array_, intArray, JNI_ABORT);
}

2.局部引用和全局引用

//局部变量
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_localRef(JNIEnv *env, jclass type) {

    //在Native层侯建的Java对象,不用了该怎么管理? 需要手动回收
    //在Java层开辟的内存由谁管理(JavaGC管理),能开辟多大内存?

    //字符串截取
    jclass str_clz = env->FindClass("java/lang/String");
    jmethodID init_str = env->GetMethodID(str_clz, "<init>", "()V");
    jobject j_str = env->NewObject(str_clz, init_str);
    //.....

    //jobject不使用了,需要回收。删除了就不能使用了
    env->DeleteLocalRef(j_str);
}



//全局变量,需要在合适的时机去释放 env->DeleteGlobalRef(globalStr);删除之后不能使用
jstring globalStr;
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_saveGlobalRef(JNIEnv *env, jclass type, jstring str_) {
    globalStr = static_cast<jstring>(env->NewGlobalRef(str_));
}
extern "C"
JNIEXPORT jstring JNICALL
Java_com_zzw_jnidemo_ArrayUtils_getStrGlobalRef(JNIEnv *env, jclass type) {

    return globalStr;
}

3.静态缓存策略

//局部静态缓存
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_staticLcalCache(JNIEnv *env, jclass type, jstring name_) {
//     jfieldID f_id =NULL;
    static jfieldID f_id = NULL;//局部缓存 ,这个方法会被多次调用,不需要反复去获取jfieldID
    if (!f_id) {
        f_id = env->GetStaticFieldID(type, "name", "Ljava/lang/String;");
    } else {
        LOGE("f_id不为null");//加上static将会缓存
    }
    env->SetStaticObjectField(type, f_id, name_);
}

//全局静态缓存

static jfieldID jfieldID1;
static jfieldID jfieldID2;
static jfieldID jfieldID3;
//一般初始化的时候调用
extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_initStatic(JNIEnv *env, jclass type) {
    jfieldID1 = env->GetStaticFieldID(type, "name", "Ljava/lang/String;");
    jfieldID2 = env->GetStaticFieldID(type, "name1", "Ljava/lang/String;");
    jfieldID3 = env->GetStaticFieldID(type, "name2", "Ljava/lang/String;");
}

//如果反复调用设置值得函数,那么将不会每次都获取id
void setData(JNIEnv *env, jclass jclazz, jobject object) {
    env->SetStaticObjectField(jclazz, jfieldID1, object);
    env->SetStaticObjectField(jclazz, jfieldID2, object);
    env->SetStaticObjectField(jclazz, jfieldID3, object);
}

4.异常处理

extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnidemo_ArrayUtils_exception2(JNIEnv *env, jclass cls) {
    jthrowable exc = NULL;
    jmethodID mid = env->GetStaticMethodID(cls, "exceptionCallback", "()V");
    if (mid != NULL) {
        env->CallStaticVoidMethod(cls, mid);
    }
    LOGE("In C: Java_com_zzw_jnidemo_ArrayUtils_exception2-->called!!!!");
    //这种也是可以检测的
//    exc = env->ExceptionOccurred();  // 返回一个指向当前异常对象的引用
//    if (exc) {

    if (env->ExceptionCheck()) {  // 检查JNI调用是否有引发异常
        env->ExceptionDescribe();//打印错误信息
        env->ExceptionClear();        // 清除引发的异常,在Java层不会打印异常的堆栈信息
        env->ThrowNew(env->FindClass("java/lang/Exception"), "JNI抛出的异常!");
        return;
    }
    //补救  。。。
    mid = env->GetStaticMethodID(cls, "normalCallback", "()V");
    if (mid != NULL) {
        env->CallStaticVoidMethod(cls, mid);
    }
}



     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        try {
            ArrayUtils.exception();
        } catch (Exception e) {
            e.printStackTrace();//打印抛出的异常
        }
    }
  1. c++调用java: 这里需要分为在主线程调用还是在子线程调用,在主线程调用就直接通过函数的env调用即可,在子线程调用则需要JavaVM ->AttachCurrentThread拿到env然后调用。

#include "JavaListener.h"
JavaVM *jvm;

JavaListener *javaListener;

pthread_t chidlThread;


void *childCallback(void *data)
{
    JavaListener *javaListener1 = (JavaListener *) data;
    //子线程调用
    javaListener1->onError(0, 101, "c++ call java meid from child thread!");
    pthread_exit(&chidlThread);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_zzw_jnithread_ThreadDemo_callbackFromC(JNIEnv *env, jobject instance) {

    javaListener = new JavaListener(jvm, env, env->NewGlobalRef(instance));
    //主线程调用
    //javaListener->onError(1, 100, "c++ call java meid from main thread!");
    //子线程调用
    pthread_create(&chidlThread, NULL, childCallback, javaListener);

}



JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void* reserved)
{
    JNIEnv *env;
    jvm = vm;
    if(vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK)
    {
        return -1;
    }
    return JNI_VERSION_1_6;
}

JavaListener.h

#include "jni.h"

#ifndef JNITHREAD_JAVALISTENER_H
#define JNITHREAD_JAVALISTENER_H


class JavaListener {

public:
    JavaVM *jvm;
    _JNIEnv *jenv;
    jobject jobj;
    jmethodID jmid;
public:
    JavaListener(JavaVM *vm, _JNIEnv *env, jobject obj);
    ~JavaListener();

    /**
     * 1:主线程
     * 0:子线程
     * @param type
     * @param code
     * @param msg
     */
    void onError(int type, int code, const char *msg);


};


#endif //JNITHREAD_JAVALISTENER_H

JavaListener.cpp

#include "JavaListener.h"


void JavaListener::onError(int type, int code, const char *msg) {

    if(type == 0)
    {
        JNIEnv *env;
        jvm->AttachCurrentThread(&env, 0);
        jstring jmsg = env->NewStringUTF(msg);
        env->CallVoidMethod(jobj, jmid, code, jmsg);
        env->DeleteLocalRef(jmsg);

        jvm->DetachCurrentThread();


    }
    else if(type == 1)
    {
        jstring jmsg = jenv->NewStringUTF(msg);
        jenv->CallVoidMethod(jobj, jmid, code, jmsg);
        jenv->DeleteLocalRef(jmsg);
    }
}

JavaListener::JavaListener(JavaVM *vm, _JNIEnv *env, jobject obj) {

    jvm = vm;
    jenv = env;
    jobj = obj;

    jclass clz = env->GetObjectClass(jobj);
    if(!clz)
    {
        return;
    }
    jmid = env->GetMethodID(clz, "onError", "(ILjava/lang/String;)V");
    if(!jmid)
        return;


}

ThreadDemo.java

public class ThreadDemo {

    static
    {
        System.loadLibrary("native-lib");
    }

    private OnErrerListener onErrerListener;

    public void setOnErrerListener(OnErrerListener onErrerListener) {
        this.onErrerListener = onErrerListener;
    }

    public void onError(int code, String msg)
    {
        if(onErrerListener != null)
        {
            onErrerListener.onError(code, msg);
        }
    }

    public interface OnErrerListener
    {
        void onError(int code, String msg);
    }

    public native  void callbackFromC();

}

-------------The End-------------

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • OpenGLES通过SurfaceTexture预览摄像头画面

    比如美颜相机那些,处理摄像头数据展示出来,为了提高预览的效率,所以这里使用了VBO和FBO,如果不知道这个,请看上面的文章。

    曾大稳
  • jni基础

    曾大稳
  • OpenGLES添加水印

    理解了添加水印的原理,不管是视频水印还是图片水印都是很简单的了,只是使用的纹理不一样而已。如果是绘制文字水印的话,则需要将文字生成图片,然后将图片使用纹理绘制即...

    曾大稳
  • NDK学习笔记(二)使用JNI同原生代码通信 原

    任何使用JNI的操作都需要两次或者三次函数调用,因此要实现大量的原生方法并让它们同Java类保持同步很容易编程一件非常艰辛的工作。

    LeoXu
  • iOS 自动打包 - fastlane (二)

    .env 是非常简单的配置文件,简单到什么程度呢? 它只支持一维K/V格式的配置项,而且 key 和 value 都只能是字符串。

    大壮
  • Android Studio2.2下NDK开发初试

    forrestlin
  • laravel 学习之路 配置config

    laravel 的配置项是在根目录下的 /config 目录中,还有一个是根目录下的 .env文件

    风清醉
  • matlab强化学习Q-Learning与Sarsa对比

    观察下一步选取哪一个动作会带来最大的奖励, 但是在真正要做决定时, 却不一定会选取到那个带来最大奖励的动作, Q-learning 只是估计了一下接下来的动作值

    万木逢春
  • Android NDK编程(六)--- JNI中类参数的传递与返回

    代码往往在不断写的情况下学习到新的东西,本以为NDK程序这块出完番外篇后应该就不用再出了,结果在使用过程中还是会有新的东西加入,所以又有了今天这一篇。

    Vaccae
  • Python 中更优雅的环境变量设置方案

    在运行一个项目的时候,我们经常会遇到设置不同环境的需求,如设置是开发环境、测试环境还是生产环境,或者在某些设置里面可能还需要设置一些变量开关,如设置调试开关、日...

    崔庆才

扫码关注云+社区

领取腾讯云代金券