zoukankan      html  css  js  c++  java
  • Android中关于JNI 的学习(六)JNI中注冊方法的实现

    在前面的样例中,我们会发现,当在Java类中定义一个方法的时候,例如以下:

    public class ParamTransferTest {
    
    	public static int testval = 1;
    	
    	public native void changeTestVal();

    则在相应的JNI层中,由javah生成的头文件和其相应的C文件,其方法名称必须例如以下:

    JNIEXPORT void JNICALL Java_com_lms_jni_ParamTransferTest_changeTestVal

    而方法名称之所以必须是这样,是基于在前面文章中我们提到过的一些JNI的命名规则,可是是不是一定要这样才行呢,多麻烦呀。

    (tips:JNIExport 和 JNICALL是windows平台的jni编译出来的,在Android手机上,事实上能够不要这两个keyword的)

    答案当然是否定的。

    在前面的样例中,为什么在JNI层实现的方法名必须符合一定的命名规则呢?这是由于,我们并没有提供JNI层方法和Java端方法的一个联系,或者说一个相应关系,而由于缺乏这样的我们自己定义的相应关系,NDK在编译的时候,操作系统在解释的时候,它们就必须制定一系列的规则,而通过这个规则去找到相应的方法。不然,假设你随便定义一个方法名,我随便定义一个方法名,别人怎么可能知道这两个就是相应的呢?无规则不成方圆,所以。。。

    那么,假设我们不想再去写这些又长又臭的方法,我们就必须给出它们的相应关系,而JNI事实上也提供了这种一套机制,这篇博文就让我们来看看,怎样在Android中实现这种机制。

    事实上Android底层的源代码中,涉及到JNI层的方法实现和载入,基本上都是通过这样的注冊方法的机制来实现的,包括我们上一篇文章中提到的Log的实现。

    在jni.h文件里,提供了JNINativeMethod的结构,例如以下:

    typedef struct {
        const char* name;<span style="white-space:pre">		</span>//java端方法名
        const char* signature;<span style="white-space:pre">	</span>//方法签名
        void*       fnPtr;<span style="white-space:pre">		</span>//jni层函数指针
    } JNINativeMethod;

    JNINativeMethod这个结构,存放的就是Java端方法跟JNI层方法的一个相应关系,其有三个字段,表示的意思,大家看凝视就清楚了。

    接下来,我们通过一个小Demo来看看怎样在JNI中实现注冊函数,然后由DVM载入使用。

    1)在Java端定义一个Native方法,例如以下:

    public class DynReg {
    
    	public native static String sayHello();
    }

    2)在JNI层中创建相应的C文件,这一次,我们不须要生成利用javah生成头文件之类,然后复制方法名之类的,例如以下:

    jstring say_hello(JNIEnv *e, jobject j) {
    	return (*e)->NewStringUTF(e, "Say Hello from dynamic register");
    }

    我们并不须要遵循传统的JNI编程命名方法,我们能够自定义我们想要的方法名称,比方say_hello等等。

    4)利用JNINativeMethod结构,创建一个相应的关系,例如以下:

    static JNINativeMethod mehtod_table[] = {
    		{ "sayHello", "()Ljava/lang/String;", (void*) say_hello },
    };
    

    这是一个结构数组,相应于多个方法,在这个样例中,我们仅仅有一个方法,所以仅仅有一个元素,我们能够看到:

    4.1)“sayHello”,相应于Java端的方法

    4.2)"()Ljava/lang/String",相应于其方法签名

    4.3)(void*) say_hello,相应于我们上面实现的C方法

    5)在JNI_OnLoad方法中将这个相应注冊到DVM中,例如以下:

    static int registerNativeMethods(JNIEnv* env, const char* className,
    		JNINativeMethod* methods, int numMethods) {
    	jclass clazz;
    	clazz = (*env)->FindClass(env, className);
    	if (clazz == NULL) {
    		return JNI_FALSE;
    	}
    	if ((*env)->RegisterNatives(env, clazz, methods, numMethods) < 0) {
    		return JNI_FALSE;
    	}
    
    	return JNI_TRUE;
    }
    
    jint JNI_OnLoad(JavaVM* jvm, void* reserved) {
    	JNIEnv* env = NULL;
    	jint result = -1;
    	if ((*jvm)->GetEnv(jvm, (void**) &env, JNI_VERSION_1_4) != JNI_OK) {
    		return result;
    	}
    
    	registerNativeMethods(env, "com/lms/jni/DynReg", mehtod_table, NELEM(mehtod_table));
    
    	return JNI_VERSION_1_4;
    }

    简单讲一下这个过程,当这些C文件被编译成相应的so文件之后,在Java端中利用System.loadLibrary来载入so库的时候,相应类中的JNI_OnLoad方法就会被调用,这事实上就是相当于一些接口的回调函数的概念, 在载入的时候,我们就能够将上面定义的JNINativeMethod数组,通过调用JNIEnv*的RegisterNatives方法,将这个数组中的方法给注冊到JNIEnv中了,而在底层的实现中,当DVM调用相应的native方法的时候,会依据我们这里有没有注冊到JNIEnv*中去找相应的方法,假设找到了,就直接运行,假设没有找到,JNIEnv*就会继续依据传统的JNI方法命名规范去找原先那又长又臭的方法,假设那个方法也没有找到,就会报错了。

    5.1)调用registerNativeMethods,传入相应的类名“com/lms/jni/DynReg”,还有方法表method_table,方法表中方法的个数,NELEM是定义的一个宏,例如以下:

    # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))

      5.2)在registerNativeMethods方法中,依据类名,找出相应的类,将类,方法和方法个数调用RegisterNatives方法注冊到JNIEnv*中。

    6)在Android.mk文件里声明我们这个新加入�的类 DynReg.c,例如以下:

    LOCAL_PATH := $(call my-dir)
    
    include $(CLEAR_VARS)
    
    LOCAL_MODULE := com_lms_jni_HwDemo
    
    LOCAL_SRC_FILES := 
    HwDemo.c 
    JniTest.c 
    ParamTransferTest.c
    DynReg.c
    
    LOCAL_LDLIBS += -llog
    
    include $(BUILD_SHARED_LIBRARY)

    7)最后在Java端中使用这种方法,例如以下:

    tv.setText(DynReg.sayHello());

    8)结果例如以下:


    关于在JNI中注冊函数实现JNI层和Java层互相通信的方法到这里也就差点儿相同了,利用这种方法,在编写C/C++方法的时候,是不是看起来就舒服多了啊?

    结束。

  • 相关阅读:
    读《构建之法》第一,二,十六章的奇思妙想
    四则运算
    鉴权
    sql注入
    【信息安全作业5】散列函数的应用及其安全性
    结对作业 -GUI四则运算
    阅读《构建之法》四章、十七章
    2016012064+小学四则运算练习软件项目报告
    简单四则运算一
    梦想开花的地方
  • 原文地址:https://www.cnblogs.com/hrhguanli/p/3961140.html
Copyright © 2011-2022 走看看