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

1 回复

更多关于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与原生代码通信。根据项目的具体需求,你可能需要调整这些步骤以适应你的环境。

回到顶部