Android Studio的gradle下配置ndk,jni
有三种种方式,使用目前最新的CMake方式,使用ndk-build也就是Android.mk构建文件,还有只使用gradle简单配置的。
而目前Android Studio对CMake的支持要好得多,配置实际上更简单了,最重要的是使用cmake是支持断点调试的,并且可以使用Android Studio的智能代码提示和各种编辑功能。
这样native开发效率可以提升数倍,而另外的方式是不行的,所以基本上建议新的项目放弃其他方式,使用cmake。但有个小问题是目前网上资料少,遇到问题解决可能麻烦一些。
使用CMake:
分两种情况,新项目中使用,以及在旧项目中添加CMake支持:
官网上有相应的教程,是汉化版,操作并不太复杂,可以直接看。
https://developer.android.com/studio/projects/add-native-code.html
然后根据上面教程去学会几个Android Studio下cmake的常用命令,下面也有按自己的理解的介绍,只有几种,记住就好了,不会花太多时间。
另外想要进行NDK开发,最好要通过官网或其他途径了解native开发的相关知识,比如.a,.so库,ABI等等,以及JNI开发的一些知识。
(书籍《Android C++使用NDK.pdf》,书中Android.mk、工具使用等的知识不用看)不然很难入手。
1、关于CMake:先对cmake有个基本的了解,用起来会更有底气一些。因为目前在不同的平台上有不同的make工具,GNU Make,QT 的qmake,微软的msnMake等,一个库或代码项目如果想要在不同的平台上运行的话,就要针对不同的平台分别创建make脚本,然后分别make一次,而CMake就是为了解决这种繁琐的操作。为着跨平台而生的,编写好cmake的脚本后,他就会自动的为你生成不同平台的makefile,然后自动的make。
cmake的构建脚本就叫做CMakeList.txt,它的语法十分的简单:有命令,注释,空格组成。而命令有命令名称,小括号及参数组成。
命令有很多,基本命令是 cmake_minimum_required(VERSION x)表明最小的版本。
2、cmake下面一些具体的操作:
set(pathToProject F:\\androidProject\\BaoZouPTu)将项目目录放到一个方便的变量中,也可以不写,使用的时候直接写。
#支持-std=gnu++11 set(CMAKE_VERBOSE_MAKEFILE on) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=gnu++11")支持的C++的标准,这个要写上,不然一些C++的API比如STL中可能就无法使用
add_library(imageSynthesis SHARED ImageSynthesis.cpp NewColorTransfer.cpp MyUtil.cpp edgeBlur.cpp )
将自己写的源文件编译到一个库比如imageSynthsis中,类型为共享库,这里注意所有用到的源文件都必须编译 到库中,头文件不用写进去。然后可以有多个库,后面连接起来就行了。
target_link_libraries(imageSynthesis log android -ljnigraphics lib_opencv)将自己生成的库,系统的库链接到目标上。
加入第三方的库,这里以OpenCv为例:
#配置加载native依赖 include_directories(${pathToOpenCv}/sdk/native/jni/include) #动态方式加载 add_library(lib_opencv STATIC IMPORTED ) #引入libopencv_java3.so文件 set_target_properties(lib_opencv PROPERTIES IMPORTED_LOCATION ${pathToProject}/app/src/main/jniLibs/${ANDROID_ABI}/libopencv_java3.so )第一条命令包含库的头文件目录,
第二条命令 指定库名称;导入方式,这里注意 静态导入和动态导入区别:静态导入静态库时是将库里面的内容抠出来放到你的库中去,而另外三种则不会,他们会直接将库赋值到你的apk中,这可能到值体积过大。然后import表明是导入的库,而不是用源文件创建的库,在下一条命令导入
第三条命令 属性和导入需要的库的绝对路径,.so或.a都可以。第二个参数表示选取的哭的属性,不如如果导入的目录下有多个ABI对应的库,这里可以指定只导入哪些ABI种类的;然后根据头文件就可以在代码中使用了。
这里关于静态动态,体积的控制还有些需要探究的地方。
3、控制ABI的话:
需要在gradle中去配置:defaultconfig节点下面用它进行配置,貌似不能在cmake相关的地方控制
ndk { // Specifies the ABI configurations of your native // libraries Gradle should build and package with your APK. abiFilters 'armeabi' }
使用CMmake时的一到库中些坑:
(1)当编写了在Java代码中定义了一个本地native方法,然后快捷键让Android Studio自动生成native方法的实现时(CMake的优势),此方法要在外部调用,必须在方法前面加上一句
extern "C"
而Android Studio自动生成的方法是不会添加的,运行起来就是找不到实现。
当时遇到这个问题也是困惑了大半天,愣是找不出问题,想着自动生成的方法明明在那儿,怎么会没有实现呢,后来通过直接new 一个C++项目仔细看了不同才知道,这是目前 版本的一个坑吧,所以生成之后要自己手动在前面加上那句话。
(2)导入系统的库的时候,native的bitmap相关的api,头文件叫bitmap.h,然而并没有对应的bimap.x的库,它被包含在
-ljnigraphics
中,所以在target_link_libraries的时候要链接上它,否则相关的api无法使用。
(3)初期开发过程中一定严格按照步骤来,不然因为不太了解,编译过程中出了什么问题要找半天,很费时间。出现问题时仔细看请错误提示,对解决很有帮助的。
(4)然后就是C++的知识了,尤其是指针和引用的区别等,使用是我就经常遇到这两个没弄清半天找不出错误的情况。
Android.mk以及纯使用gradle的
(不推荐使用)
一、gradle中简单配置的:
首先配置好ndk工具,通过sdk manager下载即可,比较简单,这里略过。
1、首先声明native方法在某个类中:
public native String stringFromeJni();2、点击build下面构建或make project,生成内点的.class文件
3、接着在Android Studio的terminate终端cd 转到src/main/java文件夹下面
输入 javah -d ../jni <包名>.<所在类的类名> 回车
Javah系统会自动生成头文件,在jni目录下面。
(注意如果代码中包含中文,可能出现这个问题“错误: 编码UTF-8的不可映射字符”
这时在刚才的命令行中加入字符集指定
javah -d ../jni -encoding UTF-8 a.baozouptu.ptu.tietu.pictureSynthesis.PictureSynthesis
UTF-8 这个字符集是Android Studio右下角指明的,如果还不行的话,点击右下角转换其它字符集,再加到命令行中尝试
最后不行的话先去注释掉中文,生成头文件之后在加入之)
4、新建c源文件,然后复制头文件名作文新建c源文件的头文件库
#include在里面写上代码。JNIEXPORT jstring JNICALL Java_com_lgc_ndklearning_MainActivity_stringFromeJni (JNIEnv *env, jobject) { return env->NewStringUTF("hello jni"); }
6、配置gradle
在module的defaultConfig节点下面加上如下,指定abi,库的名称等
ndk{ moduleName "jnilib" ldLibs "log","z","m" abiFilters "armeabi","armeabi-v7a" }7、最后在gradle.properties文件末尾加上一句
android.useDeprecatedNdk=true8、使用这个native方法前,使用 如下
static { System.loadLibrary("hello-jni"); }加载共享库。
方法这样整个配置就完成了,上面的函数可以当做一般的方法调用的。
二、使用Android.mk文件。
使用Android.mk文件时,其他可以不变,需要改动的就两个地方
首先gradle删除第一种方式添加的ndk节点,添加如下内容,在defaultConfig节点后面
前面的必须删除,因为两种方式是冲突的,如果不是会出现很难处理的问题。
/* ndk{ moduleName "jnilib" ldLibs "log","z","m" abiFilters "armeabi","armeabi-v7a" }*/ } sourceSets.main { jni.srcDirs = [] } task ndkBuild(type: Exec, description: 'Compile JNI source via NDK') { commandLine 'F:\\Android\\sdk\\ndk-bundle\\ndk-build.cmd',//这里本地ndk的路径 'NDK_PROJECT_PATH=build/intermediates/ndk', 'NDK_LIBS_OUT=src/main/jniLibs', 'APP_BUILD_SCRIPT=src/main/jni/Android.mk', 'NDK_APPLICATION_MK=src/main/jni/Application.mk' } tasks.withType(JavaCompile){ compileTask->compileTask.dependsOn ndkBuild }
2.让后在生成的jni目录下面新建Android.mk文件和Application.mk两个文件,添加如下内容
Android.mk
LOCAL_PATH :=$(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE :=hello-jni 生成的so库的名称 LOCAL_SRC_FILES :=com_lgc_ndklearning_MainActivity.cpp 源文件名 include $(BUILD_SHARED_LIBRARY)Application.mk
APP_ABI :=armeabi armeabi-v7a指定一下abi版本
这样就行了