手记

【备战春招】第17天 Flutter与Native通信

课程名称Flutter从入门到进阶 实战携程网App 一网打尽核心技术
课程章节:Flutter进阶提升:Flutter混合开发
课程讲师CrazyCodeBoy

课程内容

1.平台通道及编解码器

Flutter与Native之间的消息是通过平台通道Channel 实现的,为了确保用户界面能够正确响应,消息都是以异步的方式进行传递。无论是Native向Flutter发送消息,还是Flutter向Native发送消息。

平台通道可以使用提供的编解码器对消息进行编解码,这些编解码器支持简单类似JSON的高效二进制序列化,例如布尔值,数字,字符串,字节缓冲区以及这些的列表和映射。当发送和接收值时,会自动对这些值进行序列化和反序列化。

Android端提供了4 种编解码器。

1.BinaryCodec

BinaryCodec是最简单的一种编解码器,其返回值类型与入参的类型相同,均为二进制格式(ByteBuffer)。由于BinaryCodec在编解码过程中什么都没做,只是原封不动的将二进制数据返回。所以传递的数据在编解码时会免于拷贝,这种方式在传递的数据量比较大时很有用。比如从Android侧传入一张图片到Flutter侧显示。

ByteBuffer是Nio中的一个类,是一块存储字节的区域。它有两个实现类:DirectByteBuffer与HeapByteBuffer,DirectByteBuffer是直接在内存中开辟了一块区域来存储数据,而HeapByteBuffer是在JVM堆中开辟一块区域来存储数据,所以要想数据在DirectByteBuffer中与HeapByteBuffer互通,就需要进行一次拷贝。

2.StandardMessageCodec

StandardMessageCodec是BasicMessageChannel的默认编解码器,支持基础数据类型、列表及字典等。在编码时会先将数据写入到ByteArrayOutputStream流中,然后再将该流中的数据写入到ByteBuffer中。在解码时,直接从ByteBuffer中读取数据。

3.StandardMethodCodec

StandardMethodCodec是基于StandardMessageCodec的封装。是MethodChannel与EventChannel的默认编解码器。

4.StringCodec

StringCodec是用于字符串与二进制数据之间的编解码,其编码格式为UTF-8。在编码时会将String转成byte数组,然后再将该数组写入到ByteBuffer中。在解码时,直接从ByteBuffer中读取数据。

此外还有JSONMessageCodec,其内部调用StringCodec来实现编解码。JSONMethodCodec是基于JSONMessageCodec的封装,可以在MethodChannel与EventChannel中使用。

2.Flutter与Native的通信方式

Android与Flutter之间的通信共有四种实现方式:

  • 在初始化flutter页面时会传递一个字符串,route,因此可以用route来传递数据。该种方式仅支持单向数据传递且数据类型只能为字符串,无返回值。
  • 通过EventChannel来实现,EventChannel仅支持数据单向传递,无返回值。
  • 通过MethodChannel来实现,MethodChannel支持数据双向传递,有返回值。
  • 通过BasicMessageChannel来实现,BasicMessageChannel支持数据双向传递,有返回值。

2.1 初始化时传值

通过该种方式就可以在初始化flutter时,Android给flutter传递数据。由于runApp仅会调用一次,所以该种方式只能传递一次数据且数据只能是字符串。

在 Android 端实现:

//第三个参数换成要传递的数据
FlutterView flutterView = Flutter.createView(this, getLifecycle(), "route");

在Flutter 端实现:

void main() => runApp(MyApp(
      initParams: window.defaultRouteName,
    ));

class MyApp extends StatelessWidget {
  final String initParams;  //既是前面传递的值:route
  MyApp({Key key, @required this.initParams}) : super(key: key);
  @override
  Widget build(BuildContext context) {...}
}

2.2 EventChannel

EventChannel是一种native向flutter发送数据的单向通信方式,flutter无法返回任何数据给native。主要用于native向flutter发送手机电量变化、网络连接变化、陀螺仪、传感器等。

Android端实现:

public class EventChannelPlugin implements EventChannel.StreamHandler {

    private static final String TAG = EventChannelPlugin.class.getSimpleName();
    private EventChannel.EventSink eventSink;
    private Activity activity;

    static EventChannelPlugin registerWith(FlutterView flutterView) {
        EventChannelPlugin plugin = new EventChannelPlugin(flutterView);
        new EventChannel(flutterView, "EventChannelPlugin").setStreamHandler(plugin);
        return plugin;

    }

    private EventChannelPlugin(FlutterView flutterView) {
        this.activity = (Activity) flutterView.getContext();
    }

    void send(Object params) {
        if (eventSink != null) {
            eventSink.success(params);
        }
    }

    void sendError(String str1, String str2, Object params) {
        if (eventSink != null) {
            eventSink.error(str1, str2, params);
        }
    }

    void cancel() {
        if (eventSink != null) {
            eventSink.endOfStream();
        }
    }
    //第一个参数为flutter初始化EventChannel时返回的值,仅此一次
    @Override
    public void onListen(Object o, EventChannel.EventSink eventSink) {
        this.eventSink = eventSink;
        Log.i(TAG, "eventSink:" + eventSink);
        Log.i(TAG, "Object:" + o.toString());
        Toast.makeText(activity, "onListen——obj:" + o, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onCancel(Object o) {
        Log.i(TAG, "onCancel:" + o.toString());
        Toast.makeText(activity, "onCancel——obj:" + o, Toast.LENGTH_SHORT).show();
        this.eventSink = null;
    }
}

Flutter端实现:

class _MyHomePageState extends State<MyHomePage> {
  EventChannel _eventChannelPlugin = EventChannel("EventChannelPlugin");
  StreamSubscription _streamSubscription;
  @override
  void initState() {
    _streamSubscription = _eventChannelPlugin
         //["abc", 123, "你好"]对应着Android端onListen方法的第一个参数,可不传值
        .receiveBroadcastStream(["abc", 123, "你好"])
        .listen(_onToDart, onError: _onToDartError, onDone: _onDone);
    super.initState();
  }

  @override
  void dispose() {
    if (_streamSubscription != null) {
      _streamSubscription.cancel();
      _streamSubscription = null;
    }
    super.dispose();
  }
  //native端发送正常数据
  void _onToDart(message) {
    print(message);
  }
  //当native出错时,发送的数据
  void _onToDartError(error) {
    print(error);
  }
  //当native发送数据完成时调用的方法,每一次发送完成就会调用
  void _onDone() {
    print("消息传递完毕");
  }

  @override
  Widget build(BuildContext context) {...}
}

2.3 MethodChannel

MethodChannel是一种native与flutter之间互相发送数据的通信方式,通过MethodChannel就能调用native与flutter中相对应的方法,该种方式有返回值。

Android端实现:

public class MethodChannelPlugin implements MethodChannel.MethodCallHandler {
    private Activity activity;
    private MethodChannel channel;

    public static MethodChannelPlugin registerWith(FlutterView flutterView) {
        MethodChannel channel = new MethodChannel(flutterView, "MethodChannelPlugin");
        MethodChannelPlugin methodChannelPlugin = new MethodChannelPlugin((Activity) flutterView.getContext(), channel);
        channel.setMethodCallHandler(methodChannelPlugin);
        return methodChannelPlugin;
    }

    private MethodChannelPlugin(Activity activity, MethodChannel channel) {
        this.activity = activity;
        this.channel = channel;

    }
    //调用flutter端方法,无返回值
    public void invokeMethod(String method, Object o) {
        channel.invokeMethod(method, o);
    }
    //调用flutter端方法,有返回值
    public void invokeMethod(String method, Object o, MethodChannel.Result result) {
        channel.invokeMethod(method, o, result);
    }

    @Override
    public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
        switch (methodCall.method) {
            case "send"://返回的方法名
                //给flutter端的返回值
                result.success("MethodChannelPlugin收到:" + methodCall.arguments);
                Toast.makeText(activity, methodCall.arguments + "", Toast.LENGTH_SHORT).show();
                if (activity instanceof FlutterAppActivity) {
                    ((FlutterAppActivity) activity).showContent(methodCall.arguments);
                }
                break;
            default:
                result.notImplemented();
                break;
        }
    }
}

Flutter 端实现:

class _MyHomePageState extends State<MyHomePage> {
  MethodChannel _methodChannel = MethodChannel("MethodChannelPlugin");
  @override
  void initState() {
    _methodChannel.setMethodCallHandler((handler) => Future<String>(() {
          print("_methodChannel:${handler}");
          //监听native发送的方法名及参数
          switch (handler.method) {
            case "send":
              _send(handler.arguments);//handler.arguments表示native传递的方法参数
              break;
          }
        }));
    super.initState();
  }
  //native调用的flutter方法
  void _send(arg) {
    setState(() {
      _content = arg;
    });
  }
  String _resultContent = "";
  
  //flutter调用native的相应方法
  void _sendToNative() {
      Future<String> future =
          _methodChannel.invokeMethod("send", _controller.text);
      future.then((message) {
        setState(() {
           //message是native返回的数据
          _resultContent = "返回值:" + message;
        });
      });
  }

  @override
  Widget build(BuildContext context) {...}
}

3.4 BasicMessageChannel

BasicMessageChannel是一种能够在native与flutter之间互相发送消息的通信方式,它支持数据类型最多,使用范围最广。EventChannel与MethodChannel的应用场景可以使用BasicMessageChannel来实现,但BasicMessageChannel的应用场景就不一定能够使用EventChannel与MethodChannel来实现。

Android 端实现:

//这里支持的数据类型为String。
public class BasicMessageChannelPlugin implements BasicMessageChannel.MessageHandler<String> {
    private Activity activity;
    private 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<String>(flutterView, "BasicMessageChannelPlugin", StringCodec.INSTANCE);
        messageChannel.setMessageHandler(this);
    }

    @Override
    public void onMessage(String s, BasicMessageChannel.Reply<String> reply) {
        reply.reply("BasicMessageChannelPlugin收到:" + s);
        if (activity instanceof FlutterAppActivity) {
            ((FlutterAppActivity) activity).showContent(s);
        }
    }

    void send(String str, BasicMessageChannel.Reply<String> reply) {
        messageChannel.send(str, reply);
    }
}

Flutter端实现:

class _MyHomePageState extends State<MyHomePage> {
  //StringCodec()为编码格式
  BasicMessageChannel<String> _basicMessageChannel =
      BasicMessageChannel("BasicMessageChannelPlugin", StringCodec());

  @override
  void initState() {
    _basicMessageChannel.setMessageHandler((message) => Future<String>(() {
          print(message);
          //message为native传递的数据
          setState(() {
            _content = message;
          });
          //给Android端的返回值
          return "收到Native消息:" + message;
        }));
    _controller = TextEditingController();
    super.initState();
  }

  //向native发送消息
  void _sendToNative() {
      Future<String> future = _basicMessageChannel.send(_controller.text);
      future.then((message) {
        _resultContent = "返回值:" + message;
      });
  }

  @override
  Widget build(BuildContext context) {...}
}

课程总结

这一节介绍了在Android与Flutter混合开发模式下,Android 与 Flutter 之间相互通信的方式,需要注意的是为了保证UI的响应,通过Channels传递的消息都是异步的。

0人推荐
随时随地看视频
慕课网APP