1.
创建一个Empty Activity的工程,命名为jniTest1,如果没有下载过ndk,那就那就去下载,选择菜单栏的 SDK Manager,
勾选上 Android SDK -> SDK Tools-> NDK ,然后点击Apply。
也可以自己下载到任意目录,然后修改local.properties(SDK Location) 文件,ndk.dir=你下载ndk的目录
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
sdk.dir=/Users/zhaoyan/Development/sdk
ndk.dir=/Users/zhaoyan/Development/android-ndk-r10b
android.useDeprecatedNdk=true
ndk {
moduleName "JniTest"
ldLibs "log", "z", "m"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
moduleName "JniTest"这是生成so的名字
ldLibs 是需要包含的本地库
abiFilters 显示指定支持的ABIs
然后在jni目录下创建一个c++文件 jniTest.cpp
然后bulid工程,就会工程目录下的jniTest1/app/build/intermediates/ndk/debug目录生成Android.mk文件以及so文件
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := JniTest
LOCAL_LDFLAGS := -Wl,--build-id
LOCAL_LDLIBS := \
-llog \
-lz \
-lm \
LOCAL_SRC_FILES := \
/Users/zhaoyan/code/test/jniTest1/app/src/main/jni/jnitest.cpp \
LOCAL_C_INCLUDES += /Users/zhaoyan/code/test/jniTest1/app/src/main/jni
LOCAL_C_INCLUDES += /Users/zhaoyan/code/test/jniTest1/app/src/debug/jni
include $(BUILD_SHARED_LIBRARY)
2.
创建一个java类myJNI
public class myJNI {
//加载so库
static {
System.loadLibrary("JniTest");
}
//native方法
public static native String sayHello();
}
加载的库名和配置build.gradle文件中写的moduleName名要相同
然后调出Terminal,位置在菜单的View->Tool Windows->Terminal,执行命令
cd /<工程目录>/app/build/intermediates/classes/debug/<包路径>
然后执行 ls命令,你会看到 myJNI.class 文件,然后执行命令
javap -s myJNI
pzhaoyandeMacBook-Pro:jnitest1 zhaoyan$ javap -s myJNI
Compiled from "myJNI.java"
public class com.wolf.jnitest1.myJNI extends java.lang.Object{
public com.wolf.jnitest1.myJNI();
Signature: ()V
public static native java.lang.String sayHello();
Signature: ()Ljava/lang/String;
static {};
Signature: ()V
}
()Ljava/lang/String; 一会儿动态注册函数的时候会用。
修改jnitest.cpp文件,添加如下代码
//
// Created by zhaoyan on 2016/11/27.
//
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <assert.h>
#include <android/log.h>
#define LOG_TAG "jnitest"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG,__VA_ARGS__)
static jstring jni_sayHello(JNIEnv *env, jobject obj)
{
return env->NewStringUTF("JNI sayHello");
}
/**
* JNINativeMethod由三部分组成:(1)Java中的函数名;
(2)函数签名,格式为(输入参数类型)返回值类型;
(3)native函数名
*/
static JNINativeMethod gMethods[] = {
{"sayHello", "()Ljava/lang/String;", (void *)jni_sayHello},
};
jint JNI_OnLoad(JavaVM *vm, void *reserved)
{
JNIEnv *env = NULL;
jint result = JNI_FALSE;
if (vm->GetEnv((void **)&env, JNI_VERSION_1_4) != JNI_OK) { ////从JavaVM获取JNIEnv
LOGD("Error GetEnv\n");
return result;
}
assert(env != NULL);
//获取类引用,这里可以找到要注册的类,前提是这个类已经加载到java虚拟机中
jclass clazz = env->FindClass("com/wolf/jnitest1/myJNI");
if (clazz == NULL) {
return result;
}
//注册方法,把本地函数和一个java类方法关联起来
if (env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) < 0) {
return result;
}
return JNI_VERSION_1_4;
}
JNI_OnLoad()函数。该函数在Java程序调用System.loadLibrary()时,被调用执行,所以会在这个函数里做一些初始化工作,与之相对应的是
JNI_OnUnload()函数,该函数会在so释放是调用,做一些清除工作,实际测试的时候app退出也没调用。
在java代码里调用
String str = myJNI.sayHello();
返回的字符串就是 JNI sayHello
这里有个需要注意的地方,那就是c 和 c++ 的对JNIEnv的定义不同
jclass clazz = env->FindClass("com/wolf/jnitest1/myJNI"); //c++
jclass cla = (*env)->FindClass(env, "com/wolf/jnitest1/myJNI"); // c
下面的错误就是我在cpp文件里写了c的代码。
Error:(51, 24) error: base operand of '->' has non-pointer type 'JNIEnv {aka _JNIEnv}'