Flutter 视频系列: 图解 Flutter 与 Andro

作者: 小小小小怪兽_666 | 来源:发表于2020-03-24 16:52 被阅读0次

    上一篇简单学习了一下 Android 原生接入 Flutter Module,现在学习一下两者之间的数据交互;

    录制了一套完整的flutter开发视频,写一套flutter系列文字版又太费时间。(末尾获取flutter完整视频教程
    转载:https://www.jianshu.com/u/cf68df6e2121

    FlutterAndroid/iOS 之间信息交互通过 Platform Channel 进行桥接;Flutter 定义了三种不同的 Channel;但无论是传递方法还是传递事件,其本质上都是数据的传递;

    1. MethodChannel:用于传递方法调用;
    2. EventChannel:用于数据流信息通信;
    3. BasicMessageChannel:用于传递字符串和半结构化的信息;

    每种 Channel 均包含三个成员变量;

    1. name:代表 Channel 唯一标识符,Channel 可以包含多个,但 name 为唯一的;
    2. messager:代表消息发送与接收的工具 BinaryMessenger
    3. codec:代表消息的编解码器;

    以上一节 Android 原生集成 Flutter Module 为基础,针对不同的 Channel 进行学习尝试;且小菜通过 View / Fragment / Activity 三种原生加载方式进行测试;

    MethodChannel

    Flutter 页面,点击右下角按钮,将消息传递给 AndroidMethodChannel 通过 invokeMethod 进行消息发送,固定的第一个 name 参数是必须存在且唯一的,与 Android 原生中匹配;第二个参数为传送的数据,类似于 Intent 中的 ExtraData,只是支持的数据类型偏少;第三个可隐藏的参数为编解码器;

    class _MyHomePageState extends State<MyHomePage> {
      static const methodChannel = const MethodChannel('ace_demo_android_flutter');
      String _result = '';
    
      Future<Null> _getInvokeResult() async {
        try {
          _result = await methodChannel
              .invokeMethod('ace_demo_user', {'name': '我不是老猪', 'gender': 1});
        } on PlatformException catch (e) {
          _result = "Failed: '${e.message}'.";
        }
        setState(() {});
      }
    
      void _incrementCounter() {
        setState(() {
          _getInvokeResult();
        });
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(title: Text(widget.title)),
            body: Center(
                child: Text('${_result}', style: TextStyle(color: Colors.blueAccent, fontSize: 18.0))),
            floatingActionButton: FloatingActionButton(
                onPressed: _incrementCounter, child: Icon(Icons.arrow_back)));
      }
    }
    
    

    1. FlutterView

    Android 集成 Flutter Module 中时,官方建议使用 View / Fragment 方式,在使用 View 时,建议 Activity 继承 FlutterActivity 或 FlutterFragmentActivity,通过 FlutterView 进行 MethodChannel 绑定监听;

    public class MyFlutterViewActivity extends FlutterFragmentActivity {
    
        private static final String CHANNEL = "ace_demo_android_flutter";
        private static final String TAG = "MyFlutterViewActivity";
        private static final int REQUEST_CODE = 1000;
        FlutterView flutterView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_flutter);
    
            DisplayMetrics outMetrics = new DisplayMetrics();
            getWindowManager().getDefaultDisplay().getMetrics(outMetrics);
            int widthPixels = outMetrics.widthPixels;
            int heightPixels = outMetrics.heightPixels;
    
            flutterView = Flutter.createView(MyFlutterViewActivity.this, getLifecycle(), "/");
            FrameLayout.LayoutParams layout = new FrameLayout.LayoutParams(widthPixels, heightPixels);
            addContentView(flutterView, layout);
            new MethodChannel(flutterView, CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                @Override
                public void onMethodCall(MethodCall call, MethodChannel.Result result) {
                    if (call.method.equals("ace_demo_user")) {
                        if (call.arguments != null) {
                            Log.e(TAG, "Flutter -> Android 回调内容:" + call.arguments.toString());
                        } else {
                            Log.e(TAG, "Flutter -> Android 回调参数为空!");
                        }
                        result.success("Android -> Flutter 接收回调后返回值:" + TAG);
                        Intent intent = new Intent();
                        intent.putExtra("data", call.arguments!=null?call.arguments.toString():"");
                        setResult(REQUEST_CODE, intent);
                        MyFlutterViewActivity.this.finish();
                    } else {
                        result.notImplemented();
                    }
                }
            });
        }
    }
    
    

    2. FlutterFragment

    使用 Fragment 方式时与 View 方式均需要获取 FlutterView 进行绑定,此时 Fragment 继承 FlutterFragment 较易获取;

    public class MyFlutterFragment extends FlutterFragment {
    
        private static final String CHANNEL = "ace_demo_android_flutter";
        private static final String TAG = "MyFlutterFragment";
    
        @SuppressWarnings("unchecked")
        @Override
        public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
            super.onViewCreated(view, savedInstanceState);
            new MethodChannel((FlutterView) getView(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                @Override
                public void onMethodCall(MethodCall call, final MethodChannel.Result result) {
                    if (call.method.equals("ace_demo_user")) {
                        if (call.arguments != null) {
                            Log.e(TAG, "Flutter -> Android 回调内容:" + call.arguments.toString());
                        } else {
                            Log.e(TAG, "Flutter -> Android 回调参数为空!");
                        }
                        result.success("Android -> Flutter 接收回调后返回值:" + TAG);
                        Toast.makeText(getActivity(), (call.arguments != null) ? "回调内容为:" + call.arguments.toString() : "回调参数为空!", Toast.LENGTH_SHORT).show();
                    } else {
                        result.notImplemented();
                    }
                }
            });
        }
    }
    
    

    3. FlutterActivity

    使用 Activity 方式同样需要获取 FlutterView 此时直接继承 FlutterActivity 或 FlutterFragmentActivity 即可;

    public class MyFlutterActivity extends FlutterActivity {
    
        private static final String CHANNEL = "ace_demo_android_flutter";
        private static final String TAG = "MyFlutterActivity";
        private static final int REQUEST_CODE = 1000;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            GeneratedPluginRegistrant.registerWith(this);
            new MethodChannel(getFlutterView(), CHANNEL).setMethodCallHandler(new MethodChannel.MethodCallHandler() {
                @Override
                public void onMethodCall(MethodCall call, MethodChannel.Result result) {
                    if (call.method.equals("ace_demo_user")) {
                        if (call.arguments != null) {
                            Log.e(TAG, "Flutter -> Android 回调内容:" + call.arguments.toString());
                        } else {
                            Log.e(TAG, "Flutter -> Android 回调参数为空!");
                        }
                        result.success("Android -> Flutter 接收回调后返回值:" + TAG);
                        Intent intent = new Intent();
                        intent.putExtra("data", call.arguments!=null?call.arguments.toString():"");
                        setResult(REQUEST_CODE, intent);
                        MyFlutterActivity.this.finish();
                    } else {
                        result.notImplemented();
                    }
                }
            });
        }
    }
    
    
    image

    我们分析 FlutterFragmentFlutterActivity 时会发现,依旧是一层层封装的 FlutterView

    测试 onMethodCall 中若有与 Flutter 中传递的相同 method name 时可以尝试获取传递参数;若此时需要向 Flutter 返回传递参数可以通过 result.success() 方法进行数据传递,若无需传递则可不设置当前方法;

    理解,MethodChannel 主要是由 Flutter 主动向 Android 原生发起交互请求,小菜理解相对于于原生为被动式交互较多;

    EventChannel

    EventChannel 可以由 Android 原生主动向 Flutter 发起交互请求,小菜理解相对于原生为主动式交互,类似于 Android 发送一个广播在 Flutter 端进行接收;其使用方式与 MethodChannel 类似,根据 FlutterView 进行绑定监听,与上述相似,小菜不分开写了;

    EventChannel 是对 Stream 流的监听,通过 onListener 进行消息发送,通过 onCancel 对消息取消;

    new EventChannel(flutterView, CHANNEL).setStreamHandler(new EventChannel.StreamHandler() {
        @Override
        public void onListen(Object arguments, final EventChannel.EventSink events) {
            events.success("我来自 " + TAG +" !! 使用的是 EventChannel 方式");
        }
    
        @Override
        public void onCancel(Object arguments) {
        }
    });
    
    

    Flutter 端通过 receiveBroadcastStream 进行数据流监听;分析源码得知,其内部同样是通过 invokeMethod 方法进行发送;listen 方法中,onData 为必须参数用作收到 Android 端发送数据的回调;onError 为数据接收失败回调;onDone 为接收数据结束回调;

    StreamSubscription<T> listen(void onData(T event),
          {Function onError, void onDone(), bool cancelOnError});
    
    
    class _MyHomePageState extends State<MyHomePage> {
      static const eventChannel = const EventChannel('ace_demo_android_flutter');
      String _result = '';
      StreamSubscription _streamSubscription;
    
      @override
      void initState() {
        super.initState();
        _getEventResult();
      }
    
      @override
      void dispose() {
        super.dispose();
        if (_streamSubscription != null) {
          _streamSubscription.cancel();
        }
      }
    
      _getEventResult() async {
        try {
          _streamSubscription =
              eventChannel.receiveBroadcastStream().listen((data) {
            setState(() {
              _result = data;
            });
          });
        } on PlatformException catch (e) {
          setState(() {
            _result = "event get data err: '${e.message}'.";
          });
        }
      }
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
            appBar: AppBar(title: Text(widget.title)),
            body: Center(
                child: Text('${_result}',
                    style: TextStyle(color: Colors.blueAccent, fontSize: 18.0))),
            floatingActionButton: FloatingActionButton(
                onPressed: _incrementCounter, child: Icon(Icons.arrow_back)));
      }
    }
    
    

    BasicMessageChannel

    BasicMessageChannel 主要传递字符串和半结构化的数据交互;其编解码有多种类型,在使用时建议 AndroidFlutter 两端一致;

    1. BinaryCodec:基本二进制编码类型;
    2. StringCodec:字符串与二进制之间的编码类型;
    3. JSONMessageCodecJson 与二进制之间的编码类型;
    4. StandardMessageCodec:默认编码类型,包括基础数据类型、二进制数据、列表、字典等与二进制之间等编码类型;

    Flutter -> Android

    Flutter 端向 Android 端发送 send 数据请求,Android 端接收到后通过 replayFlutter 端发送消息,从而完成一次消息交互;

      // Flutter 端
      static const basicChannel = BasicMessageChannel<String>('ace_demo_android_flutter', StringCodec());
    
      @override
      void initState() {
        super.initState();
        _getBasicResult();
      }
    
      _getBasicResult() async {
        final String reply = await basicChannel.send('ace_demo_user');
      setState(() {
          _result = reply;
        });
      }
    
      // Android 端
      final BasicMessageChannel channel = new BasicMessageChannel<String> (flutterView, CHANNEL, StringCodec.INSTANCE);
      channel.setMessageHandler(new BasicMessageChannel.MessageHandler() {
          @Override
          public void onMessage(Object o, BasicMessageChannel.Reply reply) {
              reply.reply("我来自 " + TAG +" !! 使用的是 BasicMessageChannel 方式");
          }
      });
    
    

    Android -> Flutter

    根据上述继续由 Android 端主动向 Flutter 端发送数据,Android 通过 sendFlutter 发送数据请求,Flutter 通过 setMessageHandler 接收后向 Androidreturn 返回结果,再由 Android 回调接收,从而完成一次数据交互;

      public void send(T message) {
          this.send(message, (BasicMessageChannel.Reply)null);
      }
    
      public void send(T message, BasicMessageChannel.Reply<T> callback) {
          this.messenger.send(this.name, this.codec.encodeMessage(message), callback == null ? null : new BasicMessageChannel.IncomingReplyHandler(callback));
      }
    

    分析源码 send 有两个构造函数,有两个参数的构造方法用来接收 Flutter 回调的数据;

      // Flutter 端
      static const basicChannel = BasicMessageChannel<String>('ace_demo_android_flutter', StringCodec());
    
      @override
      void initState() {
        super.initState();
        _getBasicResult();
      }
    
      _getBasicResult() async {
        final String reply = await
        channel.setMessageHandler((String message) async {
          print('Flutter Received: ${message}');
          setState(() {
            _result = message;
          });
          return "{'name': '我不是老猪', 'gender': 1}";
        });
      }
    
      // Android 端
      channel.setMessageHandler(new 
      BasicMessageChannel.MessageHandler() {
          @Override
          public void onMessage(Object o, BasicMessageChannel.Reply reply) {
              reply.reply("我来自 " + TAG +" !! 使用的是 BasicMessageChannel 方式");
              channel.send("ace_demo_user");
              //channel.send("ace_demo_user", new BasicMessageChannel.Reply() {
              //    @Override
              //    public void reply(Object o) {
              //        Intent intent = new Intent();
              //        intent.putExtra("data", o!=null?o.toString():"");
              //        setResult(REQUEST_CODE, intent);
              //        MyFlutterViewActivity.this.finish();
              //    }
              //});
          }
      });
    

    注意事项

    1. ensureInitializationComplete must be called after startInitialization

    在从 AndroidFlutter 交互过程时,崩溃提示如下问题;

    发现在 Application 中需要使用 FlutterApplicationFlutterApplication 的作用就是通过调用 FlutterMainstartInitialization 方法进行初始化;

      import io.flutter.app.FlutterApplication;
    
      public class MyApplication extends FlutterApplication {
      }
    
    2. 注意交互返回中内容是否为空

    在测试 MethodChannel 时,invokeMethod 时尝试了一个参数和两个参数的构造,只有一个参数的 invokeMethod 是没有回调内容的,而小菜在 Android 端未判空,虽然没有报异常,但是后面的代码都没有执行,很基本的问题却困扰小菜很久,希望大家可以避免;

    3. 多种 Platform Channel 共同使用

    测试过程中,多种 Platform Channel 可以共同使用,可以根据业务或场景的不同配合使用,提高效率;

    https://github.com/xiangjiana/Android-MS
    (点个star。专注分享技术干货/编程资源/求职面试/成长感悟等,关注送983页PDF面试资源,免费下载CSDN资源。领取PDF面试大全,附送CSDN下载资源+flutter全套视频+音视频高手开发

    相关文章

      网友评论

        本文标题:Flutter 视频系列: 图解 Flutter 与 Andro

        本文链接:https://www.haomeiwen.com/subject/qktayhtx.html