Flutter原生代码集成插件ndk的使用
Flutter原生代码集成插件ndk的使用
简介
NDK(Nostr Development Kit)是一个用于增强Nostr开发体验的Dart库。它不仅提供了高级别的用例,如列表或元数据处理,同时也允许用户进行低级别的查询,并默认支持inbox/outbox(gossip)模型。本文将介绍如何在Flutter项目中集成和使用NDK插件,特别是涉及到NDK与原生代码(通过NDK或JNI)的交互部分。
准备工作
前置条件
- Dart SDK
- Android SDK(也适用于桌面构建)
- Flutter SDK
- Rust (+ 目标平台工具链)
Rust 工具链安装
对于Android:
rustup target add \
aarch64-linux-android \
armv7-linux-androideabi \
x86_64-linux-android \
i686-linux-android
对于iOS:
# 64位目标(真实设备 & 模拟器):
rustup target add aarch64-apple-ios x86_64-apple-ios
# 新版Xcode模拟器目标:
rustup target add aarch64-apple-ios-sim
# 32位目标(通常不需要):
rustup target add armv7-apple-ios i386-apple-ios
安装
在pubspec.yaml
文件中添加依赖:
dependencies:
ndk: ^latest_version
ndk_rust_verifier: ^latest_version # 可选
ndk_amber: ^latest_version # 可选
然后运行以下命令来安装包:
flutter pub get
导入
在Dart文件中导入所需的包:
import 'package:ndk/ndk.dart';
// 如果需要的话,也可以导入其他可选包
import 'package:ndk_rust_verifier/ndk_rust_verifier.dart';
import 'package:ndk_amber/ndk_amber.dart';
使用示例
下面是一个简单的示例,演示了如何初始化NDK实例并执行一个基本的查询请求:
import 'package:ndk/ndk.dart';
import 'package:ndk_rust_verifier/ndk_rust_verifier.dart';
void main() async {
// 初始化NDK实例
final ndk = Ndk(
NdkConfig(
eventVerifier: RustEventVerifier(),
cache: MemCacheManager(),
),
);
// 创建查询请求
final response = ndk.requests.query(
filters: [
Filter(
authors: ['3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d'],
kinds: [Nip01Event.kTextNodeKind],
limit: 10,
),
],
);
// 处理返回的事件流
await for (final event in response.stream) {
print(event);
}
}
更多关于Flutter原生代码集成插件ndk的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter原生代码集成插件ndk的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
在Flutter项目中集成原生代码并使用NDK(Native Development Kit)通常涉及以下几个步骤:创建Flutter插件、编写原生代码(C/C++)、配置CMake或ndk-build,并在Flutter端调用这些原生代码。以下是一个简单的示例,展示了如何在Flutter项目中集成NDK插件。
步骤 1: 创建Flutter插件
首先,我们需要创建一个Flutter插件。你可以使用Flutter命令行工具来生成插件模板。
flutter create --template=plugin my_ndk_plugin
cd my_ndk_plugin
步骤 2: 添加NDK支持
在生成的插件项目中,我们需要为Android原生代码添加NDK支持。打开android/app/build.gradle
文件,确保android
块中包含NDK配置(通常这是默认启用的,但你可以检查或添加):
android {
...
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags ""
}
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
步骤 3: 创建CMakeLists.txt
在android/app/src/main/cpp/
目录下创建一个CMakeLists.txt
文件,用于配置CMake构建系统。
cmake_minimum_required(VERSION 3.4.1)
add_library( # Sets the name of the library.
my_ndk_lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/native-lib.cpp )
find_library( # Sets the name of the path variable.
log-lib
# Specifies the NDK library that you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
my_ndk_lib
# Links the target library to the log library
# included in the NDK.
${log-lib} )
步骤 4: 编写原生代码
在android/app/src/main/cpp/
目录下创建一个native-lib.cpp
文件,并编写你的C++代码。
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_my_ndk_plugin_MainActivity_stringFromJNI(
JNIEnv* env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
注意:这里的包名和类名com_example_my_ndk_plugin_MainActivity
应该与你的实际Java/Kotlin类相匹配。如果你没有对应的Activity,可以创建一个或者调整这里的命名以匹配你的实际结构。
步骤 5: 在Java/Kotlin中加载库
在android/app/src/main/java/com/example/my_ndk_plugin/MainActivity.java
(或对应的Kotlin文件)中加载本地库并声明native方法。
package com.example.my_ndk_plugin;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
// Load the native library.
static {
System.loadLibrary("my_ndk_lib");
}
// Declare a native method that is implemented in native code.
public native String stringFromJNI();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Use the native method.
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}
}
步骤 6: 在Flutter端调用原生代码
为了从Flutter端调用这个原生方法,我们需要通过MethodChannel与原生代码通信。修改插件的Dart代码来实现这一点。
在lib/my_ndk_plugin.dart
中:
import 'package:flutter/services.dart';
class MyNdkPlugin {
static const MethodChannel _channel = const MethodChannel('my_ndk_plugin');
static Future<String?> get platformVersion async {
final String? version = await _channel.invokeMethod('getPlatformVersion');
return version;
}
// Add a method to call the native function
static Future<String?> getStringFromJNI() async {
final String? result = await _channel.invokeMethod('getStringFromJNI');
return result;
}
}
在android/src/main/kotlin/com/example/my_ndk_plugin/MyNdkPlugin.kt
(或Java对应文件)中添加MethodChannel的处理:
package com.example.my_ndk_plugin
import android.content.Context
import androidx.annotation.NonNull
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
/** MyNdkPlugin */
class MyNdkPlugin: FlutterPlugin, MethodCallHandler, ActivityAware {
/// The MethodChannel that will the communication between Flutter and native Android
///
/// This local reference serves to register the plugin with the Flutter Engine and unregister it
/// when the Flutter Engine is detached from the Activity
private lateinit var channel : MethodChannel
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPluginBinding) {
channel = MethodChannel(flutterPluginBinding.binaryMessenger, "my_ndk_plugin")
channel.setMethodCallHandler(this)
}
override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
if (call.method == "getPlatformVersion") {
result.success("Android ${android.os.Build.VERSION.RELEASE}")
} else if (call.method == "getStringFromJNI") {
// Call the native method and return the result
result.success(stringFromJNI())
} else {
result.notImplemented()
}
}
override fun onDetachedFromEngine(@NonNull binding: FlutterPluginBinding) {
channel.setMethodCallHandler(null)
}
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
// No-op
}
override fun onDetachedFromActivityForConfigChanges() {
// No-op
}
override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
// No-op
}
override fun onDetachedFromActivity() {
// No-op
}
// Native method stub (this should ideally call the actual native method)
private external fun stringFromJNI(): String
companion object {
// For singleton instance
@JvmStatic
fun registerWith(registrar: Registrar) {
val channel = MethodChannel(registrar.messenger(), "my_ndk_plugin")
channel.setMethodCallHandler(MyNdkPlugin())
}
}
}
注意:上面的Kotlin代码示例中的stringFromJNI
方法是一个占位符,因为直接从Kotlin调用C++代码需要更复杂的设置(通常通过JNI桥接)。为了简化示例,这里假设你已经有一个机制来调用C++代码并返回结果。在实际应用中,你可能需要在Activity或其他适当的位置实现JNI调用,并通过某种机制(如事件总线、单例等)将结果传递给Flutter插件。
由于直接从Kotlin调用C++代码超出了简单示例的范围,这里提供了一个概念性的框架。实际实现将依赖于你的具体需求和项目结构。
总结
以上步骤展示了如何在Flutter项目中集成NDK插件的基本流程。这包括创建插件、配置CMake、编写C++代码、在Java/Kotlin中加载库、以及在Flutter端通过MethodChannel与原生代码通信。根据项目的具体需求,你可能需要调整这些步骤以适应你的环境。