window环境和mac环境下的路径形式不一样。
给出我的配置代码(将生成的so文件放置到src/main/opencv/libs目录下)windows环境下commandLine里面ndk-build需要改成ndk-build.cmd:
android {
...
sourceSets.main.jni.srcDirs = []
sourceSets.main.jniLibs.srcDirs = ['src/main/opencv/libs','src/main/jniLibs']
task ndkBuild(type: Exec, description: 'Compile JNI source with NDK') {
def ndkDir = getNdkDir()
def mainDir = file('src/main').absolutePath
commandLine "$ndkDir/ndk-build", '-C', "$mainDir/jni", "NDK_LIBS_OUT=$mainDir/opencv/libs", "NDK_OUT=$mainDir/opencv/obj"
}
tasks.withType(JavaCompile) {
compileTask -> compileTask.dependsOn ndkBuild
}
task ndkClean(type: Exec, description: 'Clean NDK Binaries') {
def ndkDir = getNdkDir()
def mainDir = file('src/main').absolutePath
commandLine "$ndkDir/ndk-build", 'clean', '-C', "$mainDir/jni", "NDK_LIBS_OUT=$mainDir/opencv/libs", "NDK_OUT=$mainDir/opencv/obj"
}
clean.dependsOn 'ndkClean'
}
def getNdkDir() {
Properties properties = new Properties()
properties.load(project.rootProject.file('local.properties').newDataInputStream())
return properties.getProperty('ndk.dir')
}
某些library(如fresco)会在项目中生成so文件,为防止ndk打包生成的so包没有包含到library指定的ABIs,可以在主module下的build.gradle文件的defaultConfig中指定项目支持的ABIs。
这里已编写C代码为例,使用C打印出”hello from jni”字符串。
在主module的src/main文件夹下新建jni文件夹作为native代码存放目录,如果修改的话,需要同时在build.gradle的ndk配置中修改jni的地址。项目结构如图:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS :=-llog
LOCAL_MODULE = JniTest
LOCAL_SRC_FILES = com_rajesh_jnidemo_JniUtil.cpp
include $(BUILD_SHARED_LIBRARY)
LOCAL_PATH := $(call my-dir):
每个Android.mk文件必须以定义LOCAL_PATH为开始。它用于在开发tree中查找源文件。
宏my-dir 则由Build System提供。返回包含Android.mk的目录路径。
include $(CLEAR_VARS):
CLEAR_VARS 变量由Build System提供。并指向一个指定的GNU Makefile,由它负责清理很多LOCAL_xxx.
例如:LOCAL_MODULE, LOCAL_SRC_FILES, LOCAL_STATIC_LIBRARIES等等。但不清理LOCAL_PATH.
这个清理动作是必须的,因为所有的编译控制文件由同一个GNU Make解析和执行,其变量是全局的。所以清理后才能避免相互影响。
LOCAL_LDLIBS :=-llog:
可以用它来添加系统库
LOCAL_MODULE = JniTest:
LOCAL_MODULE模块必须定义,以表示Android.mk中的每一个模块。名字必须唯一且不包含空格。
Build System会自动添加适当的前缀和后缀。例如,foo,要产生动态库,则生成libfoo.so. 但请注意:如果模块名被定为:libfoo.则生成libfoo.so. 不再加前缀。
LOCAL_SRC_FILES:
LOCAL_SRC_FILES变量必须包含将要打包如模块的C/C++ 源码。
不必列出头文件,build System 会自动帮我们找出依赖文件。
include $(BUILD_SHARED_LIBRARY):
BUILD_SHARED_LIBRARY:是Build System提供的一个变量,指向一个GNU Makefile Script。
它负责收集自从上次调用 include $(CLEAR_VARS) 后的所有LOCAL_XXX信息。并决定编译为什么。
APP_STL := gnustl_static
APP_CPPFLAGS := -frtti -fexceptions
APP_OPTIM := debug
APP_ABI := armeabi
* | C++异常 | C++ RTTI | C++标准库 |
---|---|---|---|
system | 不支持 | 不支持 | 不支持 |
gabi++ | 不支持 | 支持 | 不支持 |
stlport | 不支持 | 支持 | 支持 |
gnustl | 支持 | 支持 | 支持 |
默认为system,如果想支持C++异常的话,必须要使用gunstl运行时库。
package com.rajesh.jnidemo;
/**
* Created by zhufeng on 2016/10/8.
*/
public class JniUtil {
static {
System.loadLibrary("JniTest");
}
public native String helloWorld();
}
在Terminal中cd进入到文件package所在目录javah命令生成头文件并移动到jni目录中。如图:
新建与头文件同名的cpp文件:
#include
#include
using namespace std;
#define TAG "zhufeng-jni" //定义Log的tag
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)//打印info类型的Log
extern "C" {
JNIEXPORT jstring JNICALL Java_com_rajesh_jnidemo_JniUtil_helloWorld(JNIEnv *env, jobject obj){
LOGI("测试Log打印");
return env->NewStringUTF((char *)"Hello from JNI!");
}
}
JniUtil jni = new JniUtil();
TextView.setText(jni.helloWorld());
* | 返回String |
---|---|
C | return (*env)->NewStringUTF(env,”Hello from JNI!”); |
C++ | return env->NewStringUTF((char *)”Hello from JNI!”); |
https://github.com/zhufeng1222/JniDemo