NDK开发——JNI必须掌握的常用例子
来源:互联网 发布:suse 查看端口 编辑:程序博客网 时间:2024/05/21 11:17
- 前言
- 效果预览
- 导入依赖库
- 宏定义
- 访问Java属性
- 访问java静态属性
- 访问java方法
- 访问java静态方法
- 访问构造方法
- 调用父类的方法
- 字符串转码
- 给定一个数组进行排序并同步到Java中
- 创建一个指定大小的数组
- 局部变量引用
- 创建全局引用
- 获得全局引用
- 释放全局引用
- 弱全局引用
- 抛出异常处理
- 局部静态变量
- 初始化全局变量
- 文件加密
- 文件解密
- 获取文件大小
- 分割文件
- 合并文件
前言
要学习NDK之前,我们得先在AndroidStudio中学习JNI,还有C和C++基础,这些都是接触NDK的前提,那么废话不多说,开始吧
对于NDK在AndroidStudio的配置,可以关注我的博客找到相关文章,本文章是基于配置好NDK环境之后来操作的
效果预览
github:https://github.com/AndroidHensen/JNIDemo
导入依赖库
我们先导入我们需要用到的依赖库
#include <stdio.h>#include <stdlib.h>#include <jni.h>#include <string.h>#include <android/log.h>
宏定义
定义两个宏函数,用于Log输出调试
#define LOGI(FORMAT,...) __android_log_print(ANDROID_LOG_INFO,"TAG",FORMAT,__VA_ARGS__)#define LOGE(FORMAT,...) __android_log_print(ANDROID_LOG_ERROR,"TAG",FORMAT,__VA_ARGS__)
这里使用到AndroidLog的输出,需要手动在mk中导入,或者在Gradle的ndk配置中
LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := jni_studyLOCAL_SRC_FILES := jni_study.cLOCAL_LDLIBS := -llog //Log打印include $(BUILD_SHARED_LIBRARY)
ndk{ moduleName "jni_study" ldLibs "log" //Log打印 abiFilters "armeabi", "armeabi-v7a", "x86" }
访问Java属性
JNIEXPORT jstring JNICALL Java_com_handsome_ndkdemo_MainActivity_accessField (JNIEnv *env, jobject jobj){ //获取对象的class jclass cls = (*env)->GetObjectClass(env, jobj); //获取属性id,参数二:class对象,参数三:属性名,参数四:属性签名 jfieldID fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;"); //获取属性值 jstring jstr = (*env)->GetObjectField(env, jobj, fid); //isCopy代表:函数内部是否已经复制,复制了返回JNI_TRUE,没复制返回JNI_FALSE jboolean isCopy = NULL; //转换成c语言识别的字符串 char *c_str = (*env)->GetStringUTFChars(env,jstr,&isCopy); //拼接字符串 char text[20] = "myName is"; strcat(text,c_str); //生成Java识别的字符串 jstring new_jstr = (*env)->NewStringUTF(env,text); //修改属性的值 (*env)->SetObjectField(env,jobj,fid,new_jstr); //只要使用了GetStringUTFChars,官方建议一定要释放 (*env)->ReleaseStringUTFChars(env, jstr, c_str); return new_jstr;}
访问java静态属性
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_accessStaticField (JNIEnv *env, jobject jobj){ //获取对象的class jclass cls = (*env)->GetObjectClass(env, jobj); //获取属性id,参数二:class对象,参数三:属性名,参数四:属性签名 jfieldID fid = (*env)->GetStaticFieldID(env, cls, "count", "I"); //获取属性值 jint count = (*env)->GetStaticIntField(env, cls, fid); //++操作 count++; //修改属性的值 (*env)->SetStaticIntField(env,cls,fid,count);}
访问java方法
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_accessMethod(JNIEnv *env, jobject jobj){ //jclass jclass cls = (*env)->GetObjectClass(env, jobj); //获取方法id,参数二:class对象,参数三:方法名,参数四:方法签名 jmethodID mid = (*env)->GetMethodID(env, cls, "genRandomInt", "(I)I"); //调用方法 jint random = (*env)->CallIntMethod(env, jobj, mid, 200);}
访问java静态方法
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_accessStaticMethod(JNIEnv *env, jobject jobj){ //jclass jclass cls = (*env)->GetObjectClass(env, jobj); //获取方法id,参数二:class对象,参数三:方法名,参数四:方法签名 jmethodID mid = (*env)->GetStaticMethodID(env, cls, "getUUID", "()Ljava/lang/String;"); //调用方法 jstring uuid = (*env)->CallStaticObjectMethod(env, cls, mid);}
访问构造方法
使用java.util.Date产生一个当前的时间戳
JNIEXPORT jobject JNICALL Java_com_handsome_ndkdemo_MainActivity_accessConstructor(JNIEnv *env, jobject jobj){ //jclass jclass cls = (*env)->FindClass(env, "java/util/Date"); //获取方法id,参数二:class对象,参数三:方法名,参数四:方法签名 jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V"); //创建对象 jobject date_obj = (*env)->NewObject(env , cls, constructor_mid); //获取方法id,参数二:class对象,参数三:方法名,参数四:方法签名 jmethodID mid = (*env)->GetMethodID(env , cls, "getTime", "()J"); //调用方法 jlong time = (*env)->CallLongMethod(env, date_obj, mid); printf("time:%lld\n",time); return date_obj;}
调用父类的方法
该方法出错,还未找到解决方法
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_accessNonvirtualMethod(JNIEnv *env, jobject jobj){ //jclass jclass cls = (*env)->GetObjectClass(env, jobj); //获取human对象id jfieldID fid = (*env)->GetFieldID(env, cls, "human", "Lcom/handsome/ndkdemo/Bean/Human;"); //获取human这个对象 jobject human_obj = (*env)->GetObjectField(env, jobj, fid); //获取父类Human的jclass jclass human_cls = (*env)->FindClass(env, "com/handsome/ndkdemo/Bean/Human"); //获取父类Human的sayHi的方法 jmethodID mid = (*env)->GetMethodID(env, human_cls, "sayHi", "()V"); //调用的父类的方法 (*env)->CallNonvirtualObjectMethod(env, human_obj, human_cls, mid);}
字符串转码
字符串转码,c字符串直接给jstring时,由于编码不同会出现中文乱码问题,解决方法是采用Java的String(byte bytes[], String charsetName)构造方法
JNIEXPORT jstring JNICALL Java_com_handsome_ndkdemo_MainActivity_chineseChars(JNIEnv *env, jobject jobj){ char *c_str = "马蓉与宋江"; //第一步:jmethodID jclass str_cls = (*env)->FindClass(env, "java/lang/String"); jmethodID constructor_mid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V"); //第二步:创建byte数组 jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str)); //byte数组赋值 (*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str); //第三步:字符编码jstring jstring charsetName = (*env)->NewStringUTF(env, "UTF-8"); //调用构造函数,返回编码之后的jstring return (*env)->NewObject(env,str_cls,constructor_mid,bytes,charsetName);}
给定一个数组进行排序并同步到Java中
int compare(int *a,int *b){ return (*a) - (*b);}JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_giveArray (JNIEnv *env, jobject jobj,jintArray arr){ //jintArray -> jint指针 -> c int 数组 jint *elems = (*env)->GetIntArrayElements(env, arr, NULL); //数组的长度 int len = (*env)->GetArrayLength(env, arr); //排序,参数三:排序函数 qsort(elems, len, sizeof(jint), compare); //同步,以下对第四个参数的解释 //0, Java数组进行更新,并且释放C/C++数组 //JNI_ABORT, Java数组不进行更新,但是释放C/C++数组 //JNI_COMMIT,Java数组进行更新,不释放C/C++数组(函数执行完,数组还是会释放) (*env)->ReleaseIntArrayElements(env, arr, elems, JNI_COMMIT);}
创建一个指定大小的数组
JNIEXPORT jintArray JNICALL Java_com_handsome_ndkdemo_MainActivity_getArray (JNIEnv *env, jobject jobj, jint len){ jintArray jint_arr = (*env)->NewIntArray(env, len); jint *elems = (*env)->GetIntArrayElements(env, jint_arr, NULL); int i = 0; for (; i < len; i++){ elems[i] = i; } //同步 (*env)->ReleaseIntArrayElements(env, jint_arr, elems, 0); return jint_arr;}
局部变量引用
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_localRef (JNIEnv *env, jobject jobj){ int i = 0; for (; i < 5; i++){ //创建Date对象 jclass cls = (*env)->FindClass(env, "java/util/Date"); jmethodID constructor_mid = (*env)->GetMethodID(env, cls, "<init>", "()V"); jobject obj = (*env)->NewObject(env, cls, constructor_mid); //此处省略一百行代码... //通知垃圾回收器回收这些对象 (*env)->DeleteLocalRef(env, obj); }}
创建全局引用
//全局引用,可以共享(跨多个线程),手动控制内存使用jstring global_str;JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_createGlobalRef (JNIEnv *env, jobject jobj){ jstring obj = (*env)->NewStringUTF(env, "jni development is powerful!"); global_str = (*env)->NewGlobalRef(env, obj);}
获得全局引用
JNIEXPORT jstring JNICALL Java_com_handsome_ndkdemo_MainActivity_getGlobalRef (JNIEnv *env, jobject jobj){ return global_str;}
释放全局引用
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_deleteGlobalRef (JNIEnv *env, jobject jobj){ (*env)->DeleteGlobalRef(env, global_str);}
弱全局引用
- 节省内存,在内存不足时可以是释放所引用的对象
- 可以引用一个不常用的对象,如果为NULL,临时创建
- 创建:NewWeakGlobalRef,销毁:DeleteGlobalWeakRef
抛出异常处理
异常处理做法
- Java层捕获JNI自己抛出的Throwable异常
- 用户通过清空异常,然后抛出ThrowNew新的异常
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_exeception (JNIEnv *env, jobject jobj){ jclass cls = (*env)->GetObjectClass(env, jobj); //会抛出异常 jfieldID fid = (*env)->GetFieldID(env, cls, "key2", "Ljava/lang/String;"); //检测是否发生Java异常 jthrowable exception = (*env)->ExceptionOccurred(env); if (exception != NULL){ //清空异常信息,为了让Java代码可以继续运行 (*env)->ExceptionClear(env); //补救措施 fid = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;"); } //获取属性的值 jstring jstr = (*env)->GetObjectField(env, jobj, fid); char *str = (*env)->GetStringUTFChars(env, jstr, NULL); //对比属性值是否合法 if (strcmp(str, "super Hensen") != 0){ //不合法则抛出异常,给Java层处理 jclass newExcCls = (*env)->FindClass(env, "java/lang/IllegalArgumentException"); (*env)->ThrowNew(env,newExcCls,"key's value is invalid!"); }}
局部静态变量
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_cached (JNIEnv *env, jobject jobj){ jclass cls = (*env)->GetObjectClass(env, jobj); //局部静态变量 static jfieldID key_id = NULL; if (key_id == NULL){ //只执行一次代码 key_id = (*env)->GetFieldID(env, cls, "key", "Ljava/lang/String;"); }}
初始化全局变量
jfieldID key_fid;jmethodID random_mid;JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_MainActivity_initIds (JNIEnv *env, jclass jcls){ key_fid = (*env)->GetFieldID(env, jcls, "key", "Ljava/lang/String;"); random_mid = (*env)->GetMethodID(env, jcls, "genRandomInt", "(I)I");}
文件加密
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_Utils_FileUtils_crypt(JNIEnv *env, jclass jcls, jstring normal_path_jstr,jstring crypt_path_jstr) { const char* normal_path = (*env)->GetStringUTFChars(env, normal_path_jstr, NULL); const char* cpypt_path = (*env)->GetStringUTFChars(env, crypt_path_jstr, NULL); FILE *normal_fp = fopen(normal_path,"rb"); FILE *cpypt_fp = fopen(cpypt_path,"wb"); int ch; while((ch = fgetc(normal_fp)) != EOF){ fputc(ch ^ 9 ,cpypt_fp); } fclose(normal_fp); fclose(cpypt_fp);}
文件解密
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_Utils_FileUtils_decrypt(JNIEnv * env, jclass jcls, jstring crypt_path_jstr, jstring decrypt_path_jstr) { const char* normal_path = (*env)->GetStringUTFChars(env, crypt_path_jstr, NULL); const char* cpypt_path = (*env)->GetStringUTFChars(env, decrypt_path_jstr, NULL); FILE *normal_fp = fopen(normal_path,"rb"); FILE *cpypt_fp = fopen(cpypt_path,"wb"); int ch; while((ch = fgetc(normal_fp)) != EOF){ fputc(ch ^ 9 ,cpypt_fp); } fclose(normal_fp); fclose(cpypt_fp);}
获取文件大小
long get_file_size(char *path){ FILE *fp = fopen(path,"r"); fseek(fp,0,SEEK_END); return ftell(fp);}
分割文件
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_Utils_FileUtils_diff (JNIEnv *env, jclass jcls, jstring path_jstr,jstring path_pattern_jstr, jint file_num){ const char* path = (*env)->GetStringUTFChars(env,path_jstr,NULL); const char* path_pattern = (*env)->GetStringUTFChars(env,path_pattern_jstr,NULL); //得到分割之后的子文件的路径列表 char **patches = malloc(sizeof(char*) * file_num); int i = 0; for (; i < file_num; i++) { patches[i] = malloc(sizeof(char) * 100); //元素赋值 sprintf(patches[i], path_pattern, (i+1)); LOGI("patch path:%s",patches[i]); } int filesize = get_file_size(path); FILE *fpr = fopen(path,"rb"); //不断读取path文件,循环写入file_num个文件中 //如果文件大小和文件数目整除,则每个文件大小为 filesize / file_num //如果文件大小和文件数目不整除,则每个文件大小为 filesize / (file_num - 1),最后一个文件大小为 filesize % (file_num - 1) if(filesize % file_num == 0){ //单个文件大小 int part = filesize / file_num; i = 0; //逐一写入不同的分割子文件中 for (; i < file_num; i++) { FILE *fpw = fopen(patches[i], "wb"); int j = 0; for(; j < part; j++){ //边读边写 fputc(fgetc(fpr),fpw); } fclose(fpw); } }else{ int part = filesize / (file_num - 1); i = 0; //逐一写入不同的分割子文件中 for (; i < file_num - 1; i++) { FILE *fpw = fopen(patches[i], "wb"); int j = 0; for(; j < part; j++){ //边读边写 fputc(fgetc(fpr),fpw); } fclose(fpw); } //最后一个文件 FILE *fpw = fopen(patches[file_num - 1], "wb"); i = 0; for(; i < filesize % (file_num - 1); i++){ fputc(fgetc(fpr),fpw); } fclose(fpw); } //关闭文件流 fclose(fpr); //释放动态内存 i = 0; for(; i < file_num; i++){ free(patches[i]); } free(patches); (*env)->ReleaseStringUTFChars(env,path_jstr,path); (*env)->ReleaseStringUTFChars(env,path_pattern_jstr,path_pattern);}
合并文件
JNIEXPORT void JNICALL Java_com_handsome_ndkdemo_Utils_FileUtils_patch (JNIEnv *env, jclass jcls,jstring path_pattern_jstr, jint file_num,jstring merge_path_jstr){ const char* merge_path = (*env)->GetStringUTFChars(env,merge_path_jstr,NULL); const char* path_pattern = (*env)->GetStringUTFChars(env,path_pattern_jstr,NULL); //得到分割之后的子文件的路径列表 char **patches = malloc(sizeof(char*) * file_num); int i = 0; for (; i < file_num; i++) { patches[i] = malloc(sizeof(char) * 100); //元素赋值 sprintf(patches[i], path_pattern, (i+1)); LOGI("patch path:%s",patches[i]); } FILE *fpw = fopen(merge_path,"wb"); //把所有的分割文件读取一遍,写入一个总的文件中 i = 0; for(; i < file_num; i++){ int filesize = get_file_size(patches[i]); FILE *fpr = fopen(patches[i], "rb"); int j = 0; for (; j < filesize; j++) { fputc(fgetc(fpr),fpw); } fclose(fpr); } //关闭文件流 fclose(fpw); //释放动态内存 i = 0; for(; i < file_num; i++){ free(patches[i]); } free(patches); (*env)->ReleaseStringUTFChars(env,path_pattern_jstr,path_pattern); (*env)->ReleaseStringUTFChars(env,merge_path_jstr,merge_path);}
阅读全文
2 0
- NDK开发——JNI必须掌握的常用例子
- 一个例子掌握JNI开发
- Android JNI必须掌握的五点
- NDK开发学习笔记—JNI入门
- 使用eclipse开发ndk之:导入现有的ndk工程(NDK例子-Hello-jni导入到eclipse)
- 使用eclipse开发ndk之:导入现有的ndk工程(NDK例子-Hello-jni导入到eclipse)
- 使用eclipse开发ndk之:导入现有的ndk工程(NDK例子-Hello-jni导入到eclipse)(转载)
- Android Jni 例子,ndk
- 必须掌握的常用算法之一
- 必须掌握的常用Linux命令
- 必须掌握的C++常用关键字汇总
- 必须掌握的C++常用String函数
- Android开发环境部署—— Eclipse+NDK for Android JNI 的开发环境(linux 环境)
- Android中使用NDK环境开发JNI程序例子
- 前端开发必须掌握的一些知识点
- AS3开发必须掌握的内容
- VC++ 窗体开发必须掌握的技巧
- JNI/NDK开发指南(三)——JNI数据类型及与Java数据类型的映射关系
- Ubuntu下编译VLC Android源码so库详解
- Tomcat编译JSP页面生成Servlet文件(*.class & *.java)的存放位置
- Codeforces Round #430 (Div. 2) C Ilya And The Tree SET暴力
- Pycharm界面的子窗口不见了,怎么办?
- 第十二课记录 Redis
- NDK开发——JNI必须掌握的常用例子
- mysql导入dmp文件
- 仿照springmvc 手打框架
- 2017广西邀请赛/hdu6186 CS Course (维护前缀和后缀)
- cmd打开mysql的正确方式
- 欢迎使用CSDN-markdown编辑器
- js中apply与call的区别
- SQL调优与SQL优化(SQL Tuning or SQL Optimization)
- linux命令缩写