转自:http://blog.sina.com.cn/s/blog_89f592f501013ge3.html
JNI demo 手把手教例子
1 JNI工程建立
Android目录下创一个目录jnidemo,并在该目录下创建三个文件
Android.mk //用于编译JNI工程的makefile文件
jnidemo.cpp //JNI代码文件
onload.cpp //用于注册JNI方法的文件
创建过程如下:
cd android4.1 mkdir jnidemo cd jnidemo touch Android.mk touch jnidemo.cpp touch onload.cpp |
2 编辑jnidemo.cpp
vim jnidemo.cpp
#include "JNIHelp.h" #include "jni.h"
#include "utils/Log.h" #include "utils/misc.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/android/jnidemo/Service", method_table,NELEM(method_table)); } }; |
这里我们提供了一个接口给Java层序调用,即nativeOpen(),我们是静态定义的,所以不需要的.h文件中声明。
静态初始化JNINativeMethod结构体,里面对nativeOpen()方法的描述
定义register_android_jnidemo_Service()方法,用于注册JNI文件,在该方法中,用到了两个关键的参数,
一个是"com/android/jnidemo/Service",对应着java代码的包名,即调用JNI的java代码所在的包是“com.android.jnidemo”,类名是Service,
另一个是method_table,即是上面初始化的JNINativeMethod结构体。
那么register_android_jnidemo_Service()是在哪里被调用的呢?那么请看onload.cpp
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:= libjnitdemo
include $(BUILD_SHARED_LIBRARY) |
Android.mk是android下面的makefile文件:
LOCAL_MODULE_TAGS,编译的模式,这里我们选择eng模式
LOCAL_SRC_FILES 编译的源文件,这里的源文件就是前面定义的两个cpp文件
LOCAL_SHARED_LIBRARIES 用到的共享库,libnativehelper是用于注册JNI用到的共享库,liblog是用于打印log用到的共享库,即ALOGD()方法需要的。
LOCAL_MODULE 编译输出模块的名称,编译之后将生成libjnidemo.so文件
在jnidemo目录下执行mm命令(前提是构建好Android的环境),编译当前目录下,之后将在out产品目录下的system/lib/目录下生成libjnidemo.so文件
5 编辑java代码
package com.android.jnidemo; import android.util.Log; public class Service { String TAG="Service"; static { System.loadLibrary("jnidemo"); } public native int nativeOpen(); public Service(){ Log.v(TAG,"get from jni = "+nativeOpen()); } } |
这里是java很简单的一个类,包名是com.android.jnidemo,类名是Service,这里我们看到了,在jnidemo.cpp文件中注册JNI的时候于此对应"com/android/jnidemo/Service"。
定义了一个静态模块,用于加载JNI库文件libjnidemo.ko
声明了native方法nativeOpen()
在Service类的构造函数中,调用了nativeOpen()方法。
我是在eclipse下面创建了一个Android apk的工程,在Activity的onCreate()方法中创建了Service的实例,即new Service(),这里就不写出来了。
6 代码执行
首先需要将libjnidemo.ko文件推送到小机的system/lib目录下
到out的产品目录的system/lib下执行 adb push libjnidemo.ko /system/lib
通过eclipse在小机中运行apk,在logcat中我们将看到如下打印:
com.android.jnidemo Service-JNI JNI test nativeOpen com.android.jnidemo service get from jni = 10 |
第一条是在JNI的nativeOpen方法中打印的,第二条是在Java代码中打印的。
看到这个结果,说明JNI 已经ok了。