Android JNI静态和动态注册 、Java Reflect(C或C++层反射和JAVA层反射)、Java 可变参数(JNI实现)

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=052)
  本文发布于 2018-01-09 09:13:35,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=052)

环境说明

  无

前言


  由于最近重新接触了部分Android相关的东西,对一些需要混合编程的手段做了整理,同时也对Java中常见的反射技术进行归纳总结。(本文适合知道JNI和反射是什么鬼的人阅读)(可变参数实现在JNI部分的IoctlGpio方法)





Java JNI


  Java native interface(java本地调用接口)主要是实现C或者C++和java交互。
先来看一张来至于网上的图(如果侵权,请联系我,我会第一时间删除)

rep_img

  此图说明了一个jvm中(一个进程中)所拥有的资源。而JNI就是用来实现调用本地方法的一种方法。而这些本地方法有两种方式可以被注册到jvm中,分别如下:



JNI之静态注册
public class Operate_Gpio {
    public native int IoctlGpio(int cmd, String ...arg);
    public native int IoctlGpio(int cmd, int ...arg);
    
	//java static code
	static {
        System.loadLibrary("OperateGpio");
    }
  }
    //call IoctlGpio()
  Operate_Gpio op_gpio = new Operate_Gpio();
  op_gpio.IoctlGpio(2,1,3);
  op_gpio.IoctlGpio(2,"a","b");
extern "C"
JNIEXPORT jint JNICALL
Java_JNI_Operate_1Gpio_IoctlGpio(JNIEnv *env, jobject instance, jint cmd,jobjectArray arg) {

    jboolean iscopy;

    switch (cmd) {
        case 1: {
            jint *arg1 = env->GetIntArrayElements((jintArray) arg, 0);

            NativeLog(env, instance, 'D', "Test string arg1",
                      Int_to_String(arg1[0]));
            NativeLog(env, instance, 'D', "Test string arg2",
                      Int_to_String(arg1[1]));
                      env->ReleaseIntArrayElements((jintArray)arg, arg1, 0);
            break;
        }
        case 2: {
            jstring arg3 = (jstring) env->GetObjectArrayElement(arg, 0);
            jstring arg4 = (jstring) env->GetObjectArrayElement(arg, 1);
            NativeLog(env, instance, 'D', "Test string arg3",
                      env->GetStringUTFChars(arg3, &iscopy));
            NativeLog(env, instance, 'D', "Test string arg4",
                      env->GetStringUTFChars(arg4, &iscopy));
            break;
        }
        default:{
            break;
        }

    }
    return 0;

}

  说明:通过特定的函数命名方式(包名__类名__函数名)(若以上命名中含有_,将会通过在其后加一位数字方式来标识)来注册。当在java层调用System.loadLibrary,jvm会遍历这个so库的所有合法的方法,并放到本地方法栈,以供使用。

  缺点:

  1. 初次call本地函数时,需要先在jni下去搜索相关的jni函数,然后建立jni函数和native函数关系,导致效率降低
  2. 使用规则复杂,不利于开发(不方便)。


JNI之动态注册
public class Operate_Gpio {
public native  int TestGpio();
	//java static code
	static {
        System.loadLibrary("OperateGpio");
    }
}

  Operate_Gpio op_gpio = new Operate_Gpio();
  op_gpio.TestGpio();
/**
* Table of methods associated with a single class.
*/
/*
 *
 * typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;
 * */
// 结构体,分别是java层的函数名称,签名,对应的函数指针
static JNINativeMethod gMethods[] = {
        { "TestGpio", "()I", (void*)TestGpio },//绑定
};
static int registerNativeMethods(JNIEnv* env, const char* className,
                                 JNINativeMethod* gMethods, int numMethods)
{
    jclass clazz;
    clazz = env->FindClass(className);
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
        return JNI_FALSE;
    }

    return JNI_TRUE;
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;

    if ( vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);

    if (!registerNativeMethods(env, JNIREG_CLASS, gMethods,
                               sizeof(gMethods) / sizeof(gMethods[0])))
        return -1;
    /* success -- return valid version number */
    return JNI_VERSION_1_4;
}

  说明:

  1. JNINativeMethod保存Java Native函数和JNI函数的对应关系
  2. Java层中调用System.loadLibrary加载so库,并调用JNI_OnLoad开始注册
  3. JNI_OnLoad中调用AndroidRuntime::registerNativeMethods
  4. AndroidRuntime::registerNativeMethods中调用 jniRegisterNativeMethods

  注意:这里有个知识叫做java签名,此签名是指的方法的类型标识,也就是一堆类型简写。(写法:(函数参数列表)返回值)

  下列是常用的类型总结(资料来于网上,若有侵权,请联系我,我第一时间删除):

/*

Java方法签名中特殊字符/字母含义
特殊字符	数据类型	特殊说明
V	void 	一般用于表示方法的返回值
Z	boolean	 
B	byte	 
C	char	 
S	short	 
I	int	 
J	long	 
F	float	 
D	double	 
[	数组	以[开头,配合其他的特殊字符,表示对应数据类型的数组,几个[表示几维数组
L全类名;	引用类型	以L开头、;结尾,中间是引用类型的全类名

*/




Java Reflect


  程序运行时获取类的属性,也可调用类的方法。可灵活的方便构建出各种复杂的逻辑结构。



C或者C++层反射

int static NativeLog(JNIEnv *env, jobject instance ,char type, std::string tag, std::string msg){

    jclass clazz = env->FindClass("android/util/Log") ;
    jmethodID jmth_id;
    switch (type){

        case 'V':
            jmth_id  = env->GetStaticMethodID(clazz, "v", "(Ljava/lang/String;Ljava/lang/String;)I");
            break;
        case 'D':
            jmth_id  = env->GetStaticMethodID(clazz, "d", "(Ljava/lang/String;Ljava/lang/String;)I");
            break;
        case 'I':
            jmth_id  = env->GetStaticMethodID(clazz, "i", "(Ljava/lang/String;Ljava/lang/String;)I");
            break;
        case 'W':
            jmth_id  = env->GetStaticMethodID(clazz, "w", "(Ljava/lang/String;Ljava/lang/String;)I");
            break;
        case 'E':
            jmth_id  = env->GetStaticMethodID(clazz, "e", "(Ljava/lang/String;Ljava/lang/String;)I");
            break;
        default:
            break;
    }

    return env->CallStaticIntMethod(clazz, jmth_id, env->NewStringUTF(tag.c_str()), env->NewStringUTF(msg.c_str()));
}

extern "C"
JNIEXPORT jint JNICALL
Java_JNI_Operate_1Gpio_OpenGpio(JNIEnv * env, jobject obj){
    //open devices ... ...
    NativeLog(env, obj, 'D', "Test", "Reflect test.");
    return 0;
}
public class Operate_Gpio {

    public native int OpenGpio();

    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("OperateGpio");
    }
}

Operate_Gpio op_gpio = new Operate_Gpio();
op_gpio.OpenGpio();


JAVA层反射
        try {
            Class clazz = Class.forName("android.util.Log");
            Method method = clazz.getDeclaredMethod("d", String.class, String.class);
            method.invoke(null,"Test","Fuck");//调用静态函数
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }

  所有结果的最终效果如图:

rep_img




后记


  无

参考文献




打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

posted on 2023-02-11 17:10  SkyOnSky  阅读(177)  评论(0编辑  收藏  举报

导航