【Flutter跨平臺插件開發(fā)】如何實現(xiàn)kotlin跟C++的相互調(diào)用
kotlin 調(diào) c++
在 Kotlin 中,可以使用 JNI (Java Native Interface) 來調(diào)用 C++ 代碼
調(diào)用步驟:
- 創(chuàng)建 C++ 文件并實現(xiàn)函數(shù)。
// example.cpp
#include <jni.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_MyClass_myFunction(JNIEnv* env, jobject /* this */) {
return env->NewStringUTF("Hello from C++");
}
- 在 Kotlin 中聲明需要調(diào)用的 native 函數(shù)并加載 native 庫。
class MyClass {
external fun myFunction(): String
companion object {
init {
System.loadLibrary("example") // example是庫的名字
}
}
}
- 調(diào)用示例
val myClass = MyClass()
println(myClass.myFunction()) // 輸出 "Hello from C++"
Flutter 插件項目的例子
在 Flutter 插件中引用已有的 C++ 源碼需要以下步驟:
- 首先,在 Flutter 插件的 android 目錄下創(chuàng)建一個 CMakeLists.txt 文件,這個文件會告訴 CMake 如何編譯你的 C++ 代碼。
cmake_minimum_required(VERSION 3.4.1)
add_library( # Specifies the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
- 然后,在插件的 build.gradle 文件中啟用 CMake 并指定 CMakeLists.txt 文件的位置。
android {
// ...
defaultConfig {
// ...
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}
- 在 Kotlin 代碼中,可以使用
System.loadLibrary
來加載庫,并使用external
關(guān)鍵字來聲明 native 方法。
class MyPlugin: FlutterPlugin, MethodCallHandler {
// ...
external fun myNativeMethod(): String
init {
System.loadLibrary("native-lib")
}
// ...
}
- 最后,在 C++ 代碼中實現(xiàn)函數(shù)。
#include <jni.h>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_MyPlugin_myNativeMethod(JNIEnv* env, jobject /* this */) {
// 你的代碼...
}
注意
- 需要使用
ndk-build
或者 CMake 來編譯 C++ 代碼,并將生成的庫放到 Android 項目的jniLibs
目錄下。 - C++ 函數(shù)的名稱必須遵循特定的格式:
Java_包名_類名_方法名
。在這個例子中,Java_com_example_MyClass_myFunction
對應(yīng)于com.example.MyClass
類的myFunction
方法。
c++ 調(diào) kotln
在 Kotlin 中,可以使用 JNI (Java Native Interface) 來設(shè)置回調(diào)到 C++ 代碼。
同步調(diào)用(這個需要kotlin先調(diào)到cpp,cpp再調(diào)回來,都是同步操作)
- 在 Kotlin 中創(chuàng)建一個接口,該接口將被 C++ 代碼調(diào)用。
interface Callback {
fun onEvent(event: String)
}
- 創(chuàng)建一個 native 函數(shù),kotlin 的回調(diào)函數(shù)將通過這個native函數(shù),傳給cpp
class MyClass {
private var callback: Callback? = null
fun setCallback(callback: Callback) {
this.callback = callback
}
external fun triggerEvent()
private fun onEvent(event: String) {
callback?.onEvent(event)
}
companion object {
init {
System.loadLibrary("example")
}
}
}
- 在 C++ 代碼中實現(xiàn)
triggerEvent
函數(shù),從參數(shù)中獲取onEvent
方法并調(diào)用。
#include <jni.h>
extern "C" JNIEXPORT void JNICALL
Java_com_example_MyClass_triggerEvent(JNIEnv* env, jobject instance) {
jclass cls = env->GetObjectClass(instance);
jmethodID mid = env->GetMethodID(cls, "onEvent", "(Ljava/lang/String;)V");
if (mid == nullptr) return; // method not found
jstring message = env->NewStringUTF("Hello from C++");
env->CallVoidMethod(instance, mid, message);
}
在這個例子中,triggerEvent
函數(shù)在 C++ 代碼中被調(diào)用,然后它調(diào)用 Kotlin 中的 onEvent
方法,該方法然后調(diào)用 Callback
接口的 onEvent
方法。
方法簽名
在 JNI (Java Native Interface) 中,“(Ljava/lang/String;)V” 是一個方法簽名,用于描述方法的參數(shù)類型和返回值類型
這個簽名可以被分解為以下部分:
-
括號 “(” 和 “)”:括號內(nèi)的內(nèi)容描述了方法的參數(shù)類型。在這個例子中,“Ljava/lang/String;” 表示方法有一個參數(shù),類型為
java.lang.String
。 -
“V”:這是方法的返回值類型。在 JNI 中,“V” 表示 void,也就是說這個方法沒有返回值。
所以,“(Ljava/lang/String;)V” 這個簽名表示的是一個接受一個 java.lang.String
參數(shù)并且沒有返回值的方法。
其他一些常見的 JNI 類型簽名包括:
- “I”:表示 int
- “J”:表示 long
- “S”:表示 short
- “B”:表示 byte
- “C”:表示 char
- “D”:表示 double
- “F”:表示 float
- “Z”:表示 boolean
- “[I”:表示 int 數(shù)組
- “Lfully/qualified/ClassName;”:表示 fully.qualified.ClassName 類型的對象
你可以在 JNI 文檔中找到更多關(guān)于類型簽名的信息。
異步回調(diào)
在 JNI 中,JNIEnv*
和 jobject
通常不能直接保存起來用于異步回調(diào)。
這是因為:
-
JNIEnv*
是線程相關(guān)的,每個線程都有一個不同的JNIEnv*
。如果你在一個線程保存了JNIEnv*
,然后在另一個線程使用它,可能會導(dǎo)致問題。 - 同樣,
jobject
是一個局部引用,它只在當(dāng)前的 JNI 調(diào)用中有效,調(diào)用結(jié)束后就會被自動刪除。
如果你需要在異步回調(diào)中使用這些對象,你需要做一些額外的步驟:
-
對于
JNIEnv*
,你需要在回調(diào)的線程中通過JavaVM*
獲取一個新的JNIEnv*
。你可以在保存JNIEnv*
的同時保存JavaVM*
,通過調(diào)用JNIEnv->GetJavaVM(&jvm)
獲取。 -
對于
jobject
,你需要創(chuàng)建一個全局引用,這樣它就可以跨越多個 JNI 調(diào)用。你可以通過調(diào)用JNIEnv->NewGlobalRef(jobject)
來創(chuàng)建一個全局引用。記住在你不再需要這個全局引用時,需要調(diào)用JNIEnv->DeleteGlobalRef(jobject)
來刪除它,防止內(nèi)存泄漏。
以下是一個簡單的例子:文章來源:http://www.zghlxwxcb.cn/news/detail-822279.html
JavaVM* jvm;
jobject globalObj;
JNIEXPORT void JNICALL Java_MyClass_init(JNIEnv* env, jobject obj) {
env->GetJavaVM(&jvm);
globalObj = env->NewGlobalRef(obj);
}
void asyncCallback() {
JNIEnv* env;
jvm->AttachCurrentThread(&env, NULL);
// 有了env跟obj后,這里參考上面同步調(diào)用的例子的實現(xiàn)
jvm->DetachCurrentThread();
}
在這個例子中,Java_MyClass_init
是一個 JNI 方法,它保存了 JavaVM*
和一個全局引用。然后在 asyncCallback
中,我們獲取了一個新的 JNIEnv*
,并使用了全局引用。注意我們在回調(diào)結(jié)束時調(diào)用了 DetachCurrentThread
,這是因為我們之前調(diào)用了 AttachCurrentThread
。如果你在一個已經(jīng)被附加到 JVM 的線程中調(diào)用回調(diào),你不需要調(diào)用這兩個方法。文章來源地址http://www.zghlxwxcb.cn/news/detail-822279.html
到了這里,關(guān)于【Flutter跨平臺插件開發(fā)】如何實現(xiàn)kotlin跟C++的相互調(diào)用的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!