Android NDK开发二 cmake脚本编写
来源:互联网 发布:java action是什么 编辑:程序博客网 时间:2024/05/24 06:55
1 前言
上一篇已经简单的介绍了Cmake的基本使用以及编写规则了,这一篇博客我们简单介绍在NDK开发中常见的一些场景时如何编写cmake
2 NDK中cmake的使用场景
一般在NDK开发中,我们编译so库或者使用外部的so库,cmake的编写规则场景主要有以下几种。
1 cmake 编译单个c/cpp文件为so
这种单个的c/cpp文件一般为JNI层的文件,例如在我们新建一个支持C++的工程中native-lib.cpp:
#include <jni.h>#include <string>extern "C"jstringJava_com_qiyei_essayjoke_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str());}
关于这里我们将native-lib.cpp编译成libnative-lib.so,我们只需要在cMakeLists.txt中如下配置:
add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). # Associated headers in the same location as their source # file are automatically included. src/main/cpp/native-lib.cpp )
这里add_library就表示添加一个库,native-lib表示库名称,可以随便取,SHARED表示添加的动态库,后面的src/main/cpp/native-lib.cpp表示要编译成native-lib库的源文件
2 使用外部的so库 + c/cpp
这种使用的场景很多,在NDK开发中,我们往往会使用别人现成的so库,我们在编写自己的c/cpp代码时,需要调用别人so库的函数,这个时候的cmake就需要如下编写了。以使用libjpeg.so为例,先来看我们自己的c/cpp代码:
compress_image.h
/** * Email: 1273482124@qq.com * Created by qiyei2015 on 2017/7/23. * Version: 1.0 * Description: */#ifndef ESSAYJOKE_COMPRESS_IMAGE_H#define ESSAYJOKE_COMPRESS_IMAGE_H#include <jni.h>#include <string>/** * Log打印 */#define LOG_TAG "jni"#define LOG_W(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)#define LOG_E(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)#define true 1#define false 0/** * 统一编译方式 */#ifdef __cplusplusextern "C"{#endif#include "jpeg/jpeglib.h"#include "jpeg/cdjpeg.h" /* Common decls for cjpeg/djpeg applications */#include "jpeg/jversion.h" /* for version message */#include "jpeg/jconfig.h"JNIEXPORT jint JNICALL Java_com_qiyei_sdk_util_ImageUtil_jpegCompressBitmap(JNIEnv *env, jclass type, jobject bitmap,jint quality, jstring path_);#ifdef __cplusplus};#endif#endif //ESSAYJOKE_COMPRESS_IMAGE_H
compress_image.cpp
/** * Email: 1273482124@qq.com * Created by qiyei2015 on 2017/7/23. * Version: 1.0 * Description: */#include "compress_image.h"#include <string.h>#include <android/bitmap.h>#include <android/log.h>#include <stdio.h>#include <setjmp.h>#include <math.h>#include <stdint.h>#include <time.h>/** * 定义BYTE 类型为u_int8_t */typedef u_int8_t BYTE;char *error;/** * error 结构体 */struct error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer;};/** * 结构体类型定义 */typedef struct error_mgr *error_ptr;/** * 错误退出函数 */METHODDEF(void) error_exit(j_common_ptr info){ //获取j_common_ptr中的error error_ptr err = (error_ptr)info->err; //调用error中的output_message输出打印信息 (*info->err->output_message)(info); error = (char *)err->pub.jpeg_message_table[err->pub.msg_code]; LOG_E("jpeg error_exit,jpeg_message_table[%d]:%s",err->pub.msg_code,err->pub.jpeg_message_table[err->pub.msg_code]); // longjmp(err->setjmp_buffer,1);}/** * jpeg压缩图片 */jint compress_jpeg(BYTE * data,int width,int height,int quality,jboolean optimize,char * file_name){ struct jpeg_compress_struct jcs; //当读完整个文件的时候就会回调my_error_exit这个退出方法。 struct error_mgr jpeg_err; jcs.err = jpeg_std_error(&jpeg_err.pub); jpeg_err.pub.error_exit = error_exit; //setjmp是洗衣歌系统级函数,是一个回调 if (setjmp(jpeg_err.setjmp_buffer)){ return -1; } //初始化jcs结构体 jpeg_create_compress(&jcs); //打开输出文件 wb 可写 rb 可读,最终会把压缩的图像保存到该文件中 FILE *f = fopen(file_name,"wb"); if ( f == NULL){ return -1; } //设置jcs的文件路径以及宽高 jpeg_stdio_dest(&jcs,f); jcs.image_width = width; jcs.image_height = height; jcs.arith_code = false; //true 算数编码,flase 霍夫曼编码 ///* 颜色的组成 rgb,三个 # of color components in input image */ int nComponent = 3; jcs.input_components = nComponent; //颜色组成RGB jcs.in_color_space = JCS_RGB; jpeg_set_defaults(&jcs); //是否采用霍夫曼编码 jcs.optimize_coding = optimize; //设置质量 jpeg_set_quality(&jcs,quality,true); //开始压缩 jpeg_start_compress(&jcs,TRUE); JSAMPROW row_pointer[1]; int row_stride = jcs.image_width * nComponent; //依次按照每一行循环扫描 while (jcs.next_scanline < jcs.image_height){ //得到一行的首地址。然后写入 row_pointer[0] = &data[jcs.next_scanline * row_stride]; jpeg_write_scanlines(&jcs,row_pointer,1); } //压缩结束 jpeg_finish_compress(&jcs); //销毁回收内存 jpeg_destroy_compress(&jcs); //关闭文件 fclose(f); return 0;}JNIEXPORT jint JNICALLJava_com_qiyei_sdk_util_ImageUtil_jpegCompressBitmap(JNIEnv *env, jclass type, jobject bitmap, jint quality, jstring path_) { //1. 解析RGB //1.1 获取bitmap信息,w,h.format AndroidBitmapInfo bitmapInfo; //java你调用完方法往往返回的是对象,而C往往是参数 AndroidBitmap_getInfo(env,bitmap,&bitmapInfo); //获取bitmap信息 int bitmap_width = bitmapInfo.width; int bitmap_height = bitmapInfo.height; int bitmap_format = bitmapInfo.format; //对于不是ARGB8888格式的图片不支持 if (bitmap_format != ANDROID_BITMAP_FORMAT_RGBA_8888){ return -1; } LOG_W("bitmap,width = %d,height = %d",bitmap_width,bitmap_height); //1.2 把bitmap解析到数组中,数组中保存的是rgb --> YCbCr //1.2.2 锁定画布 BYTE * pixel_color; //将bitmap数组导出到pixel_color中 AndroidBitmap_lockPixels(env,bitmap,(void **) &pixel_color); //1.2.2 解析数据 BYTE *data; BYTE r,g,b; //申请内存,因为不需要保存A通道,所以只需要申请 bitmap_width * bitmap_height * 3 data = (BYTE *) malloc(bitmap_width * bitmap_height * 3); //数组指针指向首地址,因为这块内存需要释放,所以先保存下 BYTE * head = data; int i = 0; int j = 0; int color; for (i = 0 ; i < bitmap_width ;i++){ for (j = 0;j < bitmap_height;j++){ color = *((int *)pixel_color); //将rgb取出来 ARGB 每个字节占八位 r = (color & 0x00FF0000) >> 16; g = (color & 0x0000FF00) >> 8; b = (color & 0x000000FF) >> 0; //保存到data中 *data = b; *(data + 1) = g; *(data + 2) = r; data = data + 3; // 一个像素点包括argb四个值,每+4下就是取下一个像素点 pixel_color = pixel_color + 4; } } //1.2.3 解锁画布 AndroidBitmap_unlockPixels(env,bitmap); //1.2.4 还差一个参数,jstring -> char* char * file_name = (char *)env->GetStringUTFChars(path_,NULL); int result = compress_jpeg(head,bitmap_width,bitmap_height,quality, true,file_name); //释放内存 free(head); env->ReleaseStringUTFChars(path_,file_name); //释放bitmap,调用bitmap的recycle //找到Bitmap类 jclass clazz = env->GetObjectClass(bitmap); jmethodID jmethod_ID = env->GetMethodID(clazz,"recycle","()V"); //调用Bitmap的recycle()方法释放对象 env->CallVoidMethod(bitmap,jmethod_ID); LOG_W("result = %d",result); //4 返回结果 return result;}
比如,在compress_image.cpp中我们使用大量的libjpeg.so库中的方法,例如jpeg_set_quality,jpeg_start_compress等。这个时候我们的cmake一般来说需要做如下修改:
#设置生成的so动态库最后输出的路径set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI})add_library( # Sets the name of the library. compressimg # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). # Associated headers in the same location as their source # file are automatically included. src/main/cpp/compress_image.cpp )# 添加libjpeg.so依赖库add_library(jpeg SHARED IMPORTED)#设置libjpeg.so依赖库路径set_target_properties( jpeg PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpeg.so)
解释如下:
1 还是添加compressimg.so,源文件为src/main/cpp/compress_image.cpp
2 添加jpeg.so,因为是导入别人的,因此是IMPORTED
3 设置查找jpeg.so的路径 “${PROJECT_SOURCE_DIR}/libs/${ANDROID_ABI}/libjpeg.so”,其中${PROJECT_SOURCE_DIR}表示工程源文件目录,与src文件这一目录同级,${ANDROID_ABI}表示armeabi, armeabi-v7a,arm64-v8a等目录
4 最后将jpeg.so库链接到compressimg.so中
3 多个c/cpp文件编译成so库
这种情况也比较多见,一般来说如果我们下载了某个so的源码,或者我们编写了多个c/cpp文件 ,需要将这些源文件编译成一个so库,这个情况实际和第一种差不多。
例如,我们需要将以下源文件编译成so库
这个时候我们的cmake如下:
#配置头文件路径#include_directories(${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/)#添加libbspatch 库add_library( # Sets the name of the library. bspatch SHARED ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/huffman.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/randtable.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/crctable.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/blocksort.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/bzip2.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/compress.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/decompress.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/bzlib.c ${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/bspatch.c)
解释如下:
1 include_directories添加头文件,主要告诉cmake,去哪个目录下找对应的头文件
2 由于我这里不需要将${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/文件夹下所有的c/cpp文件编译成so库,因此我将文件单独添加,实际中不建议这么做,可使用变量将某个路径全部编译,详情请见下一节内容
3 cmake常用脚本
在编写cmake的过程中,我们往往会发现一些特别有用的脚本或者变量,现在记录下来,以备不时之需,也会做不定时的更新。
1 cmake中常用的变量
STATIC: 表示静态的.a的库。
SHARED: 表示.so的库,动态库
CMAKE_LIBRARY_OUTPUT_DIRECTORY: so库或者静态库的最后输出路径
${ANDROID_NDK}: Android Studio已经定义好的变量,可以直接使用它指定的是NDK源代码的根目录
${PROJECT_SOURCE_DIR}: 表示工程源文件目录,与src文件这一目录同级
${ANDROID_ABI}: 表示armeabi, armeabi-v7a,arm64-v8a等目录
${CMAKE_SOURCE_DIR}: 表示CMakeLists.txt的当前文件夹路径。
2 cmake中常用的脚本
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY path)
设置生成的so动态库最后输出的路径
include_directories(path)
配置头文件搜索路径
#添加源文件路径aux_source_directory(${PROJECT_SOURCE_DIR}/src/main/cpp/bsdiff/ CORE_SRC_LIST)list(APPEND SRC_LIST ${CORE_SRC_LIST})
添加源文件路径,这样${SRC_LIST}就表示src/main/cpp/bsdiff/目录了
set_target_properties( xxx PROPERTIES IMPORTED_LOCATION path)
设置libxxx.so依赖库的路径
- Android NDK开发二 cmake脚本编写
- Android NDK 开发:CMake 使用
- Android Studio2.2 CMAKE高效NDK开发
- 使用CMake来进行Android NDK开发
- Android Studio配置CMake开发NDK
- 使用CMake来进行Android NDK开发
- 使用CMake来进行Android NDK开发
- AndroidStudio之NDK开发CMake CMakeLists.txt编写入门
- android ndk开发二
- Android NDK开发一 NDK环境搭建及cmake简介
- cmake脚本编写
- NDK开发--CMake篇
- nmake + cmake + android ndk
- Android NDK开发技巧二
- Android NDK开发技巧二
- Android NDK开发技巧二
- Android NDK开发技巧二
- Android NDK开发技巧二
- jquery的日历插件使用 jeDate
- 数据结构 图论
- 数据库中元数据MetaData
- 指定等级(一维数组实例)
- 泰勒公式的展开细节解析
- Android NDK开发二 cmake脚本编写
- 同步方法与同步块的区别
- 使用QSharedDataPointer报incomplete-type错误
- Super Jumping! Jumping! Jumping! HDU
- 算法之快速排序
- Squid.conf配置文件详解
- std::function 与 std::bind 的基础使用
- ImportError: No module named incremental
- jps 查看HBase 启动的进程