1. 程式人生 > >android jni c語言回撥java

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;

}