接口文件的生成
在写好Java接口类后,需要先make project ,make失败尝试在工程目录下的gradle.properties
文件末尾加上android.useDeprecatedNdk=true
。
make project成功后,在终端中cd到项目src目录下的main目录,然后输入如下命令
javah -d jni -classpath ../../build/intermediates/classes/debug 包名+文件名
for example:
javah -d jni -classpath ../../build/intermediates/classes/debug org.example.ndktest
生成后的.h文件在main目录下的jni目录中
注意:
如果修改了Java中函数接口,需要重新编译一次再用以上命令生成的头文件格式才是正确的
生成接口后还需要通过NDK来生成.so库,这里有两种方式。
通过Android.mk
和Application.mk
配置文件来实现,两个文件的配置如下。
//Android.mk file
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := yourlibname
LOCAL_SRC_FILES := your c/c++ source file
LOCAL_LDLIBS += -lm -llog
include $(BUILD_SHARED_LIBRARY) //编译生成动态库
#include $(BUILD_STATIC_LIBRARY) //编译生产静态库
//Application.mk file
APP_OPTIM:=release
APP_STL:=stlport_static
#stlport_shared
APP_MODULES:=yourlibname
#for c++11, gnustl_static is bigger than stlport
#APP_STL := gnustl_static
#APP_CPPFLAGS += -std=c++11
APP_ABI := armeabi armeabi-v7a //指定编译架构
如是通过以上方法来生成so文件,需要手动使用ndk-build 命令的方式。
cd src/main/jni/
ndk-build
另外还有两个常用的ndk命令
ndk-build clean 清掉二进制文件
ndk-build NDK_DEBUG=1 编译为可调试版的二进制文件
ndk-build NDK_DEBUG=0 编译为release版
最后生成的so文件在main/libs下面。但是,Android调用so库的默认位置在jniLibs目录下,所以还需要修改调用so库的位置。具体的可在工程的build.gradle文件中添加如下示例代码。
sourceSets.main.jni.srcDirs = []
//禁止自带的ndk功能
sourceSets.main.jniLibs.srcDirs = ['src/main/libs','src/main/jniLibs'] // <-- 你的.so库的实际路径
对于方法一需要注意的是,若编译生成的是静态库,则.a库在app/src/main/obj/local
目录下。另外,方法一可以只编译纯C代码(无JNI层)。
在编写完Android.mk
和Application.mk
后如不想通过ndk-build命令来生成库,则可以在build.grad
配置文件中通过配置来编译so文件。配置如下。该方式生成的so文件在build/intermediates/ndk
目录下。
注意:根据网上的教程方法二貌似只能编译生成.so库(未100%求证),且必要要有jni层的接口才行。
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.1"
defaultConfig {
applicationId "***.***.**.**"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
ndk {
moduleName "yourlibname"
stl "stlport_static" //"gnustl_shared"
ldLibs "log" //"jnigraphics","gomp"
abiFilters "armeabi-v7a" , "arm64-v8a" //"armeabi"
//cFlags "-fopenmp","-std=c++11 -fexceptions"
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.1.1'
}
最后,检查导入库成功否的代码
static {
try{
System.loadLibrary("yourlibname");
}catch(UnsatisfiedLinkError ule){
System.err.println("WARNING: Could not load library testndk!");
}
}
遇到的坑Tips!!!!!!!!!!!
使用方法二编好库以后其他地方调用该so库,需要so库+对应的java接口文件。这个需要特别的注意的是java接口文件存放的目录一定需要时java文件中包名所indicate的目录。要不然会一直load不进去.so。这个问题折腾了我一晚上。。。被Android坑惨了。
根据笔者的经验,方法一能编译纯C源代码,也能编译纯C代码+JNI层代码,并且这两者对.so和.a库都通用;而方法二貌似只能编译生成.so库,且必须要有JNI层代码时才能编译出.so库。综上,笔者推荐建议使用ndk-build
方式。
Reference:
ndk-buil用法:http://blog.csdn.net/smfwuxiao/article/details/8523087
编译生产不同平台库:http://www.cnblogs.com/mengshu-lbq/archive/2013/03/29/2988657.html
静/动态库的混合编译和使用:http://blog.csdn.net/heng615975867/article/details/11904737