作者: Sam (甄峰)
sam_code@hotmail.com
#include
Void {
printf("helloworld");
很早之前,在Android
下使用OpenGL时,用到了JNI。当前看到同事使用JNI,生成方式不同。所以记录之。
1. 静态注册+NDK编译:
在NDK Sample中,有gles3jni , hello-gl2,
hello-jni等例子程序。他们分别是:OpenGLES3.0, OpenGLES2.0等使用例子。无一例外都是Android
下创建JNI库和使用JNI库的例子。
这些例子采用静态注册,在C++代码中,通过
创建 的函数,提供给Java代码调用。并生成动态库。
; 载入库。
使用关键字 native声明该函数。并使用函数名()调用之。
这个方法的思路是: 首先提供C++代码,生成动态库,然后供Java代码使用。
使用Android.mk, Application.mk编译之。
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE
:= libgles3jni
LOCAL_CFLAGS
:= -Werror -DDYNAMIC_ES3
LOCAL_SRC_FILES := xxx.cpp
LOCAL_LDLIBS
:= -llog -lGLESv2 -lEGL
include $(BUILD_SHARED_LIBRARY)
这里生成了动态库。libgles3jni.so. 供Java 层使用。
2. 静态注册+javah方式:
这个方法的思路是: 首先生成Java代码,再通过javah 生成头文件,
依据头文件编写C++代码,最后生成动态库。
A:生成java文件
package test;
public class HelloWorld {
public static void main(String[] args){
System.loadLibrary("HelloWorld");
printHello();
}
public static native final void printHello();
}
public class HelloWorld {
public static void main(String[] args){
System.loadLibrary("HelloWorld");
printHello();
}
public static native final void printHello();
}
B: 生成Class文件:
javac test/HelloWorld.java
test目录下将生成HelloWorld.class
test目录下将生成HelloWorld.class
C:生成头文件:
/javah -o test/Hello.h test.HelloWorld
test目录下将生成Hello.h
test目录下将生成Hello.h
D: 创建C++源码文件:
#include
"Hello.h"#include
Void {
printf("helloworld");
}
E: 生成库文件:
g++
test/HelloWorld.cpp -I ../jdk1.5.0_22/include/ -I
../jdk1.5.0_22/include/linux/ -fPIC -shared -o
test/libHelloWorld.so
test目录下将会生成libHelloWorld.so
F: 运行:
java
test.HelloWorld
屏幕上会打印出helloworld.
当然,这个方法是在X86机器上的路线图。 在Android ARM平台。则A,B,C,D可以照做。
但后面如何生成APK,则交给Eclipse或AS吧。
3.
动态加载+NDK编译:
java上层的调用和静态方式是相同的,关键是native层的调用有所不同。
1.JNI_OnLoad()内容有所不同。
2.C++内方法名命名方式不同。
JNI_OnLoad是java
jni技术的一个实现,每次java层加载System.loadLibrary之后,
自动会查找改库一个叫JNI_OnLoad的函数,动态注册的时候,cpp可以通过实现JNI_OnLoad而完成jni的动态注册。
自动会查找改库一个叫JNI_OnLoad的函数,动态注册的时候,cpp可以通过实现JNI_OnLoad而完成jni的动态注册。
//回调函数 在这里面注册函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void*
reserved){
JNIEnv* env =
NULL;
//判断虚拟机状态是否有问题
if(vm->GetEnv((void**)&env,JNI_VERSION_1_6)!= JNI_OK){
return -1;
}
assert(env !=
NULL);
//开始注册函数
registerNatives ->registerNativeMethods
->env->RegisterNatives
if(!registerNatives(env)){
return -1;
}
//返回jni
的版本
return
JNI_VERSION_1_6;
}
static int registerNatives(JNIEnv* env){
//指定类的路径,通过FindClass
方法来找到对应的类
const char*
className =
"com/zienon/airmovesocketconnect/AirMoveGestureJni";
return
registerNativeMethods(env,className,getMethods, sizeof(getMethods)/
sizeof(getMethods[0]));
}
//此函数通过调用JNI中 RegisterNatives 方法来注册我们的函数
static int registerNativeMethods(JNIEnv* env, const
char* className,JNINativeMethod* getMethods,int methodsNum){
jclass
clazz;
//找到声明native方法的类
clazz =
env->FindClass(className);
if(clazz ==
NULL){
return JNI_FALSE;
}
//注册函数 参数:java类
所要注册的函数数组 注册函数的个数
if(env->RegisterNatives(clazz,getMethods,methodsNum) <
0){
return JNI_FALSE;
}
return
JNI_TRUE;
}
这样,修改PackageName,ClassName就方便很多。函数名可以很短。
同样,使用NDK(Android.mk,
Application.mk编译之)。 供Java使用。
4. 动态加载 + AS CMake: