Android JNI的动态注册

来源:互联网 发布:网络用语猜成语 编辑:程序博客网 时间:2024/05/18 19:20

我们知道Androd 使用JNI的注册方法包括两个,静态注册和动态注册。
静态注册就是通过javah命令生成.h文件,然后实现声明的函数即可,但此类函数的函数名都比较长:Java+包名+类名+方法名。而动态注册则没有这一限制。先以下面这个例子进行说明,例子中的jni函数参考了Android M源码。

  1. 我们新建一个的Android工程,包含一个默认的MainActivity。代码如下:
package com.example.jnidynamic;import android.app.Activity;import android.os.Bundle;import android.widget.TextView;public class MainActivity extends Activity {    private TextView mTextView;    // 声明一个jni函数,待会去实现    private native String getStringFromJni();    static {        System.loadLibrary("myJNI"); // 加载库,待会会用NDK去生成该库    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mTextView = (TextView)findViewById(R.id.my_text_view);        mTextView.setText(getStringFromJni()); // 原理很简单,显示一个从JNI层返回的字符串    }}
  1. 右键工程, Android tools -> Add native …,库的名字填写上面的myJNI即可。eclipse要先配置好NDK,这里不再说明。其实也可以不用在eclipse配置NDK,自己用NDK命令去编译也可以。

myJNI.cpp

#include <jni.h>#include <android/log.h>#include <string.h>#include <stdio.h>#include <jni.h>#include <assert.h>/* 定义了一些宏,这里用来输出Log用的,JNI层输出Android LOG, JNI层输出LOG可以参考我以前写的,LOG_TAG是tag */#ifndef LOG #define LOG_TAG "myJNI"#define ALOGD(...) \            __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__);#define ALOGE(...) \            __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__);#define ALOGV(...) \            __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__);#endif LOG#ifndef NELEM# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))#endif// c函数jstring native_hello(JNIEnv* env, jobject thiz){    return env ->NewStringUTF("hello world from jni~");}// java类的位置static const char *classPathName = "com/example/jnidynamic/MainActivity";// 映射表,这里将我们java函数和c函数对应起来static JNINativeMethod gMethods[] = {    {"getStringFromJni", "()Ljava/lang/String;", (void*)native_hello},};/* 下面的可以说,基本上是模板了,以后直接copy就行 */static int jniRegisterNativeMethods(JNIEnv* env, const char* className,    JNINativeMethod* gMethods, int numMethods){    jclass clazz;    clazz = env->FindClass(className);    if (clazz == NULL) {        ALOGE("Native registration unable to find class '%s'", className);        return JNI_FALSE;    }    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {        ALOGE("RegisterNatives failed for '%s'", className);        return JNI_FALSE;    }    return JNI_TRUE;}static int register_my_jni_methods(JNIEnv* env) {    return jniRegisterNativeMethods(env, classPathName, gMethods, NELEM(gMethods));}// 在我们load该so库的时候,JNI_OnLoad将会自动被调用,来注册JNI函数jint JNI_OnLoad(JavaVM* vm, void*){    JNIEnv* env = NULL;    jint result = -1;    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {        ALOGE("ERROR: GetEnv failed\n");        goto bail;    }    assert(env != NULL);    if (register_my_jni_methods(env) < 0) {        ALOGE("ERROR: native registration failed\n");        goto bail;    }    /* success -- return valid version number */    ALOGE("SUCCESS: native registration successed\n");    result = JNI_VERSION_1_4;bail:    return result;}

Android.mk

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llogLOCAL_MODULE    := myJNILOCAL_SRC_FILES := myJNI.cppinclude $(BUILD_SHARED_LIBRARY)

其实这里的重点是JNINativeMethod这里结构体

typedef struct {      const char* name;  //Java中函数的名字    const char* signature;  //用字符串描述的函数的参数和返回值    void* fnPtr;  //指向C函数的函数指针} JNINativeMethod; 

这里面我觉得比较难书写的是signature, 反映的是函数的参数和返回值。括号内()为参数,最后为返回值。

具体的每一个字符的对应关系如下

字符 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函数的参数是java的某个类,则以”L”开头,以”;”结尾,中间是用”/” 隔开的包名和类名。而其对应的C函数名的参数则统一为jobject。但String类是个例外,其对应的类为jstring。
举下面几个例子:

字符 Java类型 C类型 Ljava/lang/String; String jstring Ljava/net/Socket; Socket jobject L包名/类名; 类名 jobject

如果java类是一个内部类的话,则用$作为类名间的分隔符。
例如 :

“(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z”

0 0
原创粉丝点击