Quantcast
Channel: Sam的技术Blog
Viewing all articles
Browse latest Browse all 158

AndroidJNI记录

$
0
0
作者: Sam (甄峰)   sam_code@hotmail.com

很早之前,在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();
}

B: 生成Class文件:
javac test/HelloWorld.java
test目录下将生成HelloWorld.class

C:生成头文件:
/javah -o test/Hello.h test.HelloWorld
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的动态注册。
//回调函数 在这里面注册函数
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:




 

Viewing all articles
Browse latest Browse all 158

Trending Articles