美文网首页Android JNIAndroid开发经验谈Android开发
ndk07_JNI访问java方法,访问java构造,字符串的转

ndk07_JNI访问java方法,访问java构造,字符串的转

作者: IT魔幻师 | 来源:发表于2017-09-14 16:45 被阅读38次

    一、JNI调用java中的非静态方法

    • 1.获取java中声明的函数的签名

    1>java中在包名为com.hubin.jin 的Text.java文件中申明了getRandomInt()方法

    class Text{
    
        //用来触发JNI执行,演示用的
        public native void accessMethod();
    
        int getRandomInt(int max){
            return new Random().nextInt(max);
        }
    
    }
    

    2>cd到对应的Test.java文件生成的Test.class目录下执行javap命令

    3>再执行 javap -s -p Test(Test 为文件名)命令输出签名

    • 2.实现C端代码
        JNIEXPORT void JNICALL Java_com.hubin.jin_Test_accessMethod
        (JNIEnv *env ,jobject jobj){
            //1.得到jclass
            jclass jclz = (*env)->GetObjectClass(env, jobj);
        
            //2.获取jmethidId
            //getRandomInt:java方法的名字
            //(I)I:方法的签名
            jmethodID mid = (*env)->GetMethodID(env, jclz, "getRandomInt", "(I)I");
        
            //3.调用java getRandomInt()方法  200:getRandomInt的参数
            jint random = (*env)->CallIntMethod(env, jobj, mid, 200);
        
            printf("C ramdom:%d\n", random);
        
        }
    
    • 3.动态配置.dll库

        假设使用Visual Studio生成.dll库之后生成的路径为:
        D:\project\JNI_Text\x64\Debug\JNI_text.dll
        我们可以将 D:\project\JNI_Text\x64\Debug\ 配置到环境变量
        在java工程中就不需要再将.dll库拷贝到libs目录下加载,直接loadLibrary即可:
        
        static{
            System.loadLibrary("JNI_text");
        }
      

    二、JNI调用java中的静态方法

    • 1.JVM执行原理

        java中访问静态方法:类名.方法名
        JVM :ClassLoader(类加载器)加载.class文件
        如果加载失败:java.lang.ClassNotFoundException
        JNI的访问流程跟虚拟机加载的过程很类似
      
    • 2.创建java中的静态方法

        class Text{
            public native void accessStaticMethod();
        
            static String getRandeomUUId(){
                return UUID.randomUUID.toString();
            }
        }
      
    • 3.实现C端代码

        //JNI访问java中的静态方法
        JNIEXPORT void JNICALL Java_com.hubin.jin_Test_accessStaticMethod
        (JNIEnv *env ,jobject jobj){
            //1.找到jclass
            //原理:通过jobject来搜索class,如果找到了,将这个class 转变成jclass,然后返回
            jclass clz = (*env)->GetObjectClass(env, jobj);
        
            //2.找到jmethdId 
            //getRandeomUUId:java方法名字
            //()Ljava/lang/String :签名
            jmethodID jmid = (*env)->GetStaticMethodID(env, clz, "getRandeomUUId", "()Ljava/lang/String; ");
        
            //3.调用静态方法,得到java提供的uuid
            jstring uuid = (*env)->CallStaticObjectMethod(env, clz, jmid);
        
            //jstring转化成char*
            char * uuid_c = (*env)->GetStringUTFChars(env, uuid, NULL);
        
            //生成一个uuid.txt的文本文件到本地
            char filename[100];
            sprintf(filename, "D://%s.txt", uuid_c);
            FILE *fp = fopen(filename, "w";);//写入流
            fputs("I Love AV", fp);
            fclose(fp);
        
            printf("文件写入成功\n");
        }
      

    三、JNI访问java构造方法调用类中的函数

    • 1.创建要给触发执行C语言的函数

        public class Test{
            //触发函数
            public native Date accessConstructor();
            
        }
      
    • 2.C端方法实现

        JNIEXPORT jobject JNICALL Java_com.hubin.jin_Test_accessConstructor
        (JNIEnv *env ,jobject jobj){
        
            //1.通过类的路径来从JVM里面找到对应的类
            jclass jclz = (*env)->FindClass(env, "java/util/Data");
        
            //2.找到构造函数的jmethodid
            //<init> :构造方法的名字,所有的构造方法的名字都是他
            //()V :签名 自己用javap命令获取
            jmethodID jmid = (*env)->GetMethodID(env, jclz, "<init>", "()V");
        
            //3.调用newObject实例化一个Date对象,返回值是一个jobject
            //jni中所有引用的数据类型都会转化成jobject
            jobject date_obj = (*env)->NewObject(env, jclz, jmid);
        
        
            //4.获取Date类中的getTime方法的jmethidID
            //前提是,我们访问了相关对象的构造函数创建了这个对象
            jmethidID time_mid = (*env)->GetMethodID(env, jclass, "getTime", "()J")
        
            //5.执行getTime方法
            jlong time =(*env)->CallLongMethod(env, date_obj, time_mid);
        
            printf("time: %lld \n", time);
            return date_obj;
        }
      

    四、JNI中字符串的转换

    • 1.string在C端的转换方式一

    1>从java到jni到C/C++,编码的转换过程

    //java内部使用的是utf-16 16bit 的编码方式
    //jni 里面使用的utf-8  unicode编码方式  英文是1个字节,中文 3个字节
    //C/C++ 使用 ascii编码 ,中文的编码方式 GB2312编码 中文 2个字节
    

    2>声明java本地方法

    public native String chineseChars(String str);
    

    3> C端代码实现

        #include <Windows.h>
        
        JNIEXPORT jobject JNICALL Java_com.hubin.jin_Test_chineseChars
        (JNIEnv *env ,jobject jobj,jstring in){
        
            jboolean iscp;
            char * c_str = (*env)->GetStringChars(env, in, &iscp);
            if (iscp == JNI_TRUE){
                printf("is copy: JNI_TRUE\n");
            }
            else if (iscp == JNI_FALSE){
                printf("is copy: JNI_FALSE\n");
            }
        
            int length = (*env)->GetStringLength(env, in);
            const jchar * jcstr = (*env)->GetStringChars(env, in, NULL);
            if (jcstr == NULL) {
                return NULL;
            }
            //jchar -> char
            char * rtn = (char *)malloc(sizeof(char)* 2 * length + 3);
            memset(rtn, 0, sizeof(char)* 2 * length + 3);
            int size = 0;
            size = WideCharToMultiByte(CP_ACP, 0, (LPCWSTR)jcstr, length, rtn, sizeof(char)* 2 * length + 3, NULL, NULL);
            /*if (size <= 0)
            {
            printf("size: 0 \n", rtn);
            return NULL;
            }*/
            if (rtn != NULL) {
                free(rtn);
                rtn = NULL;
            }
            (*env)->ReleaseStringChars(env, in, c_str);// JVM 使用。通知JVM c_str 所指的空间可以释放了
            printf("string: %s\n", rtn);
        
            return NULL;
        }
    
    • 2.string在C端的转换方式二(使用String的构造方法在C端转换java字符串)

    1>java中String 有一个构造函数如下

    public String(byte bytes[], String charsetName){
        this(bytes,0,bytes.length,charsetName);
    }
    

    2>C端调用此构造函数生成字符串

        JNIEXPORT jobject JNICALL Java_com.hubin.jin_Test_chineseChars
        (JNIEnv *env ,jobject jobj,jstring in){
            char *c_str = "马蓉与宝宝";
        
            //获取jclass
            jclass str_cls = (*env)->FindClass(env, "java/lang/String");
            //获取String构造的jmethodID
            //([BLjava/lang/String;)V :签名
            jmethodID jmid = (*env)->GetMethodID(env, str_cls, "<init>", "([BLjava/lang/String;)V");
        
            //将jstring转换成jbyteArray
            jbyteArray bytes = (*env)->NewByteArray(env, strlen(c_str));
        
            //将Char *赋值到bytes
            (*env)->SetByteArrayRegion(env, bytes, 0, strlen(c_str), c_str);
        
            jstring charsetName = (*env)->NewStringUTF(env, "GB2312");
        
            return (*env)->NewObject(env, str_cls, jmid, bytes, charsetName);
        
        }

    相关文章

      网友评论

        本文标题:ndk07_JNI访问java方法,访问java构造,字符串的转

        本文链接:https://www.haomeiwen.com/subject/savdsxtx.html