Android Studio2.2配置MakeList使用cmake编译c文件
2017-02-21 21:59
441 查看
初次使用cmake来编译c文件在Android项目中,那个快感能传递到骨髓里,欲罢不能.只能说谁用谁知道.如果还没有使用,请赶快用一次,初次体验一共三部:
第一:studio升级到2.2以后的版本,安装NDK,cmake工具
第二:创建新项目勾选 Include C++ Support 选项。
第三:一路下一步,安装运行项目
studio会默认帮你创建一个写好的demo更加详细的请参考:http://blog.csdn.net/wl9739/article/details/52607010或者google官网:https://developer.android.com/ndk/guides/build.html
我今天以自己学到的使用c代码压缩图片为例子,用cmake来编译这个项目来分享下学到的东西,如果使用cmake来编译项目一共有两种实现方式:
第一种就是上面那样,创建项目时勾选Include C++ Support 让系统生成配置,然后自己再添加c代码,
第二种是不是新建的项目,这时候要引用c代码,那就要自己去配置文件,本文就要讲后面这种实现方式.
先看看效果图:
![](https://img-blog.csdn.net/20170221223813793?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU1OTk5Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这两张是从相册倒出来的,压缩前压缩后的
![](https://img-blog.csdn.net/20170221223706088?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU1OTk5Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
右图原图1.67M 左图压缩后57.54K不到原来的1/10,不放大的情况,用肉眼很难分辨出来差别.
要把这个c代码图片压缩集成到项目中需要四部:
第一:准备2.2以上版本studio,配置安装ndk和cmake工具
![](https://img-blog.csdn.net/20170221225201811?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU1OTk5Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
第二:导入c++文件,修改方法名
![](https://img-blog.csdn.net/20170221225926679?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU1OTk5Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
在项目module目录下新建cpp文件夹,一定要注意不要建错地方了,最终绝对路径是app\src\main\cpp,把native-lib.cpp这个文件导入到你自己的cpp文件夹中
native-lib.cpp文件如下
然后导入NativeUtil工具类
这个时候会报错,cpp文件会报错是因为没有头文件,没有类库,nativeUtily也会报错是因为cpp文件中找不到方法
在nativeUtil那两个报错的方法上alt+enter生成方法,然后把在cpp文件中生成的方法名替换掉原来老的Java_com_example_c_1demo_NativeUtil_compressBitmaps(),这个nativy方法名 的构成是java_包名_类名_方法名的格式,换完保存的两个方法名然后要导入类库,cpp报错先不管
![](https://img-blog.csdn.net/20170221231618079?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU1OTk5Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
第三:导入类库,导入头文件
在project视图下复制lib文件夹下的头文件和类库到你自己的项目
![](https://img-blog.csdn.net/20170221232306784?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU1OTk5Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
第四:配置CMakelist.text,配置build.grandle
CMakelist.text原封不动的复制到app的根目录下
![](https://img-blog.csdn.net/20170221233548232?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcXFfMzU1OTk5Nzg=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
配置build.grandle
红色为添加到你自己的项目中的配置,然后sync同步项目,这时候就不会报错了.
项目demo下载:http://download.csdn.net/download/qq_35599978/9760191
第一:studio升级到2.2以后的版本,安装NDK,cmake工具
第二:创建新项目勾选 Include C++ Support 选项。
第三:一路下一步,安装运行项目
studio会默认帮你创建一个写好的demo更加详细的请参考:http://blog.csdn.net/wl9739/article/details/52607010或者google官网:https://developer.android.com/ndk/guides/build.html
我今天以自己学到的使用c代码压缩图片为例子,用cmake来编译这个项目来分享下学到的东西,如果使用cmake来编译项目一共有两种实现方式:
第一种就是上面那样,创建项目时勾选Include C++ Support 让系统生成配置,然后自己再添加c代码,
第二种是不是新建的项目,这时候要引用c代码,那就要自己去配置文件,本文就要讲后面这种实现方式.
先看看效果图:
这两张是从相册倒出来的,压缩前压缩后的
右图原图1.67M 左图压缩后57.54K不到原来的1/10,不放大的情况,用肉眼很难分辨出来差别.
要把这个c代码图片压缩集成到项目中需要四部:
第一:准备2.2以上版本studio,配置安装ndk和cmake工具
第二:导入c++文件,修改方法名
在项目module目录下新建cpp文件夹,一定要注意不要建错地方了,最终绝对路径是app\src\main\cpp,把native-lib.cpp这个文件导入到你自己的cpp文件夹中
native-lib.cpp文件如下
#include <stdio.h> #include <string> #include <android/bitmap.h> #include <android/log.h> #include <setjmp.h> #include <math.h> #include <stdint.h> #include <time.h> extern "C" { #include "jpeglib.h" #include "jversion.h" #include "cdjpeg.h" #include "android/config.h" #define LOG_TAG "jni" #define LOGW(...) __android_log_write(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__) #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__) #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__) #define true 1 #define false 0 typedef uint8_t BYTE; char *error; struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; }; typedef struct my_error_mgr *my_error_ptr; METHODDEF(void) my_error_exit(j_common_ptr cinfo) { my_error_ptr myerr = (my_error_ptr) cinfo->err; (*cinfo->err->output_message)(cinfo); error = (char *) myerr->pub.jpeg_message_table[myerr->pub.msg_code]; // LOGE("addon_message_table:%s", myerr->pub.addon_message_table); // LOGE("SIZEOF:%d",myerr->pub.msg_parm.i[0]); // LOGE("sizeof:%d",myerr->pub.msg_parm.i[1]); longjmp(myerr->setjmp_buffer, 1); } int generateJPEG(BYTE *data, int w, int h, int quality, const char *outfilename, jboolean optimize) { //jpeg的结构体,保存的比如宽、高、位深、图片格式等信息,相当于java的类 struct jpeg_compress_struct jcs; //当读完整个文件的时候就会回调my_error_exit这个退出方法。setjmp是一个系统级函数,是一个回调。 struct my_error_mgr jem; jcs.err = jpeg_std_error(&jem.pub); jem.pub.error_exit = my_error_exit; if (setjmp(jem.setjmp_buffer)) { return 0; } //初始化jsc结构体 jpeg_create_compress(&jcs); //打开输出文件 wb:可写byte FILE *f = fopen(outfilename, "wb"); if (f == NULL) { return 0; } //设置结构体的文件路径 jpeg_stdio_dest(&jcs, f); jcs.image_width = w;//设置宽高 jcs.image_height = h; // if (optimize) { // LOGI("optimize==ture"); // } else { // LOGI("optimize==false"); // } //看源码注释,设置哈夫曼编码:/* TRUE=arithmetic coding, FALSE=Huffman */ jcs.arith_code = false; int nComponent = 3; /* 颜色的组成 rgb,三个 # of color components in input image */ jcs.input_components = nComponent; //设置结构体的颜色空间为rgb jcs.in_color_space = JCS_RGB; // if (nComponent == 1) // jcs.in_color_space = JCS_GRAYSCALE; // else // jcs.in_color_space = JCS_RGB; //全部设置默认参数/* Default parameter setup for compression */ jpeg_set_defaults(&jcs); //是否采用哈弗曼表数据计算 品质相差5-10倍 jcs.optimize_coding = optimize; //设置质量 jpeg_set_quality(&jcs, quality, true); //开始压缩,(是否写入全部像素) jpeg_start_compress(&jcs, TRUE); JSAMPROW row_pointer[1]; int row_stride; //一行的rgb数量 row_stride = jcs.image_width * nComponent; //一行一行遍历 while (jcs.next_scanline < jcs.image_height) { //得到一行的首地址 row_pointer[0] = &data[jcs.next_scanline * row_stride]; //此方法会将jcs.next_scanline加1 jpeg_write_scanlines(&jcs, row_pointer, 1);//row_pointer就是一行的首地址,1:写入的行数 } jpeg_finish_compress(&jcs);//结束 jpeg_destroy_compress(&jcs);//销毁 回收内存 fclose(f);//关闭文件 return 1; } /** * byte数组转C的字符串 */ char *jstrinTostring(JNIEnv *env, jbyteArray barr) { char *rtn = NULL; jsize alen = env->GetArrayLength(barr); jbyte *ba = env->GetByteArrayElements(barr, 0); if (alen > 0) { rtn = (char *) malloc(alen + 1); memcpy(rtn, ba, alen); rtn[alen] = 0; } env->ReleaseByteArrayElements(barr, ba, 0); return rtn; } JNIEXPORT jstring JNICALL Java_com_example_c_1demo_NativeUtil_compressBitmap__Landroid_graphics_Bitmap_2IIILbyte_3_093_2Z( JNIEnv *env, jclass type, jobject bit, jint w, jint h, jint quality, jbyteArray fileNameBytes_, jboolean optimize) { jbyte *fileNameBytes = env->GetByteArrayElements(fileNameBytes_, NULL); // TODO env->ReleaseByteArrayElements(fileNameBytes_, fileNameBytes, 0); return env->NewStringUTF("1"); } JNIEXPORT jstring JNICALL Java_com_example_c_1demo_NativeUtil_compressBitmaps(JNIEnv *env, jclass type, jobject bitmapcolor, jint w, jint h, jint quality, jbyteArray fileNameStr, jboolean optimize) { jbyte *fileNameBytes = env->GetByteArrayElements(fileNameStr, NULL); BYTE *pixelscolor; //1.将bitmap里面的所有像素信息读取出来,并转换成RGB数据,保存到二维byte数组里面 //处理bitmap图形信息方法1 锁定画布 // AndroidBitmap_lockPixels(env, bitmapcolor, (void **) &pixelscolor); AndroidBitmap_lockPixels(env,bitmapcolor,(void **)&pixelscolor); //2.解析每一个像素点里面的rgb值(去掉alpha值),保存到一维数组data里面 BYTE *data; BYTE r, g, b; data = (BYTE *) malloc(w * h * 3);//每一个像素都有三个信息RGB BYTE *tmpdata; tmpdata = data;//临时保存data的首地址 int i = 0, j = 0; int color; for (i = 0; i < h; ++i) { for (j = 0; j < w; ++j) { //解决掉alpha //获取二维数组的每一个像素信息(四个部分a/r/g/b)的首地址 color = *((int *) pixelscolor);//通过地址取值 //0~255: // a = ((color & 0xFF000000) >> 24); r = ((color & 0x00FF0000) >> 16); g = ((color & 0x0000FF00) >> 8); b = ((color & 0x00000 c9fc 0FF)); //改值!!!----保存到data数据里面 *data = b; *(data + 1) = g; *(data + 2) = r; data = data + 3; //一个像素包括argb四个值,每+4就是取下一个像素点 pixelscolor += 4; } } //处理bitmap图形信息方法2 解锁 AndroidBitmap_unlockPixels(env, bitmapcolor); char *fileName = jstrinTostring(env, fileNameStr); //调用libjpeg核心方法实现压缩 int resultCode = generateJPEG(tmpdata, w, h, quality, fileName, optimize); if (resultCode == 0) { jstring result = env->NewStringUTF("-1"); return result; } env->ReleaseByteArrayElements(fileNameStr, fileNameBytes, 0); return env->NewStringUTF("11"); } }
然后导入NativeUtil工具类
package com.example.c_demo; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Matrix; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; /** * Created by Administrator on 2017/2/11 0011. */ public class NativeUtil { static { System.loadLibrary("native-lib"); System.loadLibrary("jpegbither"); } public static void compressBitmap(Bitmap image, String filePath) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); int options = 20; NativeUtil.saveBitmap(image, options, filePath, true); } public static void saveBitmap(Bitmap bit, int quality, String fileName, boolean optimize) { // String s= compressBitmap(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize); compressBitmaps(bit, bit.getWidth(), bit.getHeight(), quality, fileName.getBytes(), optimize); } public static native String compressBitmap(Bitmap bit, int w, int h, int quality, byte[] fileNameBytes, boolean optimize); public static native String compressBitmaps(Bitmap bit, int w, int h, int quality, byte[] fileNameBytes, boolean optimize); public static void compressBitmap(String curFilePath, String targetFilePath) { // 最大图片大小 150KB int maxSize = 150; //根据地址获取bitmap Bitmap result = getBitmapFromFile(curFilePath); // ByteArrayOutputStream baos = new ByteArrayOutputStream(); // // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中 int quality = 45; // result.compress(Bitmap.CompressFormat.JPEG, quality, baos); // // 循环判断如果压缩后图片是否大于100kb,大于继续压缩 // while (baos.toByteArray().length / 1024 > maxSize) { // // 重置baos即清空baos // baos.reset(); // // 每次都减少10 // quality -= 10; // // 这里压缩quality,把压缩后的数据存放到baos中 // result.compress(Bitmap.CompressFormat.JPEG, quality, baos); // } // JNI保存图片到SD卡 这个关键 NativeUtil.saveBitmap(result, quality, targetFilePath, true); // 释放Bitmap if (!result.isRecycled()) { result.recycle(); } } /** * 通过文件路径读获取Bitmap防止OOM以及解决图片旋转问题 * * @param filePath * @return */ public static Bitmap getBitmapFromFile(String filePath) { BitmapFactory.Options newOpts = new BitmapFactory.Options(); newOpts.inJustDecodeBounds = true;//只读边,不读内容 BitmapFactory.decodeFile(filePath, newOpts); int w = newOpts.outWidth; int h = newOpts.outHeight; // 获取尺寸压缩倍数 newOpts.inSampleSize = NativeUtil.getRatioSize(w, h); newOpts.inJustDecodeBounds = false;//读取所有内容 newOpts.inDither = false; newOpts.inPurgeable = true; newOpts.inInputShareable = true; newOpts.inTempStorage = new byte[32 * 1024]; Bitmap bitmap = null; File file = new File(filePath); FileInputStream fs = null; try { fs = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { if (fs != null) { bitmap = BitmapFactory.decodeFileDescriptor(fs.getFD(), null, newOpts); //旋转图片 int photoDegree = readPictureDegree(filePath); if (photoDegree != 0) { Matrix matrix = new Matrix(); matrix.postRotate(photoDegree); // 创建新的图片 bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); } } } catch (IOException e) { e.printStackTrace(); } finally { if (fs != null) { try { fs.close(); } catch (IOException e) { e.printStackTrace(); } } } return bitmap; } /** * 计算缩放比 * * @param bitWidth 当前图片宽度 * @param bitHeight 当前图片高度 * @return int 缩放比 * @author XiaoSai * @date 2016年3月21日 下午3:03:38 * @version V1.0.0 */ public static int getRatioSize(int bitWidth, int bitHeight) { // 图片最大分辨率 int imageHeight = 1280; int imageWidth = 768; // 缩放比 int ratio = 1; // 缩放比,由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可 // if (bitWidth > bitHeight && bitWidth > imageWidth) {//如果是横图 // // 如果图片宽度比高度大,以宽度为基准 // ratio = bitWidth / imageWidth; // } else if (bitWidth < bitHeight && bitHeight > imageHeight) {//如果是竖图 // // 如果图片高度比宽度大,以高度为基准 // ratio = bitHeight / imageHeight; // } int heightScale = bitWidth/imageHeight; int widthScale = bitHeight/imageWidth; // 三元运算符 取两者之间的大值 ratio = heightScale>widthScale?heightScale:widthScale; // 最小比率为1 if (ratio <= 0) ratio = 1; return ratio; } /** * * 读取图片属性:旋转的角度 * @param path 图片绝对路径 * @return degree旋转的角度 */ public static int readPictureDegree(String path) { int degree = 0; try { ExifInterface exifInterface = new ExifInterface(path); int orientation = exifInterface.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; } }
这个时候会报错,cpp文件会报错是因为没有头文件,没有类库,nativeUtily也会报错是因为cpp文件中找不到方法
在nativeUtil那两个报错的方法上alt+enter生成方法,然后把在cpp文件中生成的方法名替换掉原来老的Java_com_example_c_1demo_NativeUtil_compressBitmaps(),这个nativy方法名 的构成是java_包名_类名_方法名的格式,换完保存的两个方法名然后要导入类库,cpp报错先不管
第三:导入类库,导入头文件
在project视图下复制lib文件夹下的头文件和类库到你自己的项目
第四:配置CMakelist.text,配置build.grandle
CMakelist.text原封不动的复制到app的根目录下
# Sets the minimum version of CMake required to build the native # library. You should either keep the default value or only pass a # value of 3.4.0 or lower. cmake_minimum_required(VERSION 3.4.1) #引用已近有的库 find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log ) #资源文件夹的位置libs set(distribution_DIR ${CMAKE_SOURCE_DIR}/../../../../libs) #导入类库,只是作为引用,不编译 add_library( jpegbither SHARED IMPORTED ) #引用目标类库是本地类库位置在libs/armeabi-v7a/libjpegbither.so set_target_properties( jpegbither PROPERTIES IMPORTED_LOCATION ../../../../libs/armeabi-v7a/libjpegbither.so ) #添加类库位置在src/main/cpp/native-lib.cpp需要编译 add_library(native-lib SHARED src/main/cpp/native-lib.cpp ) #引入头文件目录位置 include_directories(libs/jpeg) #将预构建库与你本地库相关联 target_link_libraries( # Specifies the target library. native-lib jpegbither jnigraphics # Links the target library to the log library # included in the NDK. ${log-lib} )
配置build.grandle
红色为添加到你自己的项目中的配置,然后sync同步项目,这时候就不会报错了.
apply plugin: 'com.android.application' android { tasks.whenTaskAdded { task -> if (task.name.contains("lint")//如果instant run不生效,把clean这行干掉 ||task.name.equals("clean")//项目中有用到aidl则不可以舍弃这个任务 ||task.name.contains("Aidl")//用不到测试的时候就可以先关闭 ||task.name.contains("mockableAndroidJar") ||task.name.contains("UnitTest") ||task.name.contains("AndroidTest")//用不到NDK和JNI的也关闭掉 || task.name.contains("Ndk") || task.name.contains("Jni") ) { task.enabled = false } } compileSdkVersion 25 buildToolsVersion "25.0.2" defaultConfig { applicationId "com.example.c_demo" minSdkVersion 15 targetSdkVersion 25 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" externalNativeBuild { //讲这个配置复制到你的grandle文件 cmake { cppFlags "" } } ndk {//创建过滤器,只生成相关cpu相关的so库 abiFilters "armeabi", "armeabi-v7a" } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } externalNativeBuild {//添加cmake文件 cmake { path "CMakeLists.txt" } } sourceSets {//添加资源文件 main { jniLibs.srcDirs = ['libs'] } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { exclude group: 'com.android.support', module: 'support-annotations' }) compile 'com.android.support:appcompat-v7:25.1.1' testCompile 'junit:junit:4.12' }之后只需在MainActivity中要调用NativeUtil.compressBitmap(String curFilePath, String targetFilePath)传入图片的路径和需要保存的位置就行了.
new Thread(){ @Override public void run() { NativeUtil.compressBitmap(path, path1); getContentResolver().notifyChange(Uri.parse("content://pic"), null); } }.start();
项目demo下载:http://download.csdn.net/download/qq_35599978/9760191
相关文章推荐
- android studio 2.2 使用cmake编译NDK
- Android Studio 下配置JNI的使用 ---CMakeList【开篇一】
- Android studio 2.2 使用 cmake 编译 libpng
- android studio 2.2 使用cmake编译NDK
- 大型项目使用Automake/Autoconf完成编译配置
- 大型项目使用Automake/Autoconf完成编译配置
- 编译busybox时使用make menuconfig命令出现下列问题:recipe for target 'scripts/kconfig/lxdialog/checklist.o' failed
- 我的第一篇博客——Android Studio 2.2 NDK开发之Cmake 编译多个源文件的问题
- JNI在Android Studio 2.2中基本配置和使用
- 大型项目使用Automake/Autoconf完成编译配置
- 大型项目使用Automake/Autoconf完成编译配置
- 在ubuntu下使用cmake进行opencv的配置和Windows下进行使用cmake编译源代码
- 使用cMake跨iOS平台编译过程以及配置
- android studio 2.2使用cmake进行NDK编程
- 大型项目使用Automake/Autoconf完成编译配置
- 大型项目使用Automake/Autoconf完成编译配置(2)——步步为营
- [C] tcharall(让所有平台支持TCHAR)v1.1。源码托管到github、添加CMake编译配置文件、使用doxygen规范注释
- Cmake编译OpenCV和如何在VS工程中配置来使用编译后的OpenCV进行跟踪调试问题
- 使用Automake/Autoconf完成编译配置
- Android Studio 2.2 JNI编译及Rxjava使用初级