[jni] 09_C代码回调Java代码-6种情况

Android 4.0

C代码回调Java代码-ndkcallback

使用javap -s 查看方法的签名时,字母代表的意义
jni.h
typedef
  union  jvalue {
    jboolean    z;
    jbyte       b;
    jchar       c;
    jshort      s;
    jint        i;
    jlong       j;
    jfloat      f;
    jdouble     d;
    jobject     l;
} jvalue;

一、C调用java中返回值为void,无参数的方法
步骤:
1、建立一个Android工程
2、建立一个java类。
DataProvider编写要被c调用的方法helloFromJava()
// 1、C调用java空方法
public void helloFromJava() {
    System.out.println("C调用java空方法  helloFromJava()");
}  
提供nativte方法callMethodFromJava1(),通过这个方法调用c中的方法,c中的方法再调用java编写的方法
// java中调用c代码,然后c代码再调用java代码
public native void callMethodFromJava1();  
3、用javah生成方法的头文件(在src目录下)
4、在工程目录下建立jni文件,建立c代码,引入头文件,实现native方法,
5、建立android.mk文件(参考文档:C:\android-ndk-r9b\docs\ANDROID-MK.html)
6、c代码的逻辑实现
**c代码如何找到java方法?类似java中的反射
a) 找到java类
jclass      (*FindClass)(JNIEnv*, const char*);  //参数2是类的全路径名,从src目录下开始写 (cn/...)
jclass clazz = (*env)->FindClass(env,"cn/zengfanheng/ccallinjava/DataProvider");  
b) 获取MethodID
jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
 //参数2:该方法所在的类,参数3:方法名,
//参数4:方法的签名(通过javap -s查看)
通过javap,来获取方法的详细签名信息,返回值如b,不知道看jni.h,有一个struct列举了
定位到 工程名\bin\classes目录下:
javap  -s  cn.zengfanheng.ccallinjava.DataProvider

        jmethodID methodId1 = (*env)->GetMethodID(env,clazz,"helloFromJava""()V");  
c) 调用java的方法
void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
//参数2:调用方法对象,参数2:方法id,参数3:方法的参数(为可变参数列表)
      (*env)->CallVoidMethod(env,obj,methodId1);
7、使用cygwin编译c代码成可执行的二进制代码
       $ ndk-build
8、在java代码中加载该二进制代码
static {
    // LOCAL_MODULE := callMethodFromJava
    // libcallMethodFromJava.so
    System.loadLibrary("callMethodFromJava");
}
9、java代码中调用该方法
10、结果:

二、C调用java中返回值为int,参数为2个int的方法
a、findClass
// jclass      (*FindClass)(JNIEnv*, const char*);
jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");  

b、GetMethodId 找到java中要调用的方法的id (env,jcalss,方法名,方法签名)
//jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
jmethodID methodId2 = (*env)->GetMethodID(env,dpclazz,"addFromJava","(II)I");   
// (II)I  int (int,int),其他查看jni.h的 union jvalue
//jmethodID methodId2 = (*env)->GetMethodID(env,dpclazz,"addFromJava","  (II)I");//注意:参数四,前面不要有空格 
 

c、CallIntMethod  调用返回值为int的方法,参数为2个int
//jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
int x = 3,y = 5;
jint addresult = (*env)->CallIntMethod(env,obj,methodId2,x,y);  

d、结果:


三、C调用java中返回值为void(该为String看看),参数为String的方法
调用java中的方法:public void printStringFromJava(String s)  
a、findClass
//jclass      (*FindClass)(JNIEnv*, const char*);  
jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");  

b、GetMethodId
//jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);  
jmethodID methodId3 = (*env)->GetMethodID(env,dpclazz,"printStringFromJava","(Ljava/lang/String;)V");
  
c、将char*转成java中的String类型
//string     (*NewStringUTF)(JNIEnv*, const char*);  
char* ch = "→i am from c !";
jstring jstr =  (*env)->NewStringUTF(env,ch);  

d、调用返回值为空的,参数为String的方法
//void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);  
(*env)->CallVoidMethod(env,obj,methodId3,jstr);  

e、结果:



四、C调用java中参数为String的方法,返回String
步骤:
a、jclass      (*FindClass)(JNIEnv*, const char*);
b、jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
c、jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
核心代码:
// 4、C调用java中参数为string的方法,返回String   public String getNameFromJava(String s)
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_DataProvider_callMethodFromJava4
  (JNIEnv *env, jobject obj){
    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }
    //2、找到java中要调用的方法的id (..方法名,方法签名)
    //jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId4 = (*env)->GetMethodID(env,dpclazz,"getNameFromJava","(Ljava/lang/String;)Ljava/lang/String;");
    if(methodId4==0){
        LOGI("find methodId4 error");
        return;
    }
    LOGI("find methodId4 success ");
    //3、执行方法
    //jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
    char* ch = "hacket";
    jstring jstr =  (*env)->NewStringUTF(env,ch);
    jstring name = (*env)->CallObjectMethod(env,obj,methodId4,jstr);
    char* myname = Jstring2CStr(env,name);
    LOGI("c--%s",myname);
    LOGI("callMethodFromJava4------------------------end");
}  
结果:


五、C调用java中静态的返回值为void方法,参数为String
1、步骤:
a、 jclass   (*FindClass)(JNIEnv*, const char*);
b、 jmethodID(*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
c、void   (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
2、核心代码:
//5、C调用java中static静态方法,参数为String,返回为void , public static void printStaticStr(String s)
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_DataProvider_callMethodFromJava5
(JNIEnv * env, jobject obj) {

    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }

    //注意:静态的方法,不一样
    //2、找到java中要调用的方法的id (..方法名,方法签名)
    // jmethodID   (*GetStaticMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId5 = (*env)->GetStaticMethodID(env,dpclazz,"printStaticStr","(Ljava/lang/String;)V");
    if(methodId5==0){
        LOGI("find methodId5 error");
        return;
    }
    LOGI("find methodId5 success ");

    //3、执行方法,静态方法,返回void,参数String,调用static方法
    //void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    char* ch = "i am in c static";
    jstring jstr =  (*env)->NewStringUTF(env,ch);
    (*env)->CallStaticVoidMethod(env,dpclazz,methodId5,jstr);
    LOGI("callMethodFromJava5------------------------end");
}
3、结果:


六、native代码和调用的java代码不在同一个类里面
1、步骤:
a、 jclass      (*FindClass)(JNIEnv*, const char*);
b、 jmethodID(*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
c、找到调用方法的类的对象
由于不是在同一类中,声明的native方法,故此obj代表的是MainActivity,obj不对。而要调用的方法在DataProvider,所以要new出来调用java的方法所在的类
jobject     (*AllocObject)(JNIEnv*, jclass);
d、调用方法
2、核心代码:
//6、C调用的方法的和native方法的声明不在同一个类中 ,在不同的类中,调用helloFromJava
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_MainActivity_call_1dp_1callMethodFromJava1
(JNIEnv *env, jobject obj) {
    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }
    //2、找到java中要调用的方法的id (..方法名,方法签名)
    //jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId6 = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");
    if(methodId6==0){
        LOGI("find methodId6 error");
        return;
    }
    LOGI("find methodId6 success ");
    //3、由于不是在同一类中,声明的native方法,此obj代表的是MainActivity,obj不对。而要调用的方法在DataProvider,所以要new出来调用java的方法所在的类
    //    jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject dp_obj = (*env)->AllocObject(env,dpclazz);
    //4、执行方法,由于不是在同一类中,obj不同
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,dp_obj,methodId6,jstr);
    LOGI("callMethodFromJava6------------------------end");
}  
3、结果:


其他new对象的方法
void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
jobject     (*AllocObject)(JNIEnv*, jclass);
jobject     (*NewObject)(JNIEnv*, jclass, jmethodID, ...);
全部核心代码:
#include <stdio.h>
#include <jni.h>
#include <malloc.h>
#include <android/log.h>

#include "cn_zengfanheng_ccallinjava_DataProvider.h"
#include "cn_zengfanheng_ccallinjava_MainActivity.h"

#define LOG_TAG "System.out.c" //在LogCat上打印出来的Tag
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)

//零、工具类:返回值 char* 这个代表char数组的首地址
//Jstring2CStr 把java中的jstring的类型转化成一个c语言中的char 字符串
charJstring2CStr(JNIEnv* env, jstring jstr) {
    char* rtn = NULL;
    jclass clsstring = (*env)->FindClass(env, "java/lang/String"); //String
    jstring strencode = (*env)->NewStringUTF(env, "GB2312"); // 得到一个java字符串 "GB2312"
    jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes",
            "(Ljava/lang/String;)[B"); //[ String.getBytes("gb2312");
    jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid,
            strencode); // String .getByte("GB2312");
    jsize alen = (*env)->GetArrayLength(env, barr); // byte数组的长度
    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;
}

// 一、C调用java空方法,返回void  , public void helloFromJava()
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_DataProvider_callMethodFromJava1
(JNIEnv *env, jobject obj) {

    //调用过程类似java中的反射
    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env,"cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }

    //2、找到java中要调用的方法的id (..方法名,方法签名)
    // jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId1 = (*env)->GetMethodID(env,dpclazz,"helloFromJava""()V");
    if(methodId1==0){
        LOGI("find method1 error");
        return;
    }
    LOGI("find methodId1 success ");

    //3、调用返回值为空的方法
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,obj,methodId1);
    LOGI("callMethodFromJava1------------------------end");
}

// 二、C调用java中的带两个int参数的方法,然后返回一个int值 , public int addFromJava(int x, int y)
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_DataProvider_callMethodFromJava2
(JNIEnv * env, jobject obj) {

    //1、找到这个类
    // jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
            LOGI("find class error");
            return;
    }

    //2、找到java中要调用的方法的id (env,jcalss,方法名,方法签名)
    //jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId2 = (*env)->GetMethodID(env,dpclazz,"addFromJava","(II)I");   // (II)I  int (int,int),其他查看jni.h的 union jvalue
    //jmethodID methodId2 = (*env)->GetMethodID(env,dpclazz,"addFromJava","  (II)I");//注意:参数四,前面不要有空格
    if(methodId2==0){
            LOGI("find method2 error");
            return;
    }
    LOGI("find methodId2 success ");

    //3、调用返回值为int的方法
    //jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    int x = 3,y = 5;
    jint addresult = (*env)->CallIntMethod(env,obj,methodId2,x,y);
    LOGI("%d+%d=%d",x,y,addresult);
    LOGI("callMethodFromJava2------------------------end");
}

// 三、C调用java中参数为string的方法,返回void ,  public void printStringFromJava(String s)
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_DataProvider_callMethodFromJava3
(JNIEnv *env, jobject obj) {

    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }

    //2、找到java中要调用的方法的id (..方法名,方法签名)
    //jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId3 = (*env)->GetMethodID(env,dpclazz,"printStringFromJava","(Ljava/lang/String;)V");
    if(methodId3==0){
        LOGI("find methodId3 error");
        return;
    }
    LOGI("find methodId3 success ");

    //3、调用返回值为空的,参数为String的方法
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    //将char*转换成java中的String类型。
    //jstring     (*NewStringUTF)(JNIEnv*, const char*);
    char* ch = "→i am from c !";
    jstring jstr =  (*env)->NewStringUTF(env,ch);
    (*env)->CallVoidMethod(env,obj,methodId3,jstr);
    LOGI("callMethodFromJava3------------------------end");
}

// 4、C调用java中参数为string的方法,返回String   public String getNameFromJava(String s)
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_DataProvider_callMethodFromJava4
  (JNIEnv *env, jobject obj){

    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }

    //2、找到java中要调用的方法的id (..方法名,方法签名)
    //jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId4 = (*env)->GetMethodID(env,dpclazz,"getNameFromJava","(Ljava/lang/String;)Ljava/lang/String;");
    if(methodId4==0){
        LOGI("find methodId4 error");
        return;
    }
    LOGI("find methodId4 success ");

    //3、执行方法
    //jobject     (*CallObjectMethod)(JNIEnv*, jobject, jmethodID, ...);
    char* ch = "hacket";
    jstring jstr =  (*env)->NewStringUTF(env,ch);
    jstring name = (*env)->CallObjectMethod(env,obj,methodId4,jstr);
    char* myname = Jstring2CStr(env,name);
    LOGI("c--%s",myname);
    LOGI("callMethodFromJava4------------------------end");
}

//5、C调用java中static静态方法,参数为String,返回为void , public static void printStaticStr(String s)
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_DataProvider_callMethodFromJava5
(JNIEnv * env, jobject obj) {

    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }

    //注意:静态的方法,不一样
    //2、找到java中要调用的方法的id (..方法名,方法签名)
    // jmethodID   (*GetStaticMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId5 = (*env)->GetStaticMethodID(env,dpclazz,"printStaticStr","(Ljava/lang/String;)V");
    if(methodId5==0){
        LOGI("find methodId5 error");
        return;
    }
    LOGI("find methodId5 success ");

    //3、执行方法,静态方法,返回void,参数String
    //void        (*CallStaticVoidMethod)(JNIEnv*, jclass, jmethodID, ...);
    char* ch = "i am in c static";
    jstring jstr =  (*env)->NewStringUTF(env,ch);
    (*env)->CallStaticVoidMethod(env,dpclazz,methodId5,jstr);
    LOGI("callMethodFromJava5------------------------end");
}

//6、C调用的方法的和native方法的声明不在同一个类中 ,在不同的类中,调用helloFromJava
JNIEXPORT void JNICALL Java_cn_zengfanheng_ccallinjava_MainActivity_call_1dp_1callMethodFromJava1
(JNIEnv *env, jobject obj) {

    //1、找到这个类
    //jclass      (*FindClass)(JNIEnv*, const char*);
    jclass dpclazz = (*env)->FindClass(env, "cn/zengfanheng/ccallinjava/DataProvider");
    if (dpclazz==0) {
        LOGI("find class error");
        return;
    }

    //2、找到java中要调用的方法的id (..方法名,方法签名)
    //jmethodID   (*GetMethodID)(JNIEnv*, jclassconst char*, const char*);
    jmethodID methodId6 = (*env)->GetMethodID(env,dpclazz,"helloFromJava","()V");
    if(methodId6==0){
        LOGI("find methodId6 error");
        return;
    }
    LOGI("find methodId6 success ");

    //3、由于不是在同一类中,声明的native方法,此obj代表的是MainActivity,obj不对。而要调用的方法在DataProvider,所以要new出来调用java的方法所在的类
    //    jobject     (*AllocObject)(JNIEnv*, jclass);
    jobject dp_obj = (*env)->AllocObject(env,dpclazz);

    //4、执行方法,由于不是在同一类中,obj不同
    //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
    (*env)->CallVoidMethod(env,dp_obj,methodId6,jstr);
    LOGI("callMethodFromJava6------------------------end");

}
全部结果: