Android JNI 的实现包括两种实现方法:静态和动态。两种方法的区别如下:
静态:先由Java得到本地方法的声明“System.loadLibrary("hello_jni");”,然后再通过JNI实现该声明方法。
动态:先通过JNI重载JNI_OnLoad()实现本地方法,然后直接在Java中调用本地方法。JNI在加载时,会调用JNI_OnLoad,而卸载时会调用JNI_UnLoad,所以我们可以通过在JNI_OnLoad里面注册我们的native函数来实现JNI。需要先声明“public native String HelloLoad();”,再指明需要的库“System.loadLibrary("hello_jni");”。
静态方法实现步骤:
1.Java代码中添加库:
static
{
try {
System.loadLibrary("Hello_Jni");
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "Load Failed!");
}
}
/*
* 在Java中注册需要调用的C/C++本地方法(native method),也就是需要C/C++来实现的方法
*/
public native String sayHello();
2.运行工程,生成.class文件
3.通过javah生成JNI层头文件:
javah -classpath F:\workspace1\HelloJni\src -d C: com.example.hellojni.MainActivity
-classpath 工程的源文件路径
-d <dir> 输出目录
<classes> .java文件所在的完整路径
//每个class都会产生一个对应的*.h文件(所以一个Activity可能产生多个*.h文件),每个*.h文件命名格式固定:包名_类名.h
4 实现头文件中声明的函数
新建文件jni/hello_jni.c。hello_jni.c的代码如下:
#include <string.h>
#include<jni.h>
JNIEXPORT jstring JNICALL Java_com_example_hellojni_MainActivity_sayHello
(JNIEnv* env, jobject obj)
{
return env->NewStringUTF("Hello word");
}
hello_jni.c的作用就是实现com_example_hellojni_MainActivity.h中声明的函数。
5 编写实现函数对应的Android.mk
添加文件jni/Android.mk,内容如下:
LOCAL_PATH := $(call my-dir)
include$(CLEAR_VARS)
LOCAL_MODULE := hello_jni
LOCAL_SRC_FILES:= hello_jni.c
include$(BUILD_SHARED_LIBRARY)
6 编译生成库文件
进入到工程所在目录,执行ndk-build,编译生成.so库文件。
7 运行工程
在eclipse下运行工程,ok。
动态方法步骤:
1. 在java中添加
//jni中注册的方法
static final String TAG = "HelloJni2";
static
{
try {
System.loadLibrary("HelloJni2");
} catch (Exception e) {
// TODO: handle exception
Log.e(TAG, "Load Failed!");
}
}
public native String sayHello();
2. 在jni目录下新建HelloJni2.cpp:
- #include <jni.h>
#include <stddef.h>
#include <assert.h> -
- //添加打印log 在.mk文件中添加LOCAL_LDLIBS :=-llog
#include <android/log.h>
#define TAG "HelloJni2" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
//C++ 文件需要以C函数名导出
extern "C"
{
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT jstring JNICALL native_hello(JNIEnv* env, jobject object);
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved);
}
//findclass .要转成/
#define JNIREG_CLASS "com/example/hellojni2/MainActivity"
//Java和JNI函数的绑定表
JNINativeMethod method_table[] = {
{ "sayHello", "()Ljava/lang/String;", (void*)native_hello},//绑定
};
JNIEXPORT jstring JNICALL native_hello(JNIEnv* env, jobject object)
{
LOGI("native_hello");
jstring strTemp;
strTemp = env->NewStringUTF("hello dylib");
return strTemp;
}
int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
LOGI("registerNativeMethods");
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, method_table, numMethods) < 0) {
return JNI_FALSE;
}
return JNI_TRUE;
}
jboolean registerNatives(JNIEnv* env)
{
LOGI("registerNatives");
return registerNativeMethods(env,JNIREG_CLASS,method_table, sizeof(method_table)/sizeof(method_table[0]));
}
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
LOGI("JNI_OnLoad");
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
return -1;
}
assert(env != NULL); -
if (!registerNatives(env)) {//注册
return -1;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
return result;
}
JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved)
{
}
说明:JNI_OnLoad()会在JNI注册时被调用。在JNI_OnLoad()中,调用registerNatives()。registerNatives()调用registerNativeMethods()。registerNativeMethods()中通过FindClass()找到class;然后通过RegisterNatives()将method_table注册到class中。method_table是JNINativeMethod类型。
JNINativeMethod的定义如下:
typedef struct {
constchar*name; // Java中申明的Native函数名称
constchar* signature; // 描述了函数的参数和返回值
void* fnPtr; // 函数指针,指向C函数
} JNINativeMethod;
通过method_table,就将本地的native_hello()函数和注册到Java中的sayHello()绑定起来了。当我们在Java中调用sayHello()时,实际调用的是native_hello()。
3. 在jni目录下新建Android.mk,Android.mk的代码如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := HelloJni2
LOCAL_SRC_FILES := HelloJni2.cpp
LOCAL_LDLIBS :=-llog
include $(BUILD_SHARED_LIBRARY)
4. 生成.so库文件:
5. 在eclipse下执行工程,OK。
通过Javap 生成函数签名
javap -s -p F:\workspace1\HelloJni2\bin\classes\com\example\hellojni2\MainActivity.class(编译后的class文件)