Flutter&Native通信-1


11 Flutter 与 Native通信原理/实战。

[Flutter与Android通信开发指南] (http://events.jianshu.io/p/575440e754d2)
[原始作者] (https://www.imooc.com/article/309626)

在做Flutter开发的时候,离不开Flutter之间的通信,比如:初始化Flutter时Native向Dart传递数据,Dart调用Native的相册选择图片,Dart调用Native的模块进行一些复杂的计算,native将一些数据(GPS信息、陀螺仪、传感器等)主动传递给Dart等。

几种通信场景:

  • 初始化Flutter时,Native向Dart传递数据;
  • Native发送数据给Dart;
  • Dart发送数据给Native;
  • Dart发送数据给Native,然后Native回传数据给Dart。

Flutter 与 Native通信机制

通信机制:通过Channel(平台通道)来完成。消息使用Channel在Flutter与Native之间传递。

Channel 所支持数据类型对照表。

Dart ~ Android ~ iOS

null ~ null ~ nil(NSNull when nested)
bool ~ java.lang.Boolean ~ NSNumber numberWithBool:
int ~ java.lang.Integer ~ NSNumber numberWithInt:
int,超32bits ~ java.lang.Long ~ NSNumber numberWithLong:
double ~ java.lang.Double ~ NSNumber numberWithDouble:
String ~ java.lang.String ~ NSString
Unit8List ~ byte[] ~ FlutterStandardTypedData typedDataWithBytes:
Int32List ~ int[] ~ FlutterStandardTypedData typedDataWithInt32:
Int64List ~ long[] ~ FlutterStandardTypedData typedDataWithInt64:
Float64List~ double[] ~ FlutterStandardTypedData typedDataWithFloat64:
List ~ java.util.ArrayList ~ NSArray
Map ~ java.util.HashMap ~ NSDictionary

Flutter 定义了三种不同类型的Channel

  • BasicMessageChannel:用于传递字符串和半结构化的信息,持续通信,收到消息后可以回复此消息,如:Native将遍历到的文件信息 陆续 传递给Dart,再比如:Flutter将从服务器端 陆续 获取到的信息交给native加工,native处理完后返回等。
  • MethodChannel:用于传递方法调用(method invocation)一次性通信:如Flutter调用native代码,如:拍照,信息选择等。
  • EventChannel:用于数据流(event Stream)的通信,持续通信,收到消息后无法回复此消息,通常用于native向Dart的通信(eg:手机电量变化,网络连接变化,陀螺仪,传感器等。)

以上三种类型的Channel都是 全双工通信,即A<=>B, Dart可以主动发送消息给platform端,并且platform接收到消息后可以做出回应;
同样,platform端可以主动发送消息给Dart端,Dart端接收后返回给platform端。


BasicMessageChannel用法:

Native Android端:

1.构造方法原型

BasicMessageChannel(BinaryMessage messenger, String name, MessageCodec<T> codec)

  • BinaryMessage messenger: 消息信使,是消息的发送和接收的工具,
  • String name: Channel的名字,也是唯一标识符。
  • MessageCodec<T> codec: 消息的编解码器,他有集中不同类型的实现:
    • BinaryCodec: 最为简单的一种Codec,因为其返回值类型和入参的类型相同,均为二进制格式(Android中为ByteBuffer,iOS中为NSData)。实际上,BinaryCodec在编解码过程中什么都没做,只是原封不动将二进制数据消息返回而已。或许你会因此觉得BinaryCodec没有意义,但是在某些情况下它非常有用,比如使用BinaryCodec可以使传递内存数据块时在编解码阶段免于内存拷贝;效率高。
    • StringCodec: 用于字符串与二进制数据之间的编解码,其编码格式为UTF-8;
    • JSONMessageCodec: 用于基础数据与二进制数据之间的编解码,其支持基础数据类型以及列表、字典。其在iOS端使用了NSJSONSerialization作为序列化的工具,而在Android端则使用了其自定义的JSONUtil与StringCodec作为序列化工具;
    • StandardMethodCodec: 是BasicMessageChannel的默认编解码器,其支持基础数据类型、二进制数据、列表、字典,其工作原理;

2.setMessageHandler方法原型

void setMessageHandler(BasicMessageChannel.MessageHandler<T> handler)

  • BasicMessageChannel.MessageHandler<T> handler - 消息处理器,配合BinaryMessenger完成消息的处理;
    在创建好BasicMessageChannel后,如果要让其接收Dart发来的消息,则需要调用它的setMessageHandler方法为其设置一个消息处理器。

3.BasicMessageChannel.MessageHandler原型

public interface MessageHandler<T> {
void onMessage(T var1, BasicMessageChannel.Reply<T> var2);
}

  • onMessage(T var1, BasicMessageChannel.Reply<T> var2) - 用于接受消息,var1是消息内容,var2是回复此消息的回调函数;

4. send方法原型(向Dart发送消息)

void send(T message)
void send(T message, BasicMessageChannel.Reply<T> callback)

  • T message - 要传递给Dart的具体信息;
  • BasicMessageChannel.Reply<T> callback - 消息发出去后,收到Dart的回复的回调函数;

在创建好BasicMessageChannel后,如果要向Dart发送消息,可以调用它的send方法向Dart传递数据。

public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String>, BasicMessageChannel.Reply<String> {
    private final Activity activity;
    private final BasicMessageChannel<String> messageChannel;

    static BasicMessageChannelPlugin registerWith(FlutterView flutterView) {
        return new BasicMessageChannelPlugin(flutterView);
    }
    private BasicMessageChannelPlugin(FlutterView flutterView) {
        this.activity = (Activity) flutterView.getContext();
        this.messageChannel = new BasicMessageChannel<>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
        // 设置消息处理器,处理来自Dart的消息
        messageChannel.setMessageHandler(this);
    }

    @Override
    public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {//处理Dart发来的消息
        reply.reply("BasicMessageChannel收到:" + s);//可以通过reply进行回复
        if (activity instanceof IShowMessage) {
            ((IShowMessage) activity).onShowMessage(s);
        }
        Toast.makeText(activity, s, Toast.LENGTH_SHORT).show();
    }

    /**
     * 向Dart发送消息,并接受Dart的反馈
     * @param message  要给Dart发送的消息内容
     * @param callback 来自Dart的反馈
     */
    void send(String message, BasicMessageChannel.Reply<String> callback) {
        messageChannel.send(message, callback);
    }
    @Override
    public void reply(String s) {
    }
}

Dart端:

1.构造方法原型

const BasicMessageChannel(this.name, this.codec);
- String name: Channel名字,要和Native端保持一致。
- MessageCodec<T> codec: 消息的编解码器,要和native端保持一致,有4种类型的具体实现。

2.setMessageHandler方法原型

void setMessageHandler(Future<T> handler(T message))    
- Future<T> handler(T message)- 消息处理器,配合BinaryMessager完成消息的处理;

创建好BasicMessageChannel后,如果要让其接收Native发来的消息,则需要调用它的setMessageHandler方法为其设置一个消息处理器。

3.send方法原型

Future<T> send(T message)
- T message: 要传递给native的具体消息;
- Future<T>: 消息发出去后,收到Native回复的回调函数;

在创建好BasicMessageChannel后,如果要向Native发送消息,可以调用它的send方法向Native传递数据。

import 'package:flutter/services.dart';
const BasicMessageChannel _basicMessageChannel = BasicMessageChannel('BasicMessageChannelPlugin', StringCodec());
// 使用BasicMessageChannel接收来自Native的消息,并向Native回复。
// - message是Native发来的消息;
// - Future<String> 是Flutter向Native回复的消息。

/// 被动发送消息
_basicMessageChannel.setMessageHandler((String message) => Future<String>((){
  setState((){
    showMessage = message; // 收到Native发来的消息
  });
  return "接收Native的消息:" + message; // 回复消息给native
}));
// 主动发送消息:使用BasicMessageChannel向Native发送消息,并接收Native的回复。
String response;
try {
  // Flutter主动发送消息,并接收native回复。
  response = await _basicMessageChannel.send(value);
} on PlatformException catch(e) {
  print(e);
}

MethodChannel用法:

Native Android端:

1.构造方法原型

//会构造一个StandardMethodCodec.INSTANCE类型的MethodCodec
MethodChannel(BinaryMessenger messenger, String name)
MethodChannel(BinaryMessenger messenger, String name, MethodCodec codec)

  • BinaryMessenger messenger - 消息信使,是消息的发送与接收的工具;
  • String name - Channel的名字,也是其唯一标识符;
  • MethodCodec codec - 用作MethodChannel的编解码器;

2.setMethodCallHandler方法原型

setMethodCallHandler(@Nullable MethodChannel.MethodCallHandler handler)

  • @Nullable MethodChannel.MethodCallHandler handler - 消息处理器,配合BinaryMessenger完成消息的处理;
    在创建好MethodChannel后,需要调用它的setMessageHandler方法为其设置一个消息处理器,以便能加收来自Dart的消息。

3.MethodChannel.MethodCallHandler原型

public interface MethodCallHandler {
void onMethodCall(MethodCall var1, MethodChannel.Result var2);
}

  • onMethodCall(MethodCall call, MethodChannel.Result result) - 用于接受消息,
    call是消息内容,它有两个成员变量String类型的call.method表示调用的方法名,Object 类型的call.arguments表示调用方法所传递的入参;
    MethodChannel.Result result是回复此消息的回调函数提供了result.success,result.error,result.notImplemented方法调用;
public class MethodChannelPlugin implements MethodCallHandler {
    private final Activity activity;

    /**
     * Plugin registration.
     */
    public static void registerWith(FlutterView flutterView) {
        MethodChannel channel = new MethodChannel(flutterView, "MethodChannelPlugin");
        MethodChannelPlugin instance = new MethodChannelPlugin((Activity) flutterView.getContext());
        channel.setMethodCallHandler(instance);
    }
    private MethodChannelPlugin(Activity activity) {
        this.activity = activity;
    }

    @Override
    public void onMethodCall(MethodCall call, Result result) {
        switch (call.method) {//处理来自Dart端的方法调用
            case "showMessage":
                showMessage(call.arguments());
                result.success("MethodChannelPlugin收到:" + call.arguments);//返回结果给Dart
                break;
            default: // 方法未实现
                result.notImplemented();
        }
    }

    /**
     * 展示来自Dart的数据
     * @param arguments
     */
    private void showMessage(String arguments) {
        if (activity instanceof IShowMessage) {
            ((IShowMessage) activity).onShowMessage(arguments);
        }
        Toast.makeText(activity, arguments, Toast.LENGTH_SHORT).show();
    }
}

Dart端:

1.构造方法原型

const MethodChannel(this.name, [this.codec= const StandardMethodCodec()])
- String name: channel的名字,要和Native端保持一致;
- MechodCode codec:消息的编解码器,要和Native端保持一致。

2.invokeMethod方法原型

Future<T> invodeMethod<T>(String method, [dynamic arguments])
- method: 要调用Native的方法名;
- [dynamic arguments]:调用Native方法传递的参数,可不传。

const MethodChannel _methodChannelPlugin = MethodChannel('MethodChannelPlugin');
...
String response;
try {
    // response dart调用native代码返回的结果。调用原生send方法。
  response = await _methodChannelPlugin.invokeMethod('send', value);
} on PlatformException catch (e) {
  print(e);
}

EventChannel用法:

Native Android端:

1.构造方法原型

//会构造一个StandardMethodCodec.INSTANCE类型的MethodCodec
EventChannel(BinaryMessenger messenger, String name)
//或
EventChannel(BinaryMessenger messenger, String name, MethodCodec codec)

  • BinaryMessenger messenger - 消息信使,是消息的发送与接收的工具;
  • String name - Channel的名字,也是其唯一标识符;
  • MethodCodec codec - 用作EventChannel的编解码器;

2.setStreamHandler方法原型

void setStreamHandler(EventChannel.StreamHandler handler)

  • EventChannel.StreamHandler handler - 消息处理器,配合BinaryMessenger完成消息的处理;
    在创建好EventChannel后,如果要让其接收Dart发来的消息,则需要调用它的setStreamHandler方法为其设置一个消息处理器。

3.EventChannel.StreamHandler原型

public interface StreamHandler {
void onListen(Object args, EventChannel.EventSink eventSink);
void onCancel(Object o);
}

  • void onListen(Object args, EventChannel.EventSink eventSink) - Flutter Native监听事件时调用,Object args是传递的参数,EventChannel.EventSink eventSink是Native回调Dart时的会回调函数,eventSink提供success、error与endOfStream三个回调方法分别对应事件的不同状态;
  • void onCancel(Object o) - Flutter取消监听时调用;
public class EventChannelPlugin implements EventChannel.StreamHandler {
    private List<EventChannel.EventSink> eventSinks = new ArrayList<>();

    static EventChannelPlugin registerWith(FlutterView flutterView) {
        EventChannelPlugin plugin = new EventChannelPlugin();
        new EventChannel(flutterView, "EventChannelPlugin").setStreamHandler(plugin);
        return plugin;
    }
    void sendEvent(Object params) {
        for (EventChannel.EventSink eventSink : eventSinks) {
            eventSink.success(params);
        }
    }

    @Override
    public void onListen(Object args, EventChannel.EventSink eventSink) {
        eventSinks.add(eventSink);
    }
    @Override
    public void onCancel(Object o) {
    }
}

Dart端:

1.构造方法原型

const EventChannel(this.name, [this.codec= const StandardMethodCodec()]);
- String name: channel的名字,要和Native端保持一致;
- MechodCode codec:消息的编解码器,要和Native端保持一致。

2.receiveBroadcastStream 方法原型

Stream<dynamic> receiveBroadcastStream([dynamic arguments])
- dynamic arguments: 监听事件时向Native传递的数据。

初始化一个广播用于从Channel中接收数据,它返回一个Stream接下来需要调用Stream的listen方法来完成注册。另外需要在页面销毁时,调用Stream的cancel方法来取消监听。

class EventTest extends StatefulWidget {
  const EventTest({Key? key}) : super(key: key);
  @override
  State<EventTest> createState() => _EventTestState();
}
const EventChannel _eventChannelPlugin = EventChannel('EventChannelPlugin');

class _EventTestState extends State<EventTest> {
  late StreamSubscription? _streamSubscription;
  String showMessage = '';
  
  @override
  void initState() {
    // 注册
    _streamSubscription = _eventChannelPlugin
        .receiveBroadcastStream()
        .listen(_onToDart, onError: _onToDartError);
    super.initState();
  }
  @override
  void dispose() {
    if (_streamSubscription != null) { // 取消注册
      _streamSubscription?.cancel();
      _streamSubscription = null;
    }
    super.dispose();
  }
  void _onToDart(message) {
    setState(() {
      showMessage = message;
    });
  }
  void _onToDartError(error) {
    print(error);
  }
  @override
  Widget build(BuildContext context) {
    return Container();
  }
}

完整通信例子

1. 初始化Flutter时Native向Dart传递数据

在Flutter的API中提供了Native在初始化Dart页面时传递数据给Dart的方式,这种传递数据的方式比下文中所讲的其他几种传递数据的方式发生的时机都早。
因为很少有资料介绍这种方式,所以可能有很多同学还不知道这种方式,不过不要紧,接下来我就向大家介绍如何使用这种方式来传递数据给Dart。
Android向Flutter传递初始化数据initialRoute
Flutter允许我们在初始化Flutter页面时向Flutter传递一个String类型的initialRoute参数,从参数名字它是用作路由名的,但是既然Flutter给我们开了这个口子,那我们是不是可以搞点事情啊,传递点我们想传的其他参数呢,比如:

FragmentTransaction tx = getSupportFragmentManager().beginTransaction();
tx.replace(R.id.someContainer, Flutter.createFragment("{name:'devio',dataList:['aa','bb',''cc]}"));
tx.commit();
//or
View flutterView = Flutter.createView(
      MainActivity.this,
      getLifecycle(),
      "route1"
    );
FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(600, 800);
layout.leftMargin = 100;
layout.topMargin = 200;
addContentView(flutterView, layout);

然后在Flutter module通过如下方式获取:
import 'dart:ui';//要使用window对象必须引入
String initParams=window.defaultRouteName;
//序列化成Dart obj 敢你想干的
...
通过上述方案的讲解是不是给大家分享了一个新的思路呢。
接下来,我们先来看一下如何在Android上来传递这些初始化数据。

实例2. Native到Dart的通信(Native发送数据给Dart)

在Flutter 中Native向Dart传递消息可以通过BasicMessageChannel或EventChannel都可以实现:
通过BasicMessageChannel的方式:
通过EventChannel的方式:
以上就是使用不同Channel实现Native到Dart通信的原理及方式,接下来我们来看一下实现Dart到Native之间通信的原理及方式。

3. Dart到Native的通信(Dart发送数据给Native)

在Flutter 中Dart向Native传递消息可以通过BasicMessageChannel或MethodChannel都可以实现:
通过BasicMessageChannel的方式
通过MethodChannel的方式


https://hanshuliang.blog.csdn.net/article/details/119998492

Dart端:

Android端:

String storageUrl = System.env.FLUTTER_STORAGE_BASE_URL ?: "https://storage.googleapis.com"
repositories {
maven {
// url 'D:/FlutterLearn/flutter_bybird1115/flutter_module/build/host/outputs/repo'
url '../flutter_module/build/host/outputs/repo'
}
maven {
url '$storageUrl/download.flutter.io'
}
}

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容