JNI数据与方法操作实例

LOG引入

#include <android/log.h>

#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, "com.droider.jnimethods", __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "com.droider.jnimethods", __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, "com.droider.jnimethods", __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, "com.droider.jnimethods", __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, "com.droider.jnimethods", __VA_ARGS__)

最简单的Jni方法

/*
 * Class:     com_xiaomakj_hellogcc_TestJniMethods
 * Method:    nativeMethod
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_xiaomakj_hellogcc_TestJniMethods_nativeMethod
  (JNIEnv * env , jobject object){
     const char * chs = "你好!NativeMethod";
     return (*env)->NewStringUTF(env, chs);
  }

或者

jstring stoJstring( JNIEnv* env, const char* pat )
{
    jclass strClass = (*env)->FindClass(env, "java/lang/String");
    jmethodID ctorID =  (*env)->GetMethodID(env,
                                            strClass, "<init>", "([BLjava/lang/String;)V");
    jbyteArray bytes = (*env)->NewByteArray(env, strlen(pat));
    (*env)->SetByteArrayRegion(env, bytes, 0, strlen(pat), pat);
    jstring encoding = (*env)->NewStringUTF(env, "GB2312");
    return (jstring)(*env)->NewObject(env,
                                      strClass, ctorID, bytes, encoding);
}

对应native方法

public native String nativeMethod();

加载一个本地类

    int version = (*env)->GetVersion(env);//获JNI版本 
    LOGE("GetVersion() --> jni version:%2x", version);
    jclass build_class = (*env)->FindClass(env, "android/os/Build");  //加载一个本地类
    jfieldID brand_id = (*env)->GetStaticFieldID(env,           //获取类的静态字段ID
                                                 build_class, "MODEL", "Ljava/lang/String;");
    jstring brand_obj = (jstring)(*env)->GetStaticObjectField(env,  //获取类的静态字段的值ֵ
                                                              build_class, brand_id);
    //__asm__ ("bkpt");
    //raise(SIGTRAP);

    const char *nativeString = (*env)->GetStringUTFChars(env, brand_obj, 0); //通过jstring生成char*
    LOGE("GetStringUTFChars() --> MODEL:%s", nativeString);
    LOGE("GetStaticFieldID() --> %s", nativeString);
    LOGE("ReleaseStringUTFChars() --> %s", nativeString);
    (*env)->ReleaseStringUTFChars(env, brand_obj, nativeString); //释放GetStringUTFChars()生成的char*
GetVersion() --> jni version:10006
GetStringUTFChars() --> MODEL:vivo X9i
GetStaticFieldID() --> vivo X9i
ReleaseStringUTFChars() --> vivo X9i

初始化本地对象

    jclass test_class = (*env)->FindClass(env, "com/droider/jnimethods/TestClass");
    //空参构造
    jmethodID constructor = (*env)->GetMethodID(env, test_class, "<init>", "()V"); //获取构造函数
    jobject obj = (*env)->NewObject(env, test_class, constructor);  //创建一个对象

操纵成员变量

//String
  jfieldID stringfieldID = (*env)->GetFieldID(env,  //获取String类型的字段
                                                    test_class, "aStringField", "Ljava/lang/String;");
        jstring stringfieldValue = (jstring)(*env)->GetObjectField(env,    //获取String字段的值ֵ
                                                                   obj, stringfieldID);
        const char *stringValue = (*env)->GetStringUTFChars(env,
                                                            stringfieldValue, 0);
        LOGE("GetObjectField() --> aStringField:%s", stringValue);  

//int        
 jfieldID intfieldID = (*env)->GetFieldID(env,  //获取int类型的字段
                test_class, "aIntField", "I");
        jint fieldValue = (*env)->GetIntField(env, obj, intfieldID); //获取int字段的值ֵ
        LOGV("GetIntField() --> aField:%d", fieldValue);
        (*env)->SetIntField(env, obj, intfieldID, (jint)123); // 设置int字段的值ֵ
        fieldValue = (*env)->GetIntField(env, obj, intfieldID);
        LOGV("SetIntField() --> aField:%d", fieldValue);

获取父类

jclass parent_class = (*env)->GetSuperclass(env, test_class);
LOGV("GetSuperclass() --> ");       

JNI常量 定义在jni.h当中

#define JNI_FALSE   0
#define JNI_TRUE    1

#define JNI_VERSION_1_1 0x00010001
#define JNI_VERSION_1_2 0x00010002
#define JNI_VERSION_1_4 0x00010004
#define JNI_VERSION_1_6 0x00010006

#define JNI_OK          (0)         /* no error */
#define JNI_ERR         (-1)        /* generic error */
#define JNI_EDETACHED   (-2)        /* thread detached from the VM */
#define JNI_EVERSION    (-3)        /* JNI version error */

#define JNI_COMMIT      1           /* copy content, do not free buffer */
#define JNI_ABORT       2           /* free buffer w/o copying back */

EnsureLocalCapacity 检测是否还可以创建5个局部引用 https://blog.csdn.net/xyang81/article/details/44873769)

  // 保证至少可以创建3个局部引用(str_array,cls_string,obj_str)
    if ((*env)->EnsureLocalCapacity(env, 3) != JNI_OK) {
        return NULL;
    }

操作本地方法

if(JNI_OK == (*env)->EnsureLocalCapacity(env, 5)){ //检测是否还可以创建5个局部引用
    LOGV("EnsureLocalCapacity() --> ensure 5 locals");
    jmethodID obj2_voidmethod = (*env)->GetMethodID(env, //获取一个void方法
            test_class, "aVoidMethod", "()V");
    (*env)->CallVoidMethod(env, obj, obj2_voidmethod);
    LOGV("CallVoidMethod()");
    jmethodID obj2_Staticmethod = (*env)->GetStaticMethodID(env,  //获取static方法
            test_class, "aStaticMethod", "(Ljava/lang/String;)V");
    LOGV("GetStaticMethodID()");
    const char *fromJni = "this string from jni";
    jstring jstr_static = (*env)->NewStringUTF(env, fromJni);
    (*env)->CallStaticVoidMethod(env, test_class, obj2_Staticmethod, jstr_static); //调用一个静态方法
}

MonitorEnter类似于JAVA的synchronized成对存在

 (*env)->MonitorEnter(env, obj); //同步操作
 (*env)->MonitorExit(env, obj);

操作String

//获取String
jmethodID obj2_chinesemethod = (*env)->GetMethodID(env,  //传递中文字符串
                    test_class, "getChineseString", "()Ljava/lang/String;");
jstring obj2_chinesejstring = (jstring)(*env)->CallObjectMethod(env, obj, obj2_chinesemethod);
jsize chinese_size = (*env)->GetStringLength(env, obj2_chinesejstring);
LOGV("GetStringLength() --> %d", chinese_size);

//GetStringRegion http://wiki.jikexueyuan.com/project/jni-ndk-developer-guide/string.html
//为这个字符串分配空间 0:statr,3:len,buff_jchar:end
jchar buff_jchar[4] = {0};
(*env)->GetStringRegion(env, obj2_chinesejstring, 0, 3, buff_jchar);
LOGV("GetStringRegion() -->");

jStrinfg to jchar

 const jchar * obj2_chinesechars = (*env)->GetStringChars(env, obj2_chinesejstring, NULL);
 jstring new_chinesestring = (*env)->NewString(env, obj2_chinesechars, chinese_size);

调用本地方法 并释放String

jmethodID obj2_Staticmethod = (*env)->GetStaticMethodID(env,  //获取static方法
                                                        test_class, "aStaticMethod", "(Ljava/lang/String;)V");
LOGE("GetStaticMethodID()");
(*env)->CallStaticVoidMethod(env, test_class, obj2_Staticmethod, new_chinesestring); //调用一个静态方法
(*env)->ReleaseStringChars(env, obj2_chinesejstring, obj2_chinesechars); //释放GetStringChars获取的jchar*
                LOGV("CallStaticVoidMethod()");

Jni调用JAVA返回计算结果

jmethodID obj2_sqrtmethod = (*env)->GetStaticMethodID(env,  //获取一个static方法
                                                                  test_class, "sqrt", "(I)I");
jint int_sqrt = (*env)->CallStaticIntMethod(env, //计算5的平方
                                            test_class, obj2_sqrtmethod, (jint)5);
LOGE("CallStaticIntMethod() -->5 sqrt is:%d", int_sqrt);

java代码

public static int sqrt(int x){
        return x * x;
}

IsAssignableFrom:

判断该类是否是本类或子类 是则返回true

https://www.jb51.net/article/103114.htm

抛出一个异常

if(JNI_TRUE == (*env)->IsAssignableFrom(env, test_class, parent_class)){
            LOGV("IsAssignableFrom() --> yes");
        } else {
            jclass newExceptionClazz = (*env)->FindClass(env,"java/lang/RuntimeException"); //实例化一个异常
            if(newExceptionClazz != NULL)
                (*env)->ThrowNew(env, newExceptionClazz,"这里永远不会被执行的!");
            LOGE("ThrowNew()");
        }

获取对象的类

 jclass obj_clazz = (*env)->GetObjectClass(env, obj); //获取对象的类
            if(JNI_TRUE == (*env)->IsInstanceOf(env, obj, obj_clazz)){
                LOGE("IsInstanceOf() --> Yes");
            } else {
                (*env)->FatalError(env, "fatal error!"); //抛出致命错误
                LOGE("FatalError()");
            }

FatalError致命错误

  (*env)->FatalError(env, "fatal error!");

创建全局和局部引用 相当于 new

(*env)->PushLocalFrame(env, 2); //申请局部引用空间,增加局部引用的管理
    jobject obj_localref = (*env)->NewLocalRef(env, obj); //创建一个局部引用
    jobject obj_globalref = (*env)->NewGlobalRef(env, obj); //创建一个全局引用
    LOGV("PushLocalFrame()");
    LOGV("NewLocalRef()");
    LOGV("NewGlobalRef()");
    if (JNI_TRUE == (*env)->IsSameObject(env, obj_localref, obj_globalref)){
        LOGV("IsSameObject() --> Yes");
    }
    (*env)->DeleteLocalRef(env, obj_localref);   //删除一个局部引用
    (*env)->DeleteGlobalRef(env, obj_globalref); //删除一个全局引用
    (*env)->PopLocalFrame(env, NULL);
    LOGV("DeleteLocalRef()");
    LOGV("DeleteGlobalRef()");
    LOGV("PopLocalFrame()");

调用子类成员和方法

注意:CallNonvirtualVoidMethod方法 第三个参数为指定类类型

jclass sub_class = (*env)->FindClass(env,
            "com/droider/jnimethods/TestSubClass"); //查询子类
    jobject sub_obj = (*env)->AllocObject(env, sub_class);
    jmethodID sub_methodID = (*env)->GetMethodID(env,
            sub_class, "aVoidMethod", "()V");
    (*env)->CallNonvirtualVoidMethod(env,
            sub_obj, sub_class, sub_methodID); //调用子类的方法
    (*env)->CallNonvirtualVoidMethod(env,
            sub_obj, test_class, sub_methodID); //根据调用类的不同调用父类的方法
    jfieldID sub_fieldID = (*env)->GetStaticFieldID(env, //获取子类静态字段
            sub_class, "subFloatField", "F");
    (*env)->SetStaticFloatField(env, sub_class, sub_fieldID, (jfloat)33.88f);
    LOGV("SetStaticFloatField() --> %.2f",
            (*env)->GetStaticFloatField(env, sub_class, sub_fieldID));

异常抓取
https://blog.csdn.net/arui319/article/details/2178619

    jthrowable throwable = (*env)->ExceptionOccurred(env);
    //判断是否包含异常 有则清除和打印异常
    if (throwable){     //有异常发生,还可以使用ExceptionCheck()函数来判断
        (*env)->ExceptionDescribe(env);
        (*env)->ExceptionClear(env);
        LOGE("ExceptionOccurred()");
    }

int 数组操作

jintArray int_array = (*env)->NewIntArray(env, 5);  //创建int数组
LOGV("NewIntArray() --> %d", (*env)->GetArrayLength(env, int_array)); //获取数组长度
const jint ints[] = {11, 12, 13, 14, 15};
(*env)->SetIntArrayRegion(env, int_array, 0, 5, ints); //设置数组一个范围的值ֵ
LOGV("SetIntArrayRegion() --> %d,%d,%d,%d,%d",
        ints[0], ints[1], ints[2], ints[3], ints[4]);
jint ints2[2] = {0, 0};
(*env)->GetIntArrayRegion(env, int_array, 1, 2, ints2); //获取数组一个范围的值
LOGV("GetIntArrayRegion() --> %d,%d", ints2[0], ints2[1]);
jint* array_ints = (*env)->GetIntArrayElements(env, int_array, NULL); //获取指向所有元素的指针
LOGV("GetIntArrayElements() --> %d,%d,%d,%d,%d",
        array_ints[0], array_ints[1], array_ints[2], array_ints[3], array_ints[4]);
(*env)->ReleaseIntArrayElements(env, int_array, array_ints, 0); //释放指向所有元素的指针
LOGV("ReleaseIntArrayElements()");

String 数组操作

jclass class_string = (*env)->FindClass(env, "java/lang/String");
jarray string_array = (*env)->NewObjectArray(env, 3, class_string, 0); //创建String数组
LOGV("NewObjectArray()");
jsize array_size = (*env)->GetArrayLength(env, string_array);
LOGV("GetArrayLength() --> %d", array_size);
jstring array_string1 = (*env)->NewStringUTF(env, "one");
char buff_char[4] = {0};
(*env)->GetStringUTFRegion(env, array_string1, 0, 3, buff_char);
LOGV("GetStringUTFRegion() --> %s", buff_char);
(*env)->SetObjectArrayElement(env, string_array, 0, array_string1); //设置数组元素的值
LOGV("SetObjectArrayElement() --> one");
array_string1 = (jstring)(*env)->GetObjectArrayElement(env, string_array, 0); //获取数组元素的值
const char* array_elemchars = (*env)->GetStringUTFChars(env, array_string1, NULL);
LOGV("GetObjectArrayElement() --> %s", array_elemchars);
(*env)->ReleaseStringUTFChars(env, array_string1, array_elemchars);

JNI_OnLoad和JNI_OnUnLoad:
JNI的内部方法,RegisterNatives加载本地方法,动态引入native方法,也就是在.h文件中并未实现申明的函数在JNI中动态添加

JNIEnv *g_env;
jclass native_class;
#ifndef NELEM //计算结构元素个数
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
#endif

static JNINativeMethod methods[] = {
    {"newJniThreads", "(I)V", (void*)newJniThreads}
};

jint JNI_OnLoad(JavaVM* vm, void* reserved){
    if(JNI_OK != (*vm)->GetEnv(vm, (void**)&g_env, JNI_VERSION_1_6)){ //加载指定版本的JNI  对应 int version = (*env)->GetVersion(env);
        return -1;
    }
    LOGV("JNI_OnLoad()");
    native_class = (*g_env)->FindClass(g_env, "com/xiaomakj/hellogcc/TestJniMethods");
    if (JNI_OK ==(*g_env)->RegisterNatives(g_env,   //注册未声明的本地方法
            native_class, methods, NELEM(methods))){
        LOGV("RegisterNatives() --> nativeMethod() ok");
    } else {
        LOGE("RegisterNatives() --> nativeMethod() failed");
        return -1;
    }
    return JNI_VERSION_1_6;
}

void JNI_OnUnLoad(JavaVM* vm, void* reserved){
    LOGV("JNI_OnUnLoad()");
    (*g_env)->UnregisterNatives(g_env, native_class);
    LOGV("UnregisterNatives()");
}

newJniThreads方法 注意:
1.方法名较 Java_com_droider_jnimethods_TestJniMethods_test短得多
2.这里方法需要事先申明 否则将该方法放在JNI_OnLoad之前

#include <pthread.h>//引入线程头文件
JNIEXPORT void newJniThreads(JNIEnv* env, jobject obj, jint nums){}

线程操作
注意:
1.代码的顺序 thread_func在上面
2.GetJavaVM方法获取全集JVM对象,控制线程的创建


#include <pthread.h>    //导入线程.h
JavaVM *g_vm;   //下面多线程程序用到
pthread_mutex_t thread_mutex;

void *thread_func(void *arg){
    JNIEnv *env;
    pthread_mutex_lock(&thread_mutex);
    if (JNI_OK != (*g_vm)->AttachCurrentThread(g_vm, &env, NULL)){
        LOGE("AttachCurrentThread() failed");
        return NULL;
    }
    LOGE("AttachCurrentThread() --> thread:%d", (jint)arg);
    (*g_vm)->DetachCurrentThread(g_vm);
    LOGE("DetachCurrentThread() --> thread:%d", (jint)arg);
    pthread_mutex_unlock(&thread_mutex);
    pthread_exit(0);
    return NULL;
}

JNIEXPORT void newJniThreads(JNIEnv* env, jobject obj, jint nums){
    (*env)->GetJavaVM(env, &g_vm);  //保存全局JavaVM
    LOGE("GetJavaVM()");
    pthread_t* pt = (pthread_t*)malloc(sizeof(pthread_t)* nums);
    pthread_mutex_init(&thread_mutex, NULL);
    int i;
    for (i = 0 ; i < nums; i++){
        pthread_create(&pt[i], NULL, &thread_func, (void*)i); //创建线程
    }
    free(pt);  
}

分析一下 pthread_create:

 pthread_create(&pt[i], NULL, &thread_func, (void*)i); //创建线程
 第一个参数pt代表了数组pt的首地址指正,pt[i]也就是申请的第i个指针
 &pt[i]即取地址值

参考

https://blog.csdn.net/fx677588/article/details/74857473

试验可参考

https://blog.csdn.net/oyhb_1992/article/details/77162326?locationNum=9&fps=1

帮助理解int a[5];

a的类型是int[5]   数组

&a的类型是int(*)[5]  指针——指向int[5]数组的指针

&a[0]的类型是int*    指针——指向int类型的指针

malloc和free成对存在:动态分配和释放内存
malloc返回的是分配内存的地址

pthread_mutex_lock 锁

pthread_mutex_lock(&thread_mutex);  pthread_mutex_unlock(&thread_mutex);
pthread_exit(0)

字节缓冲区


JNIEXPORT jobject allocNativeBuffer(JNIEnv* env, jobject obj, jlong size){
    void* buffer = malloc(size);
    jobject directBuffer = (*env)->NewDirectByteBuffer(env, buffer, size);
    LOGE("NewDirectByteBuffer() --> %d", (int)size);
    return directBuffer;
}

JNIEXPORT void freeNativeBuffer(JNIEnv* env, jobject obj, jobject bufferRef)
{
    void *buffer = (*env)->GetDirectBufferAddress(env, bufferRef);
    strcpy(buffer, "123");
    LOGE("GetDirectBufferAddress() --> %s", buffer);
    free(buffer);
}

JAVA调用

 Object obj = methods.allocNativeBuffer(16L); //分配字节缓冲区
 methods.freeNativeBuffer(obj);  //释放字节缓冲区

源码链接:https://pan.baidu.com/s/1_EhtJ2RoLCmtIu1pV5CdAQ 密码:sywq
文件路径:
++源代码(韦生强)\源代码\chapter7\7.6\7.6.2\jnimethods\jni++

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值