android jni c语言回调java

上一篇介绍了 java调用c方法返回值,今天总结下c调用java。

大体说下步骤,第一步是 找到class,第二步找到方法,第三步是env指针 进行调用。类似于反射。 下面详细说一下。
  1. 书写java本地代码,调用c方法,并且书写提供给c语言调用的java方法。这里多写几个
public class NativeMethods {
    Context context;

    //  加载ku
    static {
        System.loadLibrary("callback");
    }

    public NativeMethods(Context context) {
        this.context = context;
    }

    //调用c语言 无参无返回
    public native void callBack();

    //调用c语言 物参有返回
    public native String callBackReturnString();

    //调用c语言 有参数又返回值
    public native String callBackWithParmString();

    //c的 callBack函数 回调java的方法(无参无返回)
    public void callBackFromC() {
        Toast.makeText(context, "c调用了java", Toast.LENGTH_SHORT).show();
    }

    //c的 callBackReturnString函数 回调java的方法(无参返回string)
    public String callBackFromCString() {
        return "来自java的字符串";
    }

    //c语言 callBackWithParmString回调  有参数又返回值
    public int add(int a, int b) {
        return a + b;
    }

      //c回调的静态方法
    public static String callBackStatic() {
        return "java的静态方法";
    }

    //调用c方法,c方法回调静态方法。
    public  native String callStaticFromc();

}

2.接下来就是生成头文件了,在model目录下的 src/main/java目录下,执行javah

javah -d ../jni  com.example.callbcakdemo.NativeMethods
这样就在java的上层也就是main下面自动生成了jni目录和头文件,下面就是写c代码了

3. c调用java的非静态 方法。
首先是我们要用java调用c,然后在c里面回调我们的java方法。,这里先说非静态的方法
步骤分下面几步
a.得到类的class
b.得到方法id
c.调用方法
这三步都是env指针提供的方法。
首选来看下无参无返回的callBack方法
第一步得到class使用的env的findclass方法拿到jclass返回值

 jclass clazz = (*env)->FindClass(env, "com/example/callbcakdemo/NativeMethods");

第一个参数是env指针,后面的是类的全名
这里可以用log判断下是否拿到了class

    if (clazz == 0)
        LOGE("class is error ");
    else
        LOGE("class is ok");

¥##################################################################
第二步查找方法id,找到方法。
查找方法使用的env的GetMethodID方法(对于非静态的方法,如果是静态方法使用GetstaticMethodID)

 jmethodID   (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);

GetMethodID方法接受四个参数,第一个是env指针,第二个刚才第一步找到jclass,第三个是你要调用的方法名称,第四个参数是你要调用的方法签名。 签名这里注意,需要使用javap方法来生成签名。
javap是对class进行类似反编译拿到参数列表。进入到studio的class目录,是在build下面的intermediates/classes/目录。。
然后进入回调方法所在的包,这里和native写在一个文件里面。

 cd build/intermediates/classes/debug/com/example/callbcakdemo/

然后执行javap 要参数-s -p

 javap -s -p NativeMethods.class 

然后看下输出结果

public class com.example.callbcakdemo.NativeMethods {
  android.content.Context context;
    descriptor: Landroid/content/Context;
  public com.example.callbcakdemo.NativeMethods(android.content.Context);
    descriptor: (Landroid/content/Context;)V

  public native void callBack();
    descriptor: ()V

  public native java.lang.String callBackReturnString();
    descriptor: ()Ljava/lang/String;

  public native java.lang.String callBackWithParmString();
    descriptor: ()Ljava/lang/String;

  public void callBackFromC();
    descriptor: ()V

  public java.lang.String callBackFromCString();
    descriptor: ()Ljava/lang/String;

  public int add(int, int);
    descriptor: (II)I

  static {};
    descriptor: ()V
}

每个方法都descriptor,后面的就是env getmethodid需要的第四个参数,仔细看看不难发现规律
这样我们就可以得到方法的id,对于非静态的方法这样获取id。各个方法的签名不同

    //无参无返回的方法,
   jmethodID jmethod1 = (*env)->GetMethodID(env, clazz, "callBackFromC", "()V");
   //物参数返回string
    jmethodID jmethod1 = (*env)->GetMethodID(env, clazz, "callBackFromCString",
                                             "()Ljava/lang/String;");
     //接受2int,返回一个int                                       
  jmethodID jmethod1 = (*env)->GetMethodID(env, clazz, "add",
                                             "(II)I");

这样就能拿到方法id了进行调用了。

¥#################################################################
第三步进行调用
调用java方法根据方法的签名和返回值不同而不同。 调用方法是(env)->callMethod,type是返回值的类型。方法参数列表一般为(JNIEnv, jobject, jmethodID, …)。第一个是env,第二个是object的一个实例,就是c方法第二个参数jobject,这里用上了,第三个是方法的id,就是刚才找到的,后面是一个可变参数,有几个就写几个就行了。

    //调用无参无返回的。
    (*env)->CallVoidMethod(env, obj, jmethod1);
    //调用无参数 返回string
       jstring res = (*env)->CallObjectMethod(env, obj, jmethod1);
      //调用传入2个int,返回一个int的
      jint res = (*env)->CallIntMethod(env, obj, jmethod1, 2, 5);

其中规律不难发现,打开jni.h都可以看到这些方法的定义,可以看到具体的类型。由于返回string没有cllstringmethod的方法,所以调用了object,返回用jstring接受下就可以了。
到这里就是c调用java的非静态方法,一会贴上全部代码

4。调用静态方法
第一步 找class是一样的,第二部,第三步不一样了。寻找方法id需要使用getstaticmethodid来找,调用的时候也要是用static来调用。看下例子。

JNIEXPORT jstring JNICALL Java_com_example_callbcakdemo_NativeMethods_callStaticFromc
        (JNIEnv *env, jobject obj)
{


    jclass clazz = (*env)->FindClass(env, "com/example/callbcakdemo/NativeMethods");
    if (clazz == 0)
        LOGE("class is error ");
    else
        LOGE("class is ok");

    jmethodID jmethod1 = (*env)->GetStaticMethodID(env, clazz, "callBackStatic",
                                                   "()Ljava/lang/String;");
    if (jmethod1 == 0)
        LOGE("methodid error");
    else
        LOGE("method ok");

    jstring res = (*env)->CallStaticObjectMethod(env, clazz, jmethod1);
    return res;

}

静态和非静态的区别需要注意调用的时候静态是用class,非静态是用实例。 ok。下面贴上源码

首先是定义nativie方法和 回调方法的类,

package com.example.callbcakdemo;

import android.content.Context;
import android.widget.Toast;


public class NativeMethods {
    Context context;

    //  加载ku
    static {
        System.loadLibrary("callback");
    }

    public NativeMethods(Context context) {
        this.context = context;
    }

    //调用c语言 无参无返回
    public native void callBack();

    //调用c语言 物参有返回
    public native String callBackReturnString();

    //调用c语言 有参数又返回值
    public native String callBackWithParmString();

    //c的 callBack函数 回调java的方法(无参无返回)
    public void callBackFromC() {
        Toast.makeText(context, "c调用了java", Toast.LENGTH_SHORT).show();
    }

    //c的 callBackReturnString函数 回调java的方法(无参返回string)
    public String callBackFromCString() {
        return "来自java的字符串";
    }

    //c语言 callBackWithParmString回调  有参数又返回值
    public int add(int a, int b) {
        return a + b;
    }

    //c回调的静态方法
    public static String callBackStatic() {
        return "java的静态方法";
    }

    //调用c方法,c方法回调静态方法。
    public  native String callStaticFromc();

}

下面是生成h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>

#ifndef _Included_com_example_callbcakdemo_NativeMethods
#define _Included_com_example_callbcakdemo_NativeMethods
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_example_callbcakdemo_NativeMethods_callBack
  (JNIEnv *, jobject);

JNIEXPORT jstring JNICALL Java_com_example_callbcakdemo_NativeMethods_callBackReturnString
  (JNIEnv *, jobject);

JNIEXPORT jstring JNICALL Java_com_example_callbcakdemo_NativeMethods_callBackWithParmString
  (JNIEnv *, jobject);

JNIEXPORT jstring JNICALL Java_com_example_callbcakdemo_NativeMethods_callStaticFromc
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

.c文件

#include "com_example_callbcakdemo_NativeMethods.h"

#include <android/log.h>

#define TAG "callBack" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型



//c 用java 没有返回值的调用
JNIEXPORT void JNICALL Java_com_example_callbcakdemo_NativeMethods_callBack
        (JNIEnv *env, jclass obj)
{
    jclass clazz = (*env)->FindClass(env, "com/example/callbcakdemo/NativeMethods");
    if (clazz == 0)
        LOGE("class is error ");
    else
        LOGE("class is ok");

    jmethodID jmethod1 = (*env)->GetMethodID(env, clazz, "callBackFromC", "()V");
    if (jmethod1 == 0)
        LOGE("methodid error");
    else
        LOGE("method ok");

    (*env)->CallVoidMethod(env, obj, jmethod1);
}


//调用java 不带参数 返回string
JNIEXPORT jstring JNICALL Java_com_example_callbcakdemo_NativeMethods_callBackReturnString
        (JNIEnv *env, jobject obj)
{

    jclass clazz = (*env)->FindClass(env, "com/example/callbcakdemo/NativeMethods");
    if (clazz == 0)
        LOGE("class is error ");
    else
        LOGE("class is ok");

    jmethodID jmethod1 = (*env)->GetMethodID(env, clazz, "callBackFromCString",
                                             "()Ljava/lang/String;");
    if (jmethod1 == 0)
        LOGE("methodid error");
    else
        LOGE("method ok");

    jstring res = (*env)->CallObjectMethod(env, obj, jmethod1);

    return res;
}


//c调用java 出入参数,返回值
JNIEXPORT jstring JNICALL Java_com_example_callbcakdemo_NativeMethods_callBackWithParmString
        (JNIEnv *env, jobject obj)
{

    jclass clazz = (*env)->FindClass(env, "com/example/callbcakdemo/NativeMethods");
    if (clazz == 0)
        LOGE("class is error ");
    else
        LOGE("class is ok");

    jmethodID jmethod1 = (*env)->GetMethodID(env, clazz, "add",
                                             "(II)I");
    if (jmethod1 == 0)
        LOGE("methodid error");
    else
        LOGE("method ok");

    jint res = (*env)->CallIntMethod(env, obj, jmethod1, 2, 5);
    char string[2];
    string[0] = res + '0';
    string[1] = '\0';
    return (*env)->NewStringUTF(env, string);


}

//call back static 
JNIEXPORT jstring JNICALL Java_com_example_callbcakdemo_NativeMethods_callStaticFromc
        (JNIEnv *env, jobject obj)
{


    jclass clazz = (*env)->FindClass(env, "com/example/callbcakdemo/NativeMethods");
    if (clazz == 0)
        LOGE("class is error ");
    else
        LOGE("class is ok");

    jmethodID jmethod1 = (*env)->GetStaticMethodID(env, clazz, "callBackStatic",
                                                   "()Ljava/lang/String;");
    if (jmethod1 == 0)
        LOGE("methodid error");
    else
        LOGE("method ok");

    jstring res = (*env)->CallStaticObjectMethod(env, clazz, jmethod1);
    return res;

}

最后就是mainactivity的测试代码,这里就不说了,自己调用就行。
demo下载地址:http://download.csdn.net/detail/spinchao/9594550

猜你喜欢

转载自blog.csdn.net/spinchao/article/details/52092335
今日推荐