(1)JNI工程建立
在Android目录下任意创建一个目录jnidemo,并在该目录下创建三个文件:Android.mk,用于编译JNI工程的makefile文件;jnidemo.cpp,JNI代码文件;onload.cpp,用于注册JNI方法的文件。
mkdir jnidemo
cd jnidemo
touch Android.mk
touch jnidemo.cpp
touch onload.cpp
(2)编辑jnidemo.cpp
#include "JNIHelp.h"
#include "jni.h"
#define LOG_TAG "Service-JNI"
namespace android
{
static jint nativeOpen(JNIEnv* env,jobject obj){
ALOGE("JNI test nativeOpen");
return 10;
}
static JNINativeMethod method_table[] = {
{"nativeOpen","()I",(void*)nativeOpen }
};
int register_android_jnidemo_Service(JNIEnv *env){
return jniRegisterNativeMethods(env,"com/example/test/Demo",
method_table,NELEM(method_table));
}
};
这里我们提供了一个接口给Java层调用,即nativeOpen()。定义register_android_jnidemo_Service()方法,用于注册JNI文件,在该方法中,用到了两个关键的参数。一个是"com/example/test/Demo",对应着java代码的包名和类名,即调用JNI的java代码所在的包是“com.example.test”,类名是Demo;另一个是method_table,即是上面初始化的JNINativeMethod结构体。
(3)编辑onload.cpp
#include "JNIHelp.h"
#include "jni.h"
#include "utils/Log.h"
#include "utils/misc.h"
namespace android {
int register_android_jnidemo_Service(JNIEnv* env);
};
using namespace android;
extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_jnidemo_Service(env);
return JNI_VERSION_1_4;
}
当java代码调用System.loadLibrary()加载JNI库的时候,将调用到onload.cpp的JNI_OnLoad()方法,然后将调用在jnidemo.cpp文件中定义的方法register_android_jnidemo_Service(env)对JNI进行注册。其实很简单吧,onload.cpp就是完成注册功能的。
(4)编辑Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := eng
LOCAL_SRC_FILES:= \
jnidemo.cpp \
onload.cpp
LOCAL_SHARED_LIBRARIES := \
libnativehelper \
liblog
LOCAL_MODULE:= libjnidemo
include $(BUILD_SHARED_LIBRARY)
LOCAL_SHARED_LIBRARIES 用到的共享库,libnativehelper是用于注册JNI用到的共享库,liblog是用于打印log用到的共享库,即ALOGD()方法需要的。 LOCAL_MODULE 编译输出模块的名称,编译之后将生成libjnidemo.so文件
在完成以上工作后,在jnidemo目录下执行mm命令,之后将在out产品目录下的system/lib/目录下生成libjnidemo.so文件,将该文件push到目标板的/system/lib目录下。
(5)制作一个简单的apk,新建一个Demo类,在activity的onCreate函数中完成new Demo实例,就可以了。Demo.java内容:
package com.example.test;
import android.util.Log;
public class Demo {
String TAG="Demo";
static {
System.loadLibrary("jnidemo");
}
public native int nativeOpen(); //以native 申明JNI函数
public Demo(){
Log.v(TAG,"get from jni = "+nativeOpen());
}
}
activiy的内容
package com.example.test;
import android.os.Bundle;
import android.app.Activity;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Demo();
}
}
运行时就会在eclipse的Logcat中发现如下log:
01-03 00:23:56.693: E/Service-JNI(2709): JNI test nativeOpen
01-03 00:23:56.693: V/Demo(2709): get from jni = 10
第一条是在JNI的nativeOpen方法中打印的,第二条是在Java代码中打印的。看到这个结果,说明JNI 已经ok了。
参考原文:http://blog.sina.com.cn/s/blog_89f592f501013ge3.html
========================================JNINativeMethod的参数解析==============================================
Andoird 中使用了一种不同传统Java JNI的方式来定义其native的函数。其中很重要的区别是Andorid使用了一种Java 和 C 函数的映射表数组,并在其中描述了函数的参数和返回值。这个数组的类型是JNINativeMethod,定义如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
第一个变量name是Java中函数的名字;第二个变量signature,用字符串是描述了函数的参数和返回值;第三个变量fnPtr是函数指针,指向C函数。其中比较难以理解的是第二个参数,例如
"()V"
"(II)V"
实际上这些字符是与函数的参数类型一一对应的。"()" 中的字符表示参数,后面的则代表返回值。例如"()V" 就表示void Func();"(II)V" 表示 void Func(int, int);
具体的每一个字符的对应关系如下:
字符 Java类型 C类型
V void void
Z jboolean boolean
I jint int
J jlong long
D jdouble double
F jfloat float
B jbyte byte
C jchar char
S jshort short
数组则以"["开始,用两个字符表示
[I jintArray int[]
[F jfloatArray float[]
[B jbyteArray byte[]
[C jcharArray char[]
[S jshortArray short[]
[D jdoubleArray double[]
[J jlongArray long[]
[Z jbooleanArray boolean[]
上面的都是基本类型。如果Java函数的参数是class,则以"L"开头,以";"结尾中间是用"/" 隔开的包及类名。
Ljava/lang/String; jstring
========================================JNI使用中的报错==============================================
(1)在使用jni时,.c文件和.cpp文件中jni函数的使用语法是不同的。否则编译会报错,详见:http://blog.csdn.net/forandever/article/details/50396058
(2)在使用jstring 类型时,要注意ReleaseStringUTFChars的使用。详见:https://segmentfault.com/a/1190000005859213