android studio 2.2之后出的CMake 让jni的编写方便了很多,使用CMake让我们不在烦恼函数的定义,以前我们需要通过javah命令生成,jni规定的函数名,现在不需要了。他也让我们可以很方便的编写c/c++代码,自动打成so。总体来说,让我们的jni编写变得更简单。但是网上关于CMake的使用翻来覆去也就是官网的那些。所以我就记录一下自己在使用CMake进行jni编译过程中遇到的问题。
如果你是放在jniLibs文件夹
B、编译的时候不通过,报错 error: xxx.so,needed by xxxx.so,missing and no known rule to make it
这个错误的意思是你生成
xxxx.so的时候,需要xxx.so库,但是没有找到,其实在这里就是路径的问题,在CMake的使用中,可以通过add_library依赖第三方库
上面代码avcodec 其实就是依赖库的名字,可以随便取,但是下面set_target_properties的第一个参数一定要和上面统一,具体的参数含义就不在多说了,后面的
就是你依赖的so库的存放路径,${CMAKE_SOURCE_DIR} 其实就是CMakeLists.txt文件所在文件夹,${ANDROID_ABI}其实就是你编译的手机的cpu架构,比如armeabi、X86、mips64等等,编译的时候,它会自动去找libs的对应文件夹,如果找不到,就会报这个错误。现在大部分手机都是armeabi架构,模拟器是x86架构,所以如果出现这个错误,需要检查一下自己so库是否是对应版本,以及是否存放在了对应文件夹下。
如果放在libs下面,就需要如下写
对应如果在jniLibs下面,就如下
如果出现这个错误,记得多检查几次,自己的路径以及文件名是否写的正确的。
link库,这里我们故意少写了一个c,
如果在我们自己的c文件里面用到了libavcodec-57.so里面的函数,那么会出现编译不通过 undefined reference to 'xxxxxx',这是我们在c文件用到了某个so库里面的函数,但是并没有进行对应so库的依赖。
2、还是依赖第三方库的问题,如果你出现了missing and no known rule to make it这个error,但是按照上面的方法并没有解决,如下错误
其中abiFilters就指定了需要打哪种架构的so库,
看上去没有什么问题。
我们使用了libavformat.so里面的函数av_regist_all(),但是缺报了函数未定义的错误,然而我们明明已经依赖了第三方的so库和相关头文件,这里有一个需要注意的地方就是,我们引入的这个第三方库是ffmpeg,该库需要用c编译器来编译,所以有一个
但是,这里include的位置不对,我们需要放在extern "C"的代码块里面,经过如下修改
就发现编译通过了,可以正常运行了(这是非常坑的一个点)。所以如果出现了这样的报错,而CMake的依赖第三个库和引入头文件都没有问题的话,记得检查一下是否是编译器的声明问题。
CMake的使用请见android中使用CMake,这里就不讲使用了,官方的说明里讲的很清楚,我这里就说一下常见问题的出现原因。
1、如果我们想要在自己的c/c++代码中使用一些第三方库的函数,比如ffmpeg。我们可以通过add_library来添加相关依赖(官网教程里面有详细说明)。但是我们打包好的ffmpeg的so库
应该放在哪里呢? 通常存放so库,我们是放在这两个位置的,那么到底应该放哪里呢?其实两个都是可以的,只不过你需要在gradle和CMakeLists.txt里面说明
不然有可能会出现以下问题
A、编译能通过,但是安装的时候会报 java.lang.UnSatisfiedLinkError : dlopen failed : library
这种情况,你需要在build.gradle里面添加如下代码
如果你是放在libs文件夹
- sourceSets.main {
- jniLibs.srcDirs = ['libs']
- jni.srcDirs = []
- }
- sourceSets.main {
- jniLibs.srcDirs = ['src/main/jniLibs']
- jni.srcDirs = []
- }
- add_library(avcodec SHARED IMPORTED)
- set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec-57.so)
- ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec-57.so
如果放在libs下面,就需要如下写
- set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec-57.so)
- set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so)
上面说了avcodec其实只是你自定义的一个库的名字,但是你的add_library和set_target_properties以及进行link的target_link_libraries里面一定要保持一致如果我们在
target_link_libraries的时候写错了,比如依赖库。
- add_library(avcodec SHARED IMPORTED)
- set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so)
- target_link_libraries( # Specifies the target library.
- native-lib
- avcode
- }
2、还是依赖第三方库的问题,如果你出现了missing and no known rule to make it这个error,但是按照上面的方法并没有解决,如下错误
看起来,好像跟上面的错误很相似,也是在build 我们自己的libnative-lib.so这个库的时候,他需要依赖第三方库,但是没依赖成功,但是其实注意红色箭头指示的点,mips64??为什么会出现这个文件夹??其实使用jni编译的时候,在他讲我们的c文件打包成so库的时候,如果我们没有指定打包成哪种架构的so库,他默认是会进行所有的打包的(个人猜测),也就是在打mips64这个架构的so库的时候,他去对应地方找依赖的so库,发现没有找到,就会报上面的错误,这个时候,如果我们
只支持armeabi架构,那么需要在build.gradle文件defaultConfig中添加如下代码
- externalNativeBuild {
- cmake {
- cppFlags ""
- abiFilters 'armeabi'//, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'
- }
- }
3、同样是依赖第三方库,编译的时候,发现找不到使用的第三方库里面的某个函数,也就是undefined reference to “某某函数”,但是又不是上面的那个原因。
我们的cpp代码如下
- #include <jni.h>
- #include <string>
- #include <libavformat/avformat.h>
- #include <libavcodec/avcodec.h>
- extern "C"
- {
- jstring
- Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromFF(JNIEnv *env, jobject ) {
- char info[10000] = { 0 };
- av_register_all();
- sprintf(info, "%s\n", avcodec_configuration());
- return env -> NewStringUTF(info);
- }
- jstring
- Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromJNI(
- JNIEnv *env,
- jobject /* this */) {
- std::string hello = "Hello from C++";
- return env->NewStringUTF(hello.c_str());
- }
- }
- extern "C"
- #include <jni.h>
- #include <string>
- extern "C"
- {
- #include <libavformat/avformat.h>
- #include <libavcodec/avcodec.h>
- jstring
- Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromFF(JNIEnv *env, jobject ) {
- char info[10000] = { 0 };
- av_register_all();
- sprintf(info, "%s\n", avcodec_configuration());
- return env -> NewStringUTF(info);
- }
- jstring
- Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromJNI(
- JNIEnv *env,
- jobject /* this */) {
- std::string hello = "Hello from C++";
- return env->NewStringUTF(hello.c_str());
- }