热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Flutter基础:Flutter与Android的相互通信

成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~。前言本来这篇文章

成为一名优秀的Android开发,需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样~。

前言

本来这篇文章应该讲一下Flutter的插件开发,但是在插件开发的基础是PlatformChannel,也就是Flutter与Android/iOS Native的通信,理解了这一个知识点,Flutter的插件开发也就不在话下。

1.PlatformChannel概述

Flutter不能完成所有Native的功能,因此需要Flutter与Native的通信,Flutter提供了一套Platform Channel的机制,来满足Flutter与Native通信的需求。 下面是PlatformChannel架构。

Flutter基础:Flutter与Android的相互通信

图中可以看到,Flutter是Client端,Native是Host,Client和host通信是通过PlatformChannel,Client通过PlatformChannel向Host发送消息,Host监听PlatformChannel并接收消息,然后将响应结果发送给Client。消息和响应以异步方式传递,以确保UI不阻塞。另外,PlatformChannel是双工的,这意味着Flutter和Native可以交替做Client和Host。

Flutter定义了三种不同类型的PlatformChannel,它们分别是:

  • MethodChannel:用于传递方法调用,是比较常用的PlatformChannel。
  • EventChannel: 用于传递事件。
  • BasicMessageChannel:用于传递数据。

这几个PlatformChannel的用法都不难,本文会以比较常用的MethodChannel来进行举例。在此之前我们先要了解BinaryMessenger、Codec、Handler的概念。

BinaryMessenger BinaryMessenger是PlatformChannel与Flutter端的通信的工具,其通信使用的消息格式为二进制格式数据,BinaryMessenger在Android中是一个接口,它的实现类为FlutterNativeView。

Codec Codec是消息编解码器,主要用于将二进制格式的数据转化为Handler能够识别的数据,Flutter定义了两种Codec:MessageCodec和MethodCodec。MessageCodec用于二进制格式数据与基础数据之间的编解码,BasicMessageChannel所使用的编解码器是MessageCodec。MethodChannel和EventChannel所使用的编解码均为MethodCodec。

Handler Flutter定义了三种类型的Handler,它们与PlatformChannel类型一一对应,分别是MessageHandler、MethodHandler、StreamHandler。在使用PlatformChannel时,会为它注册一个Handler,PlatformChannel会将该二进制数据通过Codec解码为转化为Handler能够识别的数据,并交给Handler处理。当Handler处理完消息之后,会通过回调函数返回result,将result通过编解码器编码为二进制格式数据,通过BinaryMessenger发送回Flutter端。

MethodChannel可以实现Flutter调用Android,也可以实现Android调用Flutter,这里分别来进行举例。

2.Flutter调用Android

这里实现一个Android的简单的功能:弹出一个AlertDialog,然后在Flutter中调用这一功能。

Android端实现 先在MainActivity中实现功能,如下所示。

package com.example.platform_channel;

import android.app.AlertDialog;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);//1

        MethodChannel methodChannel = new MethodChannel(getFlutterView(), "com.example.platform_channel/dialog");//2
        methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {//3
            @Override
            public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
                if ("dialog".equals(methodCall.method)) {
                    if (methodCall.hasArgument("content")) {
                        showAlertDialog();
                        result.success("弹出成功");
                    } else {
                        result.error("error", "弹出失败", "content is null");
                    }
                } else {
                    result.notImplemented();
                }
            }
            private void showAlertDialog() {
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                builder.setPositiveButton("确定", null);
                builder.setTitle("Flutter调用Android");
                builder.show();
            }
        });
    }
}

注释1处用于注册插件,这个是创建Flutter工程时MainActivity自带的。 注释2处创建一个MethodChannel,它有两个参数,一个是getFlutterView方法,用于获取FlutterView,FlutterView实现了BinaryMessenger接口。一个是MethodChannel的Name,这个Name要保证是唯一的,后面Flutter端实现中会用到这个Name。 注释3处为methodChannel注册一个MethodCallHandler,用于监听回调的数据。 onMethodCall方法中的result是Flutter端传来的数据,我们需要对数据进行判断,然后向Flutter端发送数据。 向Flutter端发送数据有以下方法:

result.success(Object result) 结果成功,将result返回给Flutter端。

result.error(String errorCode,String errorMsg,Object errorDetails) 结果失败,将errorCode、errorMsg、errorDetails返回给Flutter端。

result.notImplemented() Android端没有实现Flutter端需要的方法,会将notImplemented返回给Flutter端。

Flutter端实现 在main.dart中加入如下代码。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  static const platformChannel =
      const MethodChannel('com.example.platform_channel/dialog');//1

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      home: Scaffold(
        appBar: AppBar(
          title: Text("Flutter调用Android"),
        ),
        body: Padding(
          padding: EdgeInsets.all(40.0),
          child: RaisedButton(
            child: Text("调用Dialog"),
            onPressed: () {
              showDialog("Flutter调用AlertDialog");
            },
          ),
        ),
      ),
    );
  }

  void showDialog(String content) async {
    var arguments = Map();
    arguments['content'] = content;
    try {
      String result = await platformChannel.invokeMethod('dialog', arguments);//2
      print('showDialog ' + result);
    } on PlatformException catch (e) {
      print('showDialog ' + e.code + e.message + e.details);
    } on MissingPluginException catch (e) {
      print('showDialog ' + e.message);
    }
  }
}

注释1处创建了MethodChannel,它需要传入MethodChannel的Name,这个Name要保证和Android端设置的Name是一样的。当点击按钮时会触发showDialog方法。注释2处用于调用Android中的方法,第一个参数是方法的名称,第二个参数arguments只能是Map或者JSON类型的,是我们需要传递给Android端的数据。 运行程序,当我们点击”调用Dialog”按钮时,效果如下所示。

Flutter基础:Flutter与Android的相互通信

3.Android调用Flutter

有的时候Flutter调用Android后,Android还会将结果返回给Flutter,虽然有时可以用result来实现,但Android端的处理可能是异步的,result对象也不能长期的持有,这时就需要Android来调用Flutter。 因为页面UI是Flutter端绘制的,我们很难在页面中控制Android端,要实现Android调用Flutter,可以利用Android的Activty的生命周期,如果将应用切到后台再切回前台,这样Activty的onResume方法就会被调用,我们在onResume方法中实现调用Flutter的功能就可以了。

Android端的实现

package com.example.platform_channel;

import android.os.Bundle;
import android.util.Log;
import java.util.HashMap;
import java.util.Map;
import io.flutter.app.FlutterActivity;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    public static final String MAIN_ACTIVITY = "MainActivity";
    MethodChannel methodChannel;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        GeneratedPluginRegistrant.registerWith(this);

        methodChannel = new MethodChannel(getFlutterView(),"com.example.platform_channel/text");//1
    }

    @Override
    protected void onResume() {
        super.onResume();
        Map map = new HashMap();
        map.put("content","Android进阶三部曲");
        methodChannel.invokeMethod("showText", map, new MethodChannel.Result() {//2
            @Override
            public void success(Object o) {
                Log.d(MAIN_ACTIVITY,(String)o);
            }
            @Override
            public void error(String errorCode, String errorMsg, Object errorDetail) {
                Log.d(MAIN_ACTIVITY,"errorCode:"+errorCode+" errorMsg:"+errorMsg+" errorDetail:"+(String)errorDetail);
            }
            @Override
            public void notImplemented() {
                Log.d(MAIN_ACTIVITY,"notImplemented");
            }
        });
    }
}


和Flutter调用Android的代码是类似的,在注释1处创建MethodChannel,然后在注释2处调用Flutter端的showText方法,并将数据以Map的形式传递过去。MethodChannel.Result() 的回调里有三个方法,通过这三个方法可以得到Android调用Flutter的结果。

Flutter端的实现

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
  @override
  State createState() {
    // TODO: implement createState
    return MyAppState();
  }
}

class MyAppState extends State {
  static const platformChannel =
      const MethodChannel('com.example.platform_channel/text');

  String textCOntent= 'Flutter端初始文字';

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    platformChannel.setMethodCallHandler((methodCall) async {
      switch (methodCall.method) {
        case 'showText':
          String cOntent= await methodCall.arguments['content'];
          if (content != null && content.isNotEmpty) {
            setState(() {
              textCOntent= content;
            });
            return 'success';
          } else {
            throw PlatformException(
                code: 'error', message: '失败', details: 'content is null');
          }
          break;
        default:
          throw MissingPluginException();
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Demo",
      home: Scaffold(
        appBar: AppBar(
          title: Text('Android调用Flutter'),
        ),
        body: Padding(
          padding: EdgeInsets.all(40.0),
          child: Text(textContent),
        ),
      ),
    );
  }
}


因为要实现Flutter页面的改变,就需要在initState方法中为MethodChannel添加回调,如果Android端传递过来的方法名称为showText,就获取Android端传来的content的值,赋值给Text来改变页面的状态。 运行程序后,将程序切到后台再切回前台,效果如下图所示。

Flutter基础:Flutter与Android的相互通信

作者:刘望舒
链接:https://juejin.cn/post/6844903906842017800
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


推荐阅读
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • CEPH LIO iSCSI Gateway及其使用参考文档
    本文介绍了CEPH LIO iSCSI Gateway以及使用该网关的参考文档,包括Ceph Block Device、CEPH ISCSI GATEWAY、USING AN ISCSI GATEWAY等。同时提供了多个参考链接,详细介绍了CEPH LIO iSCSI Gateway的配置和使用方法。 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
  • 本文介绍了PhysioNet网站提供的生理信号处理工具箱WFDB Toolbox for Matlab的安装和使用方法。通过下载并添加到Matlab路径中或直接在Matlab中输入相关内容,即可完成安装。该工具箱提供了一系列函数,可以方便地处理生理信号数据。详细的安装和使用方法可以参考本文内容。 ... [详细]
  • 解决VS写C#项目导入MySQL数据源报错“You have a usable connection already”问题的正确方法
    本文介绍了在VS写C#项目导入MySQL数据源时出现报错“You have a usable connection already”的问题,并给出了正确的解决方法。详细描述了问题的出现情况和报错信息,并提供了解决该问题的步骤和注意事项。 ... [详细]
  • 关键词:Golang, Cookie, 跟踪位置, net/http/cookiejar, package main, golang.org/x/net/publicsuffix, io/ioutil, log, net/http, net/http/cookiejar ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
author-avatar
锶飨_646_165
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有