Qt (QAndroidJniObject) 調用 Java 方法完全指南:callMethod vs callObjectMethod 選型與避坑
核心規則:根據 Java 方法返回類型選擇調用函數
| Java 返回類型 | Qt 調用函數 | 返回值類型 | 示例 |
| 基本類型 | callMethod<ReturnType>() | 對應基本類型 | jint, jboolean, jdouble |
void | void (無返回) | void |
| 對象類型 | callObjectMethod<ReturnType>() | QAndroidJniObject | jstring, jobject, jarray |
| String | QAndroidJniObject | jstring |
| 數組/集合 | QAndroidJniObject | jarray, jobject (List/Map) |
錯誤案例:調用返回 String 的方法卻用 callMethod
// 錯誤:返回 jstring 屬於對象類型,不能用 callMethod
QAndroidJniObject result = QAndroidJniObject::callStaticMethod(
"com/example/Utils", "getString", "()Ljava/lang/String;");
// 編譯錯誤:undefined reference to callStaticMethod
// 正確:使用 callObjectMethod
QAndroidJniObject result = QAndroidJniObject::callStaticObjectMethod(
"com/example/Utils", "getString", "()Ljava/lang/String;");
// result 是 QAndroidJniObject,需轉換
QString cppStr = result.toString();
完整調用流程示例
Java 端
package com.example.qtbridge;
public class MathUtils {
// 靜態方法:基本類型
public static int add(int a, int b) { return a + b; }
public static double calc(double x) { return Math.sin(x); }
// 靜態方法:對象類型
public static String greet(String name) { return "Hello, " + name; }
public static int[] generateArray(int n) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) arr[i] = i * 2;
return arr;
}
// 實例方法
public String instanceMethod(String input) { return "Instance: " + input; }
}
Qt/C++ 端
#include
#include
#include
void callJavaMethods() {
// 1. 調用靜態基本類型方法
jint sum = QAndroidJniObject::callStaticMethod(
"com/example/qtbridge/MathUtils",
"add",
"(II)I",
10, 20
);
qDebug() << "add(10,20) =" << sum; // 30
// 2. 調用靜態對象類型方法 (String)
QAndroidJniObject javaStr = QAndroidJniObject::callStaticObjectMethod(
"com/example/qtbridge/MathUtils",
"greet",
"(Ljava/lang/String;)Ljava/lang/String;",
QAndroidJniObject::fromString("Qt").object()
);
QString cppStr = javaStr.toString(); // "Hello, Qt"
qDebug() << "greet =" << cppStr;
// 3. 調用靜態數組方法
QAndroidJniObject arrObj = QAndroidJniObject::callStaticObjectMethod(
"com/example/qtbridge/MathUtils",
"generateArray",
"(I)[I",
5
);
// 轉換 jintArray 到 C++ 容器
QAndroidJniEnvironment env;
jintArray arr = static_cast(arrObj.object());
jsize len = env->GetArrayLength(arr);
jint* elems = env->GetIntArrayElements(arr, nullptr);
QVector cppVec;
for (jsize i = 0; i < len; ++i) cppVec.append(elems[i]);
env->ReleaseIntArrayElements(arr, elems, JNI_ABORT);
qDebug() << "generateArray(5) =" << cppVec; // [0, 2, 4, 6, 8]
// 4. 調用實例方法 (需先創建 Java 對象)
QAndroidJniObject mathUtilsObj("com/example/qtbridge/MathUtils");
QAndroidJniObject result = mathUtilsObj.callObjectMethod(
"instanceMethod",
"(Ljava/lang/String;)Ljava/lang/String;",
QAndroidJniObject::fromString("C++").object()
);
qDebug() << "instanceMethod =" << result.toString(); // "Instance: C++"
}
關鍵簽名規則 (JNI 類型簽名)
| Java 類型 | JNI 簽名 |
| boolean | Z |
| byte | B |
| char | C |
| short | S |
| int | I |
| long | J |
| float | F |
| double | D |
| void | V |
| String | Ljava/lang/String; |
| Object | Lfully/qualified/ClassName; |
| int[] | [I |
| Object[] | [Lfully/qualified/ClassName; |
| 方法簽名 | (參數簽名)返回簽名 例:(ILjava/lang/String;)V |
常見陷阱與解決
| 陷阱 | 現象 | 解決 |
| 基本/對象類型調用函數混用 | 編譯錯誤 undefined reference / 運行崩潰 | 嚴格對照返回類型選擇 callMethod / callObjectMethod |
| 簽名字符串錯誤 | NoSuchMethodError / 參數不匹配 | 使用 javap -s ClassName 生成準確簽名 |
| 局部引用泄漏 | JNI 表滿導致崩潰 | 及時 DeleteLocalRef / 使用 QAndroidJniEnvironment 管理 |
| 線程未附加 JVM | JNIEnv 為空 / 崩潰 | 非 Qt 主線程需 QAndroidJniEnvironment env; env->AttachCurrentThread() |
| ProGuard 混淆導致找不到類/方法 | 運行時 ClassNotFound / NoSuchMethod | 在 proguard-rules.pro 保留類:-keep class com.example.qtbridge.** { *; } |
最佳實踐建議
- 封裝 JNI 調用層:C++ 端建立專門的 Bridge 類,隱藏簽名、轉換、異常處理細節。
- 統一異常處理:調用後檢查
QAndroidJniEnvironment::checkAndClearExceptions()。
- 字符串轉換工具:
QAndroidJniObject::fromString() (C++→Java)、.toString() (Java→C++)。
- 數組/集合轉換:編寫通用模板函數處理
jintArray, jobjectArray, List, Map。
- 單元測試:在 Android 模擬器/真機上測試所有 JNI 調用路徑。
總結
Qt Android 通過 QAndroidJniObject 呼叫 Java,核心在於根據 Java 方法返回類型選擇 callMethod (基本類型/void) 或 callObjectMethod (對象類型)。掌握 JNI 類型簽名、引用管理、線程附加與 ProGuard 保護,構建穩健的 C++/Java 混合開發體驗。