android jni c語言回撥java
上一篇介紹了 java呼叫c方法返回值,今天總結下c呼叫java。
大體說下步驟,第一步是 找到class,第二步找到方法,第三步是env指標 進行呼叫。類似於反射。 下面詳細說一下。
- 書寫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;
}