Android Studio的gradle下配置ndk,jni

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=true
8、使用这个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版本

这样就行了

你可能感兴趣的:(ndk方面,gradle相关)