Android JNI与C互调

JIN方法签名

在C中调用Java中方法时需要使用到方法签名。就是将方法的返回类型和参数类型转换为特定的符号。规则如下:

java类型符号
BooleanZ
ByteB
CharC
ShortS
IntI
LongJ
FloatF
DoubleD
VoidV
数组[ , 如:int[]-> [I, int[][]-> [[I, Thread[]-> [Ljava/lang/Thread;
objects以”L”开头,以”;”结尾,中间是用”/” 隔开的包及类名。比如:Ljava/lang/String;如果是嵌套类,则用 来 表 示 嵌 套 。 例 如 “ ( L j a v a / l a n g / S t r i n g ; L a n d r o i d / o s / F i l e U t i l s 来表示嵌套。例如 “(Ljava/lang/String;Landroid/os/FileUtils (Ljava/lang/String;Landroid/os/FileUtilsFileStatus;)Z”

java调用C中方法

需要先在java中定义native方法:

    public native int add(int x,int y);
    public native String sayHello(String s);
    public native int[] increaseArrayEles(int[] intArray);
    public native int checkPwd(String pwd);

需要在C代码中实现这些方法,可以在Android studio中执行javah命令:
进入到java目录下,执行 javah com.xx.xx(包名).xx(类名) 可以生成.h文件,然后可以在.c文件中执行对应的方法。
c中实现方法:

/**
 * 将jstring转换为C中的char数组。
 * @param env
 * @param jstr  字符串
 * @return
 */
char* _JString2CStr(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env, "java/lang/String");
    jstring strencode = (*env)->NewStringUTF(env,"GB2312");
    jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
    jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312");
    jsize alen = (*env)->GetArrayLength(env, barr);
    jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
    if(alen > 0) {
        rtn = (char*)malloc(alen+1); //"\0"
        memcpy(rtn, ba, alen);
        rtn[alen]=0;
    }
    (*env)->ReleaseByteArrayElements(env, barr, ba,0);
    return rtn;
}

JNIEXPORT jstring JNICALL Java_wangjin_com_cmakendkdemo_JNI_sayHello
        (JNIEnv *env, jobject jobj, jstring jstr){
    char* fromJava = _JString2CStr(env,jstr);
    char* formC = "add I am from C";
    strcat(fromJava,formC);
    return (*env)->NewStringUTF(env,fromJava);  //返回字符串
}
JNIEXPORT jintArray JNICALL Java_wangjin_com_cmakendkdemo_JNI_increaseArrayEles
        (JNIEnv *env, jobject jobj, jintArray array){
    int size = (*env)->GetArrayLength(env,array);
    jint* intArray = (*env)->GetIntArrayElements(env,array,JNI_FALSE);
    for(int i=0;i < size;i++){
        *(intArray+i) = *(intArray+i) +10;
        LOGE("from c: %d",*(intArray+i));
    }
    return array;  //返回int类型数组
};
JNIEXPORT jint JNICALL Java_wangjin_com_cmakendkdemo_JNI_checkPwd
        (JNIEnv *env, jobject jobj, jstring pwd){
    char* formJava = _JString2CStr(env,pwd);
    char* passworld="12345";
    int code = strcmp(passworld,formJava);
    if(code == 1){
        return 200;
    } else {
        return 400;
    }
};

C中回调java方法

即Java中调用的C方法需要使用java中的方法。要注意调用的Java方法是不是static类型。
调用非static类型的步骤如下:
1、得到字节码 jclass
2、得到 method IDs jmethodID
3、实例化该类
4、调用方法
1)调用返回类型为int类型

/**
     * 当执行这个方法的时候,让C代码调用
     * public int add(int x, int y)
     */
    public native void callbackAdd();

Java中的add方法:

    public int add(int x, int y) {
        Log.e("TAG", "add() x=" + x + " y=" + y);
        return x + y;
    }

C代码中实现方法:

    //1、得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env,"wangjin/com/cmakendkdemo/CCallJava");
    //2、得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //第3个参数是要调用的方法名,第4个参数是方法签名,由于int add(int ,int),所已参数为"(II)I"
    jmethodID jmethodID1=(*env)->GetMethodID(env,jclazz,"add","(II)I");
    //3、实例化该类
    //jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject jobject1 = (*env)->AllocObject(env,jclazz);
    //4、调用方法 有返回值,类型为int,后两个参数时add方法的参数:99,1
    //jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallIntMethod(env,jobject1,jmethodID1,99,1);
    //成功调用add方法。

2)调用返回类型和参数都为void的方法

    /**
     * 当执行这个方法的时候,让C代码调用
     * public void helloFromJava()
     */
    public native void callbackHelloFromJava();  
    public void helloFromJava() {
        Log.e("TAG", "helloFromJava()");
    }

实现方法:

    //1、得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env,"wangjin/com/cmakendkdemo/CCallJava");
    //2、得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //第3个参数是要调用的方法名,第4个参数是方法签名,参数为null,则签名为()V,不要写成(V)V
    jmethodID jmethodID1=(*env)->GetMethodID(env,jclazz,"helloFromJava","()V");
    //3、实例化该类
    //jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject jobject1 = (*env)->AllocObject(env,jclazz);
    //4、调用方法 无返回值
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,jobject1,jmethodID1);
    //成功调用helloFromJava方法。

3) 调用参数为String 返回类型为void的方法

    /**
     * 当执行这个方法的时候,让C代码调用void printString(String s)
     */
    public native void callbackPrintString();  
        public void printString(String s) {
        Log.e("TAG","C中输入的:" + s);
    }

实现方法:

    //1、得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env,"wangjin/com/cmakendkdemo/CCallJava");
    //2、得到方法
    //jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
    //第3个参数是要调用的方法名,第4个参数是方法签名,注意参数的签名
    jmethodID jmethodID1=(*env)->GetMethodID(env,jclazz,"printString","(Ljava/lang/String;)V");
    //3、实例化该类
    //jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject jobject1 = (*env)->AllocObject(env,jclazz);
    //4、调用方法 有返回值
    //jstring     (*NewStringUTF)(JNIEnv*, const char*);
    jstring jstring1 = (**env).NewStringUTF(env,"I am from C");
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,jobject1,jmethodID1,jstring1);
    //成功调用printString方法。

4)调用static方法
调用static方法和非static方法不同,不在需要实例化类。
1、得到字节码 jclass
2、得到 method IDs jmethodID
3、调用方法

    /**
     * 当执行这个方法的时候,让C代码静态方法 static void sayHello(String s)
     */
        public native void callbackSayHello();  
        public static void sayHello(String s){
        Log.e("TAG",  "我是java代码中的JNI."
                + "java中的sayHello(String s)静态方法,我被C调用了:"+ s);
    }

C中实现方法:

    //1、得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env,"wangjin/com/cmakendkdemo/CCallJava");
    //2、得到方法
    //jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
    //第3个参数是要调用的方法名,第4个参数是方法签名
    jmethodID jmethodStaticID1=(*env)->GetStaticMethodID(env,jclazz,"sayHello","(Ljava/lang/String;)V");
    //3、实例化该类
    //jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject jobject1 = (*env)->AllocObject(env,jclazz);
    //4、调用方法 有返回值
    //jstring     (*NewStringUTF)(JNIEnv*, const char*);
    jstring jstring1 = (**env).NewStringUTF(env,"I am from C");
    //void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    //注意,第二个参数是jclass类型
    (*env)->CallStaticVoidMethod(env,jclazz,jmethodStaticID1,jstring1);
    //成功调用sayHello方法。

5)C中修改更新UI

   //native 只能写在更新UI的类中。
    public native void callBackShowToast();
    public void showToast(){
        Toast.makeText(this,"C中调用更改UI的方法",Toast.LENGTH_SHORT).show();
    }

C中实现:

/**
 * instance:谁调用了当前Java_wangjin_com_cmakendkdemo_MainActivity_callBackShowToast对应的Java接口
 * 谁就是实例instance,当改变UI时,只能是在UI线程中,此处的实例是activity
 * @param env
 * @param instance
 */
JNIEXPORT void JNICALL
Java_wangjin_com_cmakendkdemo_MainActivity_callBackShowToast(JNIEnv *env, jobject instance) {
    //1、得到字节码
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass jclazz = (*env)->FindClass(env,"wangjin/com/cmakendkdemo/MainActivity");
    //2、得到方法
    //jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
    //第3个参数是要调用的方法名,第4个参数是方法签名
    jmethodID jmethodStaticID1=(*env)->GetMethodID(env,jclazz,"showToast","()V");
    //3、调用方法 无返回值,注意,这里的jobject是instance
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,instance,jmethodStaticID1);
    //成功调用showToast方法。
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值