androidstudio2.3 jni cmake 链接外部库问题

android studio 2.2之后出的CMake 让jni的编写方便了很多,使用CMake让我们不在烦恼函数的定义,以前我们需要通过javah命令生成,jni规定的函数名,现在不需要了。他也让我们可以很方便的编写c/c++代码,自动打成so。总体来说,让我们的jni编写变得更简单。但是网上关于CMake的使用翻来覆去也就是官网的那些。所以我就记录一下自己在使用CMake进行jni编译过程中遇到的问题。
      CMake的使用请见android中使用CMake,这里就不讲使用了,官方的说明里讲的很清楚,我这里就说一下常见问题的出现原因。

      1、如果我们想要在自己的c/c++代码中使用一些第三方库的函数,比如ffmpeg。我们可以通过add_library来添加相关依赖(官网教程里面有详细说明)。但是我们打包好的ffmpeg的so库
   <Image_1>
      应该放在哪里呢? 通常存放so库,我们是放在这两个位置的,那么到底应该放哪里呢?其实两个都是可以的,只不过你需要在gradle和CMakeLists.txt里面说明
    <Image_2>
     不然有可能会出现以下问题
      A、编译能通过,但是安装的时候会报 java.lang.UnSatisfiedLinkError : dlopen failed : library
这种情况,你需要在build.gradle里面添加如下代码
如果你是放在libs文件夹
  1. sourceSets.main {  
  2.     jniLibs.srcDirs = ['libs']  
  3.     jni.srcDirs = []  
  4. }  
             如果你是放在jniLibs文件夹
  1. sourceSets.main {  
  2.     jniLibs.srcDirs = ['src/main/jniLibs']  
  3.     jni.srcDirs = []  
  4. }  

        B、编译的时候不通过,报错 error: xxx.so,needed by xxxx.so,missing and no known rule to make it
<Image_3>
       这个错误的意思是你生成 xxxx.so的时候,需要xxx.so库,但是没有找到,其实在这里就是路径的问题,在CMake的使用中,可以通过add_library依赖第三方库
  1. add_library(avcodec SHARED IMPORTED)  
  2. set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION  ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec-57.so)  
       上面代码avcodec 其实就是依赖库的名字,可以随便取,但是下面set_target_properties的第一个参数一定要和上面统一,具体的参数含义就不在多说了,后面的
  1. ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec-57.so  
     就是你依赖的so库的存放路径,${CMAKE_SOURCE_DIR} 其实就是CMakeLists.txt文件所在文件夹,${ANDROID_ABI}其实就是你编译的手机的cpu架构,比如armeabi、X86、mips64等等,编译的时候,它会自动去找libs的对应文件夹,如果找不到,就会报这个错误。现在大部分手机都是armeabi架构,模拟器是x86架构,所以如果出现这个错误,需要检查一下自己so库是否是对应版本,以及是否存放在了对应文件夹下。
      如果放在libs下面,就需要如下写
  1. set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION  ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libavcodec-57.so)  
      对应如果在jniLibs下面,就如下
  1. 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的时候写错了,比如依赖库。
  1. add_library(avcodec SHARED IMPORTED)  
  2. set_target_properties(avcodec PROPERTIES IMPORTED_LOCATION  ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so)  
      link库,这里我们故意少写了一个c,
  1. target_link_libraries( # Specifies the target library.  
  2.                    native-lib  
  3.                     avcode                      
  4. }  
      如果在我们自己的c文件里面用到了libavcodec-57.so里面的函数,那么会出现编译不通过  undefined reference to 'xxxxxx',这是我们在c文件用到了某个so库里面的函数,但是并没有进行对应so库的依赖。
<Image_4>

      2、还是依赖第三方库的问题,如果你出现了missing and no known rule to make it这个error,但是按照上面的方法并没有解决,如下错误
<Image_5>
         看起来,好像跟上面的错误很相似,也是在build  我们自己的libnative-lib.so这个库的时候,他需要依赖第三方库,但是没依赖成功,但是其实注意红色箭头指示的点,mips64??为什么会出现这个文件夹??其实使用jni编译的时候,在他讲我们的c文件打包成so库的时候,如果我们没有指定打包成哪种架构的so库,他默认是会进行所有的打包的(个人猜测),也就是在打mips64这个架构的so库的时候,他去对应地方找依赖的so库,发现没有找到,就会报上面的错误,这个时候,如果我们 只支持armeabi架构,那么需要在build.gradle文件defaultConfig中添加如下代码
  1. externalNativeBuild {  
  2.     cmake {  
  3.          cppFlags ""  
  4.          abiFilters 'armeabi'//, 'x86', 'armeabi-v7a', 'x86_64', 'arm64-v8a'  
  5.     }  
  6. }  
        其中abiFilters就指定了需要打哪种架构的so库,

      3、同样是依赖第三方库,编译的时候,发现找不到使用的第三方库里面的某个函数,也就是undefined reference to “某某函数”,但是又不是上面的那个原因。
<Image_6>
       我们的cpp代码如下
  1. #include <jni.h>  
  2. #include <string>  
  3. #include <libavformat/avformat.h>  
  4. #include <libavcodec/avcodec.h>  
  5. extern "C"  
  6. {  
  7.     jstring  
  8.     Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromFF(JNIEnv  *env, jobject ) {  
  9.         char info[10000] = { 0 };  
  10.         av_register_all();  
  11.         sprintf(info, "%s\n", avcodec_configuration());  
  12.         return env -> NewStringUTF(info);  
  13.     }  
  14.     jstring  
  15.     Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromJNI(  
  16.         JNIEnv *env,  
  17.         jobject /* this */) {  
  18.         std::string hello = "Hello from C++";  
  19.         return env->NewStringUTF(hello.c_str());  
  20.     }  
  21. }  
      看上去没有什么问题。 我们使用了libavformat.so里面的函数av_regist_all(),但是缺报了函数未定义的错误,然而我们明明已经依赖了第三方的so库和相关头文件,这里有一个需要注意的地方就是,我们引入的这个第三方库是ffmpeg,该库需要用c编译器来编译,所以有一个
  1. extern "C"  
      但是,这里include的位置不对,我们需要放在extern "C"的代码块里面,经过如下修改
  1. #include <jni.h>  
  2. #include <string>  
  3. extern "C"  
  4. {  
  5. #include <libavformat/avformat.h>  
  6. #include <libavcodec/avcodec.h>  
  7.     jstring  
  8.     Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromFF(JNIEnv  *env, jobject ) {  
  9.         char info[10000] = { 0 };  
  10.         av_register_all();  
  11.         sprintf(info, "%s\n", avcodec_configuration());  
  12.         return env -> NewStringUTF(info);  
  13.     }  
  14.     jstring  
  15.     Java_com_example_lenovo_ffmpegdemo_FFmpegUtils_stringFromJNI(  
  16.         JNIEnv *env,  
  17.         jobject /* this */) {  
  18.         std::string hello = "Hello from C++";  
  19.         return env->NewStringUTF(hello.c_str());  
  20.     }  
     就发现编译通过了,可以正常运行了(这是非常坑的一个点)。所以如果出现了这样的报错,而CMake的依赖第三个库和引入头文件都没有问题的话,记得检查一下是否是编译器的声明问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值