Flutter鸿蒙插件开发与双端消息通信指南

Viewed 0

本文介绍如何开发一个Flutter鸿蒙插件,实现Flutter与鸿蒙的混合开发,并详细说明双端消息通信的实现方法。

Flutter侧编写MethodChannel

在Flutter侧,首先需要声明一个MethodChannel,并定义相关调用方法。以下是一个示例代码:

const MethodChannel _methodChannel = MethodChannel('xxx.com/app');

/// 获取token
static Future<dynamic> getToken() {
  return _methodChannel.invokeMethod("getPrefs", 'token');
}

/// 设置token
static Future<dynamic> setToken(String token) {
  return _methodChannel
      .invokeMethod("setPrefs", {'key': 'token', 'value': token});
}

这段代码声明了一个MethodChannel,并实现了token存储的调用方法,用于与原生端进行通信。

ArkTs侧实现调用

在鸿蒙ArkTs侧,需要配置Flutter引擎并注册插件。首先,在EntryAbility.ets文件中进行初始化:

import { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';
import { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';
import ForestPlugin from './ForestPlugin';
import { BusinessError } from '@kit.BasicServicesKit';
import { window } from '@kit.ArkUI';
import { preferences } from '@kit.ArkData';

let dataPreferences: preferences.Preferences | null = null;

export default class EntryAbility extends FlutterAbility {
  onWindowStageCreate(windowStage: window.WindowStage): void {
    super.onWindowStageCreate(windowStage);
    preferences.getPreferences(this.context, 'forestStore', (err: BusinessError, val: preferences.Preferences) => {
      if (err) {
        console.error("Failed to get preferences. code =" + err.code + ", message =" + err.message);
        return;
      }
      dataPreferences = val;
      console.info("Succeeded in getting preferences1.");
    })
  }

  configureFlutterEngine(flutterEngine: FlutterEngine) {
    super.configureFlutterEngine(flutterEngine)
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    this.addPlugin(new ForestPlugin());
  }
}

export {dataPreferences};

该文件在原生页面加载时配置Flutter引擎,注册插件,并初始化首选项dataPreferences以备后用。

接下来,在ForestPlugin.ets文件中实现插件类,处理来自Flutter的消息调用:

import { Any, BasicMessageChannel, EventChannel, FlutterManager, FlutterPlugin, Log, MethodCall, MethodChannel, StandardMessageCodec} from '@ohos/flutter_ohos';
import { FlutterPluginBinding } from '@ohos/flutter_ohos/src/main/ets/embedding/engine/plugins/FlutterPlugin';
import { batteryInfo } from '@kit.BasicServicesKit';
import { MethodCallHandler, MethodResult } from '@ohos/flutter_ohos/src/main/ets/plugin/common/MethodChannel';
import { preferences } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import {dataPreferences} from './EntryAbility';
import router from '@ohos.router'
import { webviewRouterParams } from '../pages/Webview';

const TAG = "[flutter][plugin][forest]";

export default class ForestPlugin implements FlutterPlugin {
  private channel?: MethodChannel;
  private basicChannel?: BasicMessageChannel<Any>;
  private api = new ForestApi();
  private dataPreferences: preferences.Preferences | null = null;

  onAttachedToEngine(binding: FlutterPluginBinding): void {
    this.channel = new MethodChannel(binding.getBinaryMessenger(), "xxx.com/app");
    this.channel.setMethodCallHandler({
      onMethodCall : (call: MethodCall, result: MethodResult) => {
        console.log(`${TAG}-->[${call.method}]: ${JSON.stringify(call.args)}`);
        switch (call.method) {
          case "getPrefs":
            this.api.getPrefs(String(call.args), result);
            break;
          case "setPrefs":
            let key = String(call.argument("key"));
            let value = String(call.argument("value"));
            this.api.setPrefs(key, value);
          default:
            result.notImplemented();
            break;
        }
      }
    })
  }

  onDetachedFromEngine(binding: FlutterPluginBinding): void {
    Log.i(TAG, "onDetachedFromEngine");
    this.channel?.setMethodCallHandler(null);
  }

  getUniqueClassName(): string {
    return "ForestPlugin";
  }
}

该插件类实现了FlutterPluginonAttachedToEngine方法,在Flutter引擎加载成功后调用。onMethodCall中接收来自Flutter的消息,处理getPrefssetPrefs回调,其中getPrefs通过result.success(val)异步返回数据,setPrefs则无返回值。

ForestApi类的具体实现如下,使用HarmonyOS的首选项API进行数据读写:

class ForestApi {
  getPrefs(key: string, result: MethodResult) {
    dataPreferences?.get(key, '', (err: BusinessError, val: preferences.ValueType) => {
      if (err) {
        console.error(`${TAG} Failed to get value of ${key}. code =` + err.code + ", message =" + err.message);
        result.success('');
      }
      console.info(`${TAG} Succeeded in getting value of ${key}:${val}.`);
      result.success(val);
    })
  }

  setPrefs(key: string, value: string) {
    dataPreferences?.put(key, value, (err: BusinessError) => {
      if (err) {
        console.error(`${TAG} Failed to put value of ${key}. code =` + err.code + ", message =" + err.message);
        return;
      }
      console.info(`${TAG} Succeeded in putting value of ${key}.`);
    })
  }

  clearPrefs(key: string) {
    dataPreferences?.delete(key, (err: BusinessError) => {
      if (err) {
        console.error("Failed to delete the key 'startup'. code =" + err.code + ", message =" + err.message);
        return;
      }
      console.info(`Succeeded in deleting the key ${key}.`);
    })
  }
}

注意事项

  1. 双端初始化MethodChannel时名称必须保持一致,例如xxx.com/app
  2. ArkTs侧通过result.success(val)返回数据,该过程是异步的,因此在Dart侧需要使用await或回调函数获取值。
  3. 通信中默认只支持基础数据类型,复杂类型需要进行序列化或编解码处理。
  4. 在Dart侧接收的数据为dynamic类型,需要进行适当的数据类型转换。

参考资料

  • 撰写双端平台代码(插件编写实现)
  • 用户首选项API
0 Answers