Android studio 2.2 开始支持 cmake 的方式进行 jni 开发, 我在另一篇 10.Eclipse 下 Ndk 开发 (FFMPEG 解码) 中写过 Eclipse 下编译 FFMPEG 的过程, 但是现在几乎没有人会使用 eclipse 作为 Android 开发工具了, 所以很有必要写一下使用 Android studio 进行 jni 开发的一般步骤.
首要确保两点: 第一, 你的 Android studio 是 2.2 及以版本, 第二, 在你的 sdk tools 中, 确保安装了 ndk,cmake,LLDB 这三个东西.
其中 LLDB 是一个高效的 c/c++ 的调试器, 目前 LLDB 也已经取代 GDB 成为 XCode 的默认调试器, 在 Android studio 中也可以使用.
CMake 则是一个跨平台的编译工具.
ndk 配置. PNG
那么我们如何进行开发呢, Android studio 中项目的目录结构又是怎样安排, 各有什么意义?
Android studio 下 cmake 编译 ndk
新建项目时, 添加 c++ 支持很简单, Android studio 为我们提供了一个选项(Include C++ support), 只需要勾选, 系统会为我们默认添加一些配置
那么如果是在旧项目上添加呢, 这就需要我们手动配置一下了
Cmake 配置文件目录. PNG
第一步, 创建 CMakeLists.txt 文件, 注意, 文件名必须这样设置, 大小写区分, 默认放在项目根目录下, 或者在其他位置, 只需要你在 build.gradle 中配置即可
第二步, 在项目根目录的 build.gradle 文件中配置
- Android {
- ......
- defaultConfig {
- ......
- // 指定要 ndk 需要兼容的架构(这样其他依赖包里 mips,x86,armeabi,ARM-v8 之类的 so 会被过滤掉)
- // 例如如果的的 so 只支持 ARM 平台, 那么就像下边这样设置, 如果还支持其他平台则再后边追加
- ndk{
- abiFilters 'armeabi'
- }
- }
- ......
- externalNativeBuild{
- // 配置 CMakeLists 文件地址
- cmake{
- path 'CMakeLists.txt'
- }
- }
- }
这里贴出来我的 CMakeLists.txt 配置文件
部分参数解析:
include_directories: 表示引入项目 so 文件所需的头文件的路径, 在配置文件中配置之后, 编写 c 代码的时候可以会在当前路径下查找头文件, 引入的路径可以少些一些层级
find_library: 引入内部已经支持的库文件
add_library: 引入外部添加的库文件
- cmake_minimum_required(VERSION 3.4.1)
- # 引入头文件位置
- include_directories(src/main/cpp/include/FFMPEG)
- include_directories(src/main/cpp/include/owner)
- find_library(
- Android-lib
- Android
- )
- find_library(
- log-lib
- log
- )
- find_library(
- jnigraphics-lib
- jnigraphics
- )
- # 自己的库
- add_library(
- newffmpeg
- SHARED
- src/main/cpp/ffmpeg_player.c
- )
- # 编解码(最重要的库)
- add_library(
- avcodec
- SHARED
- IMPORTED
- )
- # 指定编码库的位置
- set_target_properties(
- avcodec
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-56.so
- )
- # 设备信息
- add_library(
- avdevice
- SHARED
- IMPORTED
- )
- # 指定设备信息的位置
- set_target_properties(
- avdevice
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavdevice-56.so
- )
- # 滤镜特效处理库
- add_library(
- avfilter
- SHARED
- IMPORTED
- )
- # 指定滤镜库位置
- set_target_properties(
- avfilter
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavfilter-5.so
- )
- # 封装格式处理库
- add_library(
- avformat
- SHARED
- IMPORTED
- )
- # 指定格式库路径
- set_target_properties(
- avformat
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavformat-56.so
- )
- # 工具库(大部分库都需要这个库的支持)
- add_library(
- avutil
- SHARED
- IMPORTED
- )
- # 指定工具库路径
- set_target_properties(
- avutil
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavutil-54.so
- )
- # 后期处理
- add_library(
- postproc
- SHARED
- IMPORTED
- )
- # 指定后期处理库路径
- set_target_properties(
- postproc
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libpostproc-53.so
- )
- # 数据格式转换库
- add_library(
- swresample
- SHARED
- IMPORTED
- )
- # 指定库位置
- set_target_properties(
- swresample
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswresample-1.so
- )
- # 视频像素数据格式转换
- add_library(
- swscale
- SHARED
- IMPORTED
- )
- # 视频像素格式转换库位置
- set_target_properties(
- swscale
- PROPERTIES IMPORTED_LOCATION
- ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libswscale-3.so
- )
- # 将预构建库与本地库相连
- target_link_libraries(
- newffmpeg
- avcodec
- avdevice
- avfilter
- avformat
- avutil
- postproc
- swresample
- swscale
- ${Android-lib}
- ${jnigraphics-lib}
- ${log-lib}
- )
so 和 C 文件目录划分
配置文件搞好了, 接下来就要正式的引入编译好的 FFMPEG so 文件了, 如果不知道如何编译, 可以参考我之前的一片文章 09. 阿里云服务器 (Ubantu 系统) 配置 ndk 编译 ffmpeghttps://www.jianshu.com/p/826d2175f157, 我们在项目 main package 下创建 jniLibs 文件夹, 将编译好的 so 放入这里
so 目录. PNG
jniLibs 目录也是系统默认支持的, 你当然可以设置其他名字, 或者将目录放在别的位置, 但是要注意, 如果你这样做, 一定要在 build.gradle 中进行指定, 否则系统找不到这些文件
细心的你可能发现在上边的截图上还有一个 cpp 的目录, 这个目录是我们一般用来防止头文件和 c c++ 文件的, 系统默认不会创建这个目录, 也需要我们手动添加, 除非你在创建项目的时候就添加了 c++ support
这时候, 基本上过程已经接近尾声了, 我们来看一下都做了什么:
1. 创建 CMakeLists.txt 脚本文件
2. 在 build.gradle 中配置编译选项
3. 将 so 库加入 jniLibs 文件夹, 将. h 和 .c(c++)文件放入 cpp 目录
4. 还有一步, 我们要创建 native 方法, 生成头文件, 加载 so,
5. 最后一步就是调用了, 这一整个过程到此结束
- package com.rzm.ffmpeglibrary;
- public class FFmpegUtils {
- static{
- System.loadLibrary("newffmpeg");
- }
- public native static void decode(String input,String output);
- }
- #include "com_rzm_ffmpeglibrary_FFmpegUtils.h"
- /*
- * Class: com_rzm_ffmpeglibrary_FFmpegUtils
- * Method: decode
- * Signature: ()V
- */
- JNIEXPORT void JNICALL Java_com_rzm_ffmpeglibrary_FFmpegUtils_decode
- (JNIEnv *env, jclass jclazz,jstring input,jstring output){
- }
其实这里还有很多细节可以多说一点, 比如 javah 命令如何生成头文件, javap 命令如何获取方法或者属性的签名, 最重要的一点, FFMPEG 从编写脚本文件到开始编译, 这一整个过程如何走来, 这些基本上在之前的文章中有提到, 这里就不再多说了.
总结一下, 这里提到的每个过程, CMakeLists.txt 文件的编写才是重中之重, 也正是我想说的, 把脚本中每一个命令搞清楚, 在 ndk 开发的路上你就更近一步了.
来源: http://www.jianshu.com/p/850e47c28a6b