Android NDK系列(6) — 动态注册native方法

来源:互联网 发布:淘宝怎么下架商品 编辑:程序博客网 时间:2024/06/16 02:35

我在博客上发表一些我的NDK学习心得,希望对大家能有帮助。 这一篇我们讲述如何动态注册native方法

介绍

首先,之前写的文章中通过一个简单的例子来使用了一下NDK,实现了从Native中调用Java方法。

下面,我们要介绍的是实现动态绑定native方法来破除命名限制。

问题

在静态注册的情况,所有的方法都是有固定的方法名:Java_<包名> <类名> <方法名>,这种情况下,调用一个方法比较繁琐,同时也有命名限制,所以使用动态绑定来解决这个问题。

实践

首先,还是老样子,加载so,定义native方法

public class MyJni {    static {        System.loadLibrary("myjni");    }    public native void nativePrint();    public native int nativeCount(int a, int b);    public MyJni() {        Log.d("123", "Load MyJni nativePrint & nativeCount");        nativePrint();        int a = nativeCount(1,2);        Log.d("123", a+"");    }}

为了在程序初始化的时候进行方法的动态注册,当so被加载时就会调用JNI_OnLoad函数,需要重写JNI_OnLoad方法,在该方法中注册

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){    JNIEnv* env = NULL;    jint result = -1;    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {        LOGE("GetEnv failed!");        return -1;    }    //===========================================    assert(env != NULL);    if (!registerNatives(env)) // 注册本地方法    {        return -1;    }    //===========================================    /* success -- return valid version number */    result = JNI_VERSION_1_4;    return result;}

调用registerNatives进行注册,其中需要指定注册的类信息,这样才能够获取其jclass对象

/** 为所有类注册本地方法*/static int registerNatives(JNIEnv* env) {    const char* kClassName = "com/example/qiuyu/testhellojni/MainActivity";//指定要注册的类    return registerNativeMethods(env, kClassName, gMethods,                                 sizeof(gMethods) / sizeof(gMethods[0]));}

调用registerNativeMethods,使用RegisterNatives进行注册

/** 为某一个类注册本地方法*/static int registerNativeMethods(JNIEnv* env        , const char* className        , JNINativeMethod* gMethods, int numMethods) {    jclass clazz;    clazz = (*env)->FindClass(env, className);    if (clazz == NULL) {        return JNI_FALSE;    }    if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {        return JNI_FALSE;    }    return JNI_TRUE;}

需要定义gMethods JNINativeMethod数组,实际就是修改指针指向执行的函数,从而实现方法注册

// 该结构表示一个Java本地方法与native层的c方法的映射  typedef struct {      char *name; // Java本地方法名称      char *signature; // Java本地方法签名      void *fnPtr; // c函数指针  } JNINativeMethod;  /*** Table of methods associated with a single class.*/static JNINativeMethod gMethods[] ={    {"nativePrint", "()V", (void*)native_Print },    {"nativeCount", "(II)I", (void*)native_Count },};// 实际执行函数void native_Print(JNIEnv * env, jobject thiz) {    LOGE("native_Print");}jint native_Count(JNIEnv * env, jobject thiz, jint a, jint b) {    return a+b;}

最终执行结果如下:

07-05 04:52:17.739 23202-23202/com.example.qiuyu.testhellojni D/123: Load MyJni nativeSetup07-05 04:52:17.739 23202-23202/com.example.qiuyu.testhellojni E/jni_thread: native_Print07-05 04:52:17.739 23202-23202/com.example.qiuyu.testhellojni D/123: 3

注:classname千万不能写错,写错之后会出现java.lang.NoClassDefFoundError错误
注:如果使用ProGuard进行混淆,很可能会找不到native方法,这个要注意

源码

Github : https://github.com/QyMars/AndroidNativeCode

总结

通过动态注册,可以避免硬编码,更加灵活,下面一章学习Native线程使用,之后要完成如何Native中启动一个线程来进行签名校验判断。

原创粉丝点击