Flutter 与第三方 Native-SDK 的交互代理方案

news2025/1/8 20:20:27

场景

在使用 Flutter 进行功能模块或者整体项目的开发时,如果需要(阶段性)频繁地和某个第三方 Native-SDK 进行交互,而该 Native-SDK 没有实现 Flutter 插件版本的情况下,如果直接把这部分交互 API 加入到原有的 channel 类里面,会使得该 channel 类变得臃肿,造成代码维护及迭代上的难度;而且,对于一些平常比较少用到的功能,也不需要一上来就初始化 channel-api,占用空间,而是在使用时才初始化,使用后也可以手动清除;

在此提出一种“Flutter 与第三方 Native-SDK 的交互代理方案”,通过一种“代理包装”的方式,尝试较好地解决以上提到的问题;

效果

该方案使用的是 Flutter+Android 进行 demo,全屏 loading、底部灰底 toast 为 Android 端实现,方块 loading、中间黑底 toast 为 Flutter 端实现

在这里插入图片描述

范例

需要使用时才开启 channel,使用结束后关闭 channel 监听

class _TestPageState extends State<TestPage> {
  
  void initState() {
    super.initState();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Channel')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            GestureDetector(
              onTap: () => channelTest(),
              child: const Text('开启通道,进行交互'),
            ),
          ],
        ),
      ),
    );
  }

  Future<void> channelTest() async {
    TestChannelProxy? channel = await TestChannelProxy.openChannel();
    if (channel != null) {
      Function()? cancel;
      channel.doSomethingAndListener(DoSomethingCallback(
        onTestFirst: (result) {
          cancel = MsgUtil.loading(msg: result);
        },
        onTestSecond: () {
          cancel?.call();
        },
        onTestThird: (result) async {
          MsgUtil.toast(result.toString());
          return !result;
        },
      ));
    }
  }
}

代码(Flutter 端代理类)

维护与 Native 端的交互,使用者不需要知道具体交互逻辑,但是在该代理类中,需要保证每次有回调的交互都能正确回调(不管是状态为成功的回调,还是状态为失败的回调)

import 'package:flutter/services.dart';

import 'platform_method_channel.dart';

const String _testChannelName = 'zzq.channel.TestChannelProxy';

class TestChannelProxy {
  late MethodChannel _channel;
  DoSomethingCallback? _callback;

  TestChannelProxy._() {
    _channel = const MethodChannel(_testChannelName);
    _channel.setMethodCallHandler(_handleMethod);
  }

  /// [return] 为空表示开启失败,不为空表示开启成功
  static Future<TestChannelProxy?> openChannel() async {
    bool success = await PlatformMethodChannel.getInstance()
        .openPlatformChannel(_testChannelName);
    return success ? TestChannelProxy._() : null;
  }
  
  /*Future<void> closeChannel() async {
    await _channel.invokeMethod('closeChannel');
    _channel.setMethodCallHandler(null);
  }*/

  // ------------------------------ flutter 调用 native ------------------------------

  void doSomethingAndListener(DoSomethingCallback callback) {
    _callback = callback;
    _channel.invokeMethod('doSomethingAndListener');
  }

  // ------------------------------ native 调用 flutter ------------------------------

  Future _handleMethod(MethodCall call) async {
    var args = call.arguments;
    switch (call.method) {
      case 'getString':
        return _getString();
      case 'getBoolean':
        return _getBoolean();
      case 'onTestFirst':
        _onTestFirst(args);
        break;
      case 'onTestSecond':
        _onTestSecond();
        break;
      case 'onTestThird':
        return _onTestThird(args);
      default:
        break;
    }
  }

  Future<String> _getString() async {
    await Future.delayed(const Duration(seconds: 2));
    return 'String';
  }

  Future<bool> _getBoolean() async {
    await Future.delayed(const Duration(seconds: 2));
    return true;
  }

  void _onTestFirst(String result) {
    _callback?.onTestFirst?.call(result);
  }

  void _onTestSecond() {
    _callback?.onTestSecond?.call();
  }

  Future<bool> _onTestThird(bool result) async {
    return (await _callback?.onTestThird?.call(result)) ?? false;
  }
}

class DoSomethingCallback {
  final Function(String result)? onTestFirst;
  final Function()? onTestSecond;
  final Future<bool> Function(bool result)? onTestThird;

  DoSomethingCallback({
    this.onTestFirst,
    this.onTestSecond,
    this.onTestThird,
  });
}

需要在默认/全局的 channel 中定义通用的开启 channel 的 api,提供代理交互 channel 的开启能力

  /// 开启 flutter 与 native 端的交互通道,用于使用第三方 native-sdk 的能力
  /// 可以是 BasicMessageChannel、MethodChannel、EventChannel
  /// [return] 通道是否开启成功
  Future<bool> openPlatformChannel(String channelName) async {
    var result = await _channel.invokeMethod('openPlatformChannel', channelName);
    return result == true;
  }
    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        val args = call.arguments
        when (call.method) {
            "openPlatformChannel" -> openPlatformChannel(args as String, result)
            else -> result.notImplemented()
        }
    }

    /**
     * 开启 flutter 与 native 端的交互通道,用于 flutter 端使用第三方 native-sdk 的能力
     * 可以是 BasicMessageChannel、MethodChannel、EventChannel
     * [result.success] 通道是否开启成功
     */
    private fun openPlatformChannel(channelName: String, result: MethodChannel.Result) {
        var success = true
        when (channelName) {
            TEST_CHANNEL_NAME -> TestTool.openChannel(binaryMessenger)
            else -> success = false
        }
        result.success(success)
    }

代码(Android 端第三方 Native-SDK 包装类)

该类用于包装第三方 Native-SDK 所提供的能力,同时持有、维护 Flutter-Android-channel 中在 Android 端的代理 channel 实例;即该类负责 SDK 能力使用及拓展,而 channel 代理类只负责通讯(发起及监听等)

import io.flutter.plugin.common.BinaryMessenger
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

object TestTool {

    private var testChannelProxy: TestChannelProxy? = null

    fun openChannel(messenger: BinaryMessenger) {
        if (testChannelProxy == null) {
            synchronized(TestTool::class.java) {
                if (testChannelProxy == null) {
                    testChannelProxy = TestChannelProxy(messenger)
                }
            }
        }
    }
    
    /*fun closeChannel() {
        testChannelProxy = null
    }*/

    fun doSomethingAndListener() {
        DialogManager.showLoading()
        testChannelProxy?.getBoolean { it ->
            DialogManager.hideLoading()
            if (it) {
                CoroutineScope(Dispatchers.Main).launch {
                    delay(2000)
                    testChannelProxy?.onTestFirst("点了")
                    delay(2000)
                    testChannelProxy?.onTestSecond()
                    delay(2000)
                    testChannelProxy?.onTestThird(true) {
                        DialogManager.toast(it.toString())
                    }
                }
            }
        }
    }

}

代码(Android 端代理类)

维护与 Flutter 端的交互,使用者不需要知道具体交互逻辑,但是在该代理类中,需要保证每次有回调的交互都能正确回调(不管是状态为成功的回调,还是状态为失败的回调)

import android.os.Handler
import android.os.Looper
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel

const val TEST_CHANNEL_NAME = "zzq.channel.TestChannelProxy"

class TestChannelProxy(messenger: BinaryMessenger) : MethodChannel.MethodCallHandler {
    private val channel: MethodChannel

    init {
        channel = MethodChannel(messenger, TEST_CHANNEL_NAME)
        channel.setMethodCallHandler(this)
    }

    // ------------------------------ flutter 调用 native ------------------------------

    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
        val args = call.arguments
        when (call.method) {
            "doSomethingAndListener" -> TestTool.doSomethingAndListener()
            else -> result.notImplemented()
        }
    }
    
    /*fun closeChannel() {
        channel.setMethodCallHandler(null)
        TestTool.closeChannel()
    }*/

    // ------------------------------ native 调用 flutter ------------------------------

    /**
     * Methods marked with @UiThread must be executed on the main thread. Current thread: AsyncTask
     */
    private fun mainInvokeMethod(method: String, arguments: Any?, callback: MethodChannel.Result?) {
        if (Looper.myLooper() == Looper.getMainLooper()) {
            channel.invokeMethod(method, arguments, callback)
        } else {
            Handler(Looper.getMainLooper()).post {
                channel.invokeMethod(method, arguments, callback)
            }
        }
    }

    fun getString(callback: (string: String?) -> Unit) {
        mainInvokeMethod(
            "getString",
            null,
            object : MethodChannel.Result {
                override fun success(result: Any?) {
                    callback(if (result is String) result else null)
                }

                override fun error(code: String, msg: String?, details: Any?) {
                    callback(null)
                }

                override fun notImplemented() {
                    callback(null)
                }
            })
    }

    fun getBoolean(callback: (boolean: Boolean) -> Unit) {
        mainInvokeMethod(
            "getBoolean",
            null,
            object : MethodChannel.Result {
                override fun success(result: Any?) {
                    callback(result == true)
                }

                override fun error(code: String, msg: String?, details: Any?) {
                    callback(false)
                }

                override fun notImplemented() {
                    callback(false)
                }
            })
    }

    fun onTestFirst(result: String) {
        mainInvokeMethod("onTestFirst", result, null)
    }

    fun onTestSecond() {
        mainInvokeMethod("onTestSecond", null, null)
    }

    fun onTestThird(result: Boolean, callback: (success: Boolean) -> Unit) {
        mainInvokeMethod(
            "onTestThird",
            result,
            object : MethodChannel.Result {
                override fun success(result: Any?) {
                    callback(result == true)
                }

                override fun error(code: String, msg: String?, details: Any?) {
                    callback(false)
                }

                override fun notImplemented() {
                    callback(false)
                }
            })
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/484551.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最优化方法Python计算:一元函数搜索算法——二分法

设一元目标函数 f ( x ) f(x) f(x)在区间 [ a 0 , b 0 ] ⊆ R [a_0,b_0]\subseteq\text{R} [a0​,b0​]⊆R&#xff08;其长度记为 λ \lambda λ&#xff09;上为单峰函数&#xff0c;且在 ( a 0 , b 0 ) (a_0,b_0) (a0​,b0​)内连续可导&#xff0c;即其导函数 f ′ ( x ) f…

TinyJAMBU的制动原理——一种轻量化的认证密码

关于TinyJAMBU的定义和介绍在另一篇博文已经介绍过了&#xff0c;这里只对其动作原理进行描述和说明。 对应的博文链接如下&#xff1a;TinyJAMBU&#xff1a;一种轻量化密码介绍 首先&#xff0c;该密码是一个流密码体系的块密码框架。其加密模式整体上来看是块密码&#xff0…

P4903 心碎

这是一道洛谷里的题目 难度级别&#xff1a;普及/提高- 题目提供者&#xff1a;大神cyd 题目背景 NOIP2015初赛。CYD大神在他的母校跪掉了。初赛那天&#xff0c;他回到原来的教室参观了一下&#xff0c;发现那张遍布了N个洞的课桌还摆在那里——那是他初中时和XHY同学坐过…

优思学院|8D和DMAIC两种方法应如何选择?

在现代的商业环境中&#xff0c;客户投诉是一个非常常见的问题。当客户不满意产品或服务时&#xff0c;他们往往会向企业发出投诉。质量管理部门是一个负责处理这些投诉的重要部门&#xff0c;因为它们需要确保产品和服务的质量满足客户的期望。改善方法是质量管理部门用来解决…

作为一名程序员,如何写出一手让同事膜拜的漂亮代码?

整洁的代码 有意义的命名 函数命名 变量命名 函数的定义 注释的规范 代码的长度 代码的对齐 我写代码已经有好几年了&#xff0c;最近看了一本书叫做《代码整洁之道》。我发现这本书中介绍的一些内容对我来说非常有启发性。书中提到的一些方法和技巧让我重新审视了自己的…

疑难问题定位案例复盘(三)

今天我们分享一个数据库被异常改写的案例&#xff0c;通过该案例我们可以学习总结出常规的文件被改写问题定位思路。 问题现象 1、测试环境在进行特定压力测试时发现页面登陆异常&#xff0c;且调试日志多个进程持续打印“数据库打开失败”日志。 2、测试环境在进行多个压力测…

Unity教程||Unity添加中文字体||Unity知识记录--制作UI粒子特效

Unity添加中文字体 ## 1、拷贝字体文件 拷贝C:\Windows\Fonts文件夹下&#xff0c;华文细黑常规文件到项目中 ## 2、下载中文字库 链接: https://pan.baidu.com/s/1KW31KB5vEImZHUWvQ9PLEQ 提取码: bgug 3、添加字体字库 选择Window->TextMeshPro->Font Asset Crea…

HTML的基础语法

文章目录 前言一.HTML结构1.1 什么是html语言1.2 html的结构是什么 二.HTML常见标签2.1 文本格式标签标题标签段落标签换行标签格式化标签图片标签超链接标签 2.2 表格标签2.3 表单标签from标签input标签文本框密码框单选框复选框普通按钮提交按钮清空按钮选择文件select标签te…

延迟队列与SpringBoot实战

延迟队列与SpringBoot实战 概念 延时队列,队列内部是有序的&#xff0c;最重要的特性就体现在它的延时属性上&#xff0c;延时队列中的元素是希望在指定时间到了以后或之前取出和处理&#xff0c;简单来说&#xff0c;延时队列就是用来存放需要在指定时间被处理的元素的队列 …

基于jQuery------购物车案例

目录 基于jQuery------购物车案例 案例&#xff1a;购物车案例模块-增减商品数量分析 案例&#xff1a;购物车案例模块-修改商品小计分析 案例&#xff1a;购物车案例模块-计算总计和总额 案例&#xff1a;购物车案例模块-删除商品模块 案例&#xff1a;购物车案例模块-选…

从‘discover.partitions‘=‘true‘分析Hive的TBLPROPERTIES

从’discover.partitions’true’分析Hive的TBLPROPERTIES 前言 Hive3.1.2先建表&#xff1a; show databases ;use db_lzy;show tables ;create external table if not exists test_external_20230502(id int,comment1 string,comment2 string ) stored as parquet ;creat…

C语言通过控制台命令行传入参数

Linux 与 windows运行c语言程序 切换到对应目录下 1. gcc hello.c -o hello 2.Linux: ./hello Windows: hello.exe int main(){}默认无参数 但在一些情况下想要直接通过在上述过程中第二步就传入参数而不是使用scanf..之类的输入语句就需要使用有参数的main方法: int main() {…

Docker--harbor私有库部署与管理

目录 一、本地私有仓库 搭建本地私有仓库 Docker容器的重启策略 二、Harbor 1、什么是Harbor 2、Harbor特性 3、Harbor的构成 三、Harbor部署 实验步骤 1、安装Docker-Compose服务 2、部署Harbor服务 1、下载或上传Harbor安装程序 2、修改Harbor安装的配置文件 3、…

基于TI板MSP430 玩转PID

文章目录 前言一、整体框架二、PID算法1. 位置式PID2. 增量式PID3. 比例外置式PID4. 积分限幅、输出限幅和PID参数整定5. 位置式PID和增量式PID的区别及抉择 三、初值获取1. 定时器输入捕获2. 外部中断3. ADC采样 前言 具体啥是PID&#xff0c;我这里不做介绍&#xff0c;网上…

SpringMVC(后)SSM整合

10、文件上传和下载 10.1、文件下载 ResponseEntity用于控制器方法的返回值类型&#xff0c;该控制器方法的返回值就是响应到浏览器的响应报文 使用ResponseEntity实现下载文件的功能 RequestMapping("/testDown") public ResponseEntity<byte[]> testResp…

【Hello Algorithm】复杂度 二分法

作者&#xff1a;小萌新 专栏&#xff1a;算法 作者简介&#xff1a;大二学生 希望能和大家一起进步 本篇博客简介&#xff1a;介绍算法的复杂度 对数器和二分法 复杂度 对数器 二分法 复杂度常数时间操作非常数时间操作时间复杂度空间复杂度 二分法有序数组中找一个值寻找有序…

树的存储和遍历

文章目录 6.5 树与森林6.5.1 树的存储结构1. 双亲表示法(顺序存储结构)2 孩子链表表示法3 孩子兄弟表示法(二叉树表示法) 6.5.2 森林与二叉树的转换1 树转换成二叉树2 二叉树转换成树3 森林转换成二叉树4 二叉树转换成森林 6.5.3 树和森林的遍历1. 树的遍历2. 森林的遍历 6.6 赫…

数据库篇:表设计、创建编辑以及导出导入数据

微信小程序云开发实战-答题积分赛小程序系列 数据库篇:表设计、添加编辑以及导出导入数据 原型: 最终实现界面截图:

Moqui REST API的两种实现方法

实现Restful API的方法 实现REST API有两种方法。第一种&#xff1a; The main tool for building a REST API based on internal services and entity operations is to define resource paths in a Service REST API XML file such as the moqui.rest.xml file in moqui-fr…

chatGPT国内可用镜像源地址

chatGPT国内可用镜像源地址 彷丶徨丶 关注 IP属地: 湖北 0.811 2023.03.15 16:02:16 字数 1,152 阅读 249,582 如果你正在尝试访问Chatgpt网站&#xff0c;但由于某些原因无法访问该网站&#xff0c;那么你可以尝试使用Chatgpt的国内镜像网站。以下是一些Chatgpt国内镜像网站的…