如需转载,请注明出处:Flutter学习笔记(29)--Flutter如何与native进行通信

前言:在我们开发Flutter项目的时候,难免会遇到需要调用native api或者是其他的情况,这时候就需要处理Flutter与native的通信问题,一般常用的Flutter与native的通信方式有3中。

1.MethodChannel:Flutter端向native端发送通知,通常用来调用native的某一个方法。

2.EventChannel:用于数据流的通信,有监听功能,比如电量变化后直接推送给Flutter端。

3.BasicMessageChannel:用于传递字符串或半结构体的数据。

接下来具体看一下每种通信方式的使用方法!

  • MethodChannel

先来整体说一下逻辑思想吧,这样能更容易理解一些,如果想要实现Flutter与native通信,首先要建立一个通信的通道,通过一个通道标识来进行匹配,匹配上了之后Flutter端通过invokeMethod调用方法来发起一个请求,在native端通过onMethodCall进行匹配请求的key,匹配上了就处理对应case内的逻辑!!!整体来看,我感觉有点EventBus的意思呢,就像是一条事件总线。。。

第一步:实现通信插件Plugin-native端

由于一个项目中可能会需要很多Flutter与native的通信,所以我这里是将测试的插件封装到一个类里面了,然后在MainActivity里面的onCreate进行注册

package com.example.flutter_demo;

import android.content.Context;

import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry; public class TestPlugin implements MethodChannel.MethodCallHandler {
public static String CHANNELNAME = "channel_name";//每一个通信通道的唯一标识,在整个项目内唯一!!!
private static MethodChannel methodChannel;
private Context context; public TestPlugin(Context context) {
this.context = context;
} public static void registerWith(PluginRegistry.Registrar registrar){
methodChannel = new MethodChannel(registrar.messenger(),CHANNELNAME);
TestPlugin instance = new TestPlugin(registrar.activity());
methodChannel.setMethodCallHandler(instance);
} @Override
public void onMethodCall(MethodCall methodCall, MethodChannel.Result result) {
if (methodCall.method.equals("method_key")){
result.success("what is up man???");
}
}
}

注:CHANNELNAME-->上面说过了,由于项目内会有很多的通信,所以我们定义的Channel必须是唯一的!!!!

TestPlugin实现MethodChannel.MethodCallHandler,定义一个对外暴露的注册方法registerWith,因为我们需要在MainActivity进行注册,在registerWith方法内初始化MethodChannel

接下来我们看一下onMethodCall方法,这个方法在Flutter发起请求时被调用,方法内有两个参数,一个methodCall和一个result,我们分别来说一下这两个参数:

methodCall:其中当前请求的相关信息,比如匹配请求的key

result:用于给Flutter返回数据,有3个方法,result.success(成功调用)、result.erro(失败调用)、result.notImplemented(方法没有实现调用)

第二步:注册通信插件Plugin-native端

package com.example.flutter_demo;

import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant; public class MainActivity extends FlutterActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
TestPlugin.registerWith(this.registrarFor(TestPlugin.CHANNELNAME));
}
}

注册这块我感觉作用是起到了一个桥梁的作用,通过注册将插件和Flutter内定义的CHANNEL关联了起来。

第三步:Flutter内发起通信请求-flutter端

import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; void main() => runApp(MyApp()); class MyApp extends StatefulWidget{
@override
State<StatefulWidget> createState() {
// TODO: implement createState
return new MyAppState();
} } class MyAppState extends State<MyApp> {
var _textContent = 'welcome to flutter word'; Future<Null> _changeTextContent() async{
//channel_name每一个通信通道的唯一标识,在整个项目内唯一!!!
const platfom = const MethodChannel('channel_name');
try {
//method_key是插件TestPlugin中onMethodCall回调匹配的key
String resultValue = await platfom.invokeMethod('method_key');
setState(() {
_textContent = resultValue;
});
}on PlatformException catch (e){
print(e.toString());
}
} @override
Widget build(BuildContext context) {
// TODO: implement build
return new MaterialApp(
theme: new ThemeData(
primaryColor: Colors.white,
),
debugShowCheckedModeBanner: false,
title: 'demo',
home: new Scaffold(
appBar: new AppBar(
title: new Text('Demo'),
leading: Icon(Icons.menu,size: ,),
actions: <Widget>[
Icon(Icons.search,size: ,)
],
),
body: new Center(
child: new Text(_textContent),
),
floatingActionButton: new FloatingActionButton(onPressed: _changeTextContent,child: new Icon(Icons.adjust),),
),
);
}
}

这里的功能就是页面中央有一个text,通过点击一个按钮,发起通信请求,通信成功在就收到native返回的数据后将text的文案修改。

我们看一下最终的效果:

                

MethodChannel通信是双向的,也就是说,Flutter端可以向native发起通信,native也可以向Flutter端发起通信,本质上就是反过来调用一下,原理上是同一个意思,具体的代码就不在这里写了,需要的话可以自行百度一下!

  • EventChannel

EventChannel的使用我们也以官方获取电池电量的demo为例,手机的电池状态是不停变化的。我们要把这样的电池状态变化由Native及时通过EventChannel来告诉Flutter。这种情况用之前讲的MethodChannel办法是不行的,这意味着Flutter需要用轮询的方式不停调用getBatteryLevel来获取当前电量,显然是不正确的做法。而用EventChannel的方式,则是将当前电池状态"推送"给Flutter。

第一步:MainActivity内注册EventChannel,并提供获取电量的方法-native端

public class EventChannelPlugin implements EventChannel.StreamHandler {

    private Handler handler;
private static final String CHANNEL = "com.example.flutter_battery/stream";
private int count = ; public static void registerWith(PluginRegistry.Registrar registrar) {
// 新建 EventChannel, CHANNEL常量的作用和 MethodChannel 一样的
final EventChannel channel = new EventChannel(registrar.messenger(), CHANNEL);
// 设置流的处理器(StreamHandler)
channel.setStreamHandler(new EventChannelPlugin());
} @Override
public void onListen(Object o, EventChannel.EventSink eventSink) {
// 每隔一秒数字+1
handler = new Handler(message -> {
// 然后把数字发送给 Flutter
eventSink.success(++count);
handler.sendEmptyMessageDelayed(, );
return false;
});
handler.sendEmptyMessage(); } @Override
public void onCancel(Object o) {
handler.removeMessages();
handler = null;
count = ;
}
}

其中onCancel代表对面不再接收,这里我们应该做一些clean up的事情。而 onListen则代表通道已经建好,Native可以发送数据了。注意onListen里带的EventSink这个参数,后续Native发送数据都是经过EventSink的。

第二步:同MethodChannel一样,发起通信请求

class _MyHomePageState extends State<MyHomePage> {
// 创建 EventChannel
static const stream = const EventChannel('com.example.flutter_battery/stream'); int _count = ; StreamSubscription _timerSubscription; void _startTimer() {
if (_timerSubscription == null)
// 监听 EventChannel 流, 会触发 Native onListen回调
_timerSubscription = stream.receiveBroadcastStream().listen(_updateTimer);
} void _stopTimer() {
_timerSubscription?.cancel();
_timerSubscription = null;
setState(() => _count = );
} void _updateTimer(dynamic count) {
print("--------$count");
setState(() => _count = count);
} @override
void dispose() {
super.dispose();
_timerSubscription?.cancel();
_timerSubscription = null;
} @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Container(
margin: EdgeInsets.only(left: , top: ),
child: Center(
child: Column(
children: [
Row(
children: <Widget>[
RaisedButton(
child: Text('Start EventChannel',
style: TextStyle(fontSize: )),
onPressed: _startTimer,
),
Padding(
padding: EdgeInsets.only(left: ),
child: RaisedButton(
child: Text('Cancel EventChannel',
style: TextStyle(fontSize: )),
onPressed: _stopTimer,
)),
Padding(
padding: EdgeInsets.only(left: ),
child: Text("$_count"),
)
],
)
],
),
),
),
);
}
}

整体说明一下:Flutter端通过stream.receiveBroadcastStream().listen监听native发送过来的数据,native端通过eventSink.success(++count)不断的将数据返回给Flutter端,这样就实现了我们想要的实时监听的效果了!

  • BasicMessageChannel

其实他就是一个简版的MethodChannel,也可以说MethodChannel是基于BasicMessageChannel实现的,BasicMessageChannel只是进行通信,更通俗的理解就是两端发通知,但是不需要进行方法匹配。

第一步:初始化及注册-native

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 省略其他代码... messageChannel = new BasicMessageChannel<>(flutterView, CHANNEL, StringCodec.INSTANCE);
messageChannel.
setMessageHandler(new MessageHandler<String>() {
@Override
public void onMessage(String s, Reply<String> reply) {
// 接收到Flutter消息, 更新Native
onFlutterIncrement();
reply.reply(EMPTY_MESSAGE);
}
}); FloatingActionButton fab = findViewById(R.id.button);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 通知 Flutter 更新
sendAndroidIncrement();
}
});
} private void sendAndroidIncrement() {
messageChannel.send(PING);
} private void onFlutterIncrement() {
counter++;
TextView textView = findViewById(R.id.button_tap);
String value = "Flutter button tapped " + counter + (counter == ? " time" : " times");
textView.setText(value);
}

第二步:Flutter端发起通信-flutter

class _MyHomePageState extends State<MyHomePage> {
static const String _channel = 'increment';
static const String _pong = 'pong';
static const String _emptyMessage = '';
static const BasicMessageChannel<String> platform =
BasicMessageChannel<String>(_channel, StringCodec()); int _counter = ; @override
void initState() {
super.initState();
// 设置消息处理器
platform.setMessageHandler(_handlePlatformIncrement);
} // 如果接收到 Native 的消息 则数字+1
Future<String> _handlePlatformIncrement(String message) async {
setState(() {
_counter++;
});
// 发送一个空消息
return _emptyMessage;
} // 点击 Flutter 中的 FAB 则发消息给 Native
void _sendFlutterIncrement() {
platform.send(_pong);
} @override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('BasicMessageChannel'),
),
body: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Expanded(
child: Center(
child: Text(
'Platform button tapped $_counter time${_counter == 1 ? '' : 's'}.',
style: const TextStyle(fontSize: 17.0)),
),
),
Container(
padding: const EdgeInsets.only(bottom: 15.0, left: 5.0),
child: Row(
children: <Widget>[
Image.asset('assets/flutter-mark-square-64.png', scale: 1.5),
const Text('Flutter', style: TextStyle(fontSize: 30.0)),
],
),
),
],
)),
floatingActionButton: FloatingActionButton(
onPressed: _sendFlutterIncrement,
child: const Icon(Icons.add),
),
);
}
}

总结:以上就是Flutter和native通信的全部内容了,理解了以后其实很简单,上面的内容有一些我个人的理解,更深一层的还需要继续挖掘!

Flutter学习笔记(29)--Flutter如何与native进行通信的更多相关文章

  1. Flutter学习笔记(30)--Android原生与Flutter混编

    如需转载,请注明出处:Flutter学习笔记(30)--Android原生与Flutter混编 这篇文章旨在学习如何在现有的Android原生项目上集成Flutter,实现Android与Flutte ...

  2. Flutter学习笔记(3)--Dart变量与基本数据类型

    一.变量 在Dart里面,变量的声明使用var.Object或Dynamic关键字,如下所示: var name = ‘张三’: 在Dart语言里一切皆为对象,所以如果没有将变量初始化,那么它的默认值 ...

  3. Flutter学习笔记(4)--Dart函数

    如需转载,请注明出处:Flutter学习笔记(4)--Dart函数 Dart是一个面向对象的语言,所以函数也是对象,函数属于Function对象,函数可以像参数一样传递给其他函数,这样便于做回调处理: ...

  4. Flutter学习笔记(5)--Dart运算符

    如需转载,请注明出处:Flutter学习笔记(5)--Dart运算符 先给出一个Dart运算符表,接下来在逐个解释和使用.如下:                            描述       ...

  5. Flutter学习笔记(6)--Dart异常处理

    如需转载,请注明出处:Flutter学习笔记(6)--Dart异常处理 异常是表示发生了意外的错误,如果没有捕获异常,引发异常的隔离程序将被挂起,并且程序将被终止: Dart代码可以抛出并捕获异常,但 ...

  6. Flutter学习笔记(8)--Dart面向对象

    如需转载,请注明出处:Flutter学习笔记(7)--Dart异常处理 Dart作为高级语言,支持面向对象的很多特性,并且支持基于mixin的继承方式,基于mixin的继承方式是指:一个类可以继承自多 ...

  7. Flutter学习笔记(9)--组件Widget

    如需转载,请注明出处:Flutter学习笔记(9)--组件Widget 在Flutter中,所有的显示都是Widget,Widget是一切的基础,我们可以通过修改数据,再用setState设置数据(调 ...

  8. Flutter学习笔记(10)--容器组件、图片组件

    如需转载,请注明出处:Flutter学习笔记(10)--容器组件.图片组件 上一篇Flutter学习笔记(9)--组件Widget我们说到了在Flutter中一个非常重要的理念"一切皆为组件 ...

  9. Flutter学习笔记(11)--文本组件、图标及按钮组件

    如需转载,请注明出处:Flutter学习笔记(10)--容器组件.图片组件 文本组件 文本组件(text)负责显示文本和定义显示样式,下表为text常见属性 Text组件属性及描述 属性名 类型 默认 ...

随机推荐

  1. 树形动态规划 fjutoj-2392 聚会的快乐

    聚会的快乐 TimeLimit:1000MS  MemoryLimit:128MB 64-bit integer IO format:%lld Problem Description 你要组织一个由你 ...

  2. Java集合中List、Set以及Map

    概述: List , Set, Map都是接口:List , Set继承至Collection接口,Map为独立接口 Set下有HashSet,LinkedHashSet,TreeSet List下有 ...

  3. webpack多页应用架构系列(一):一步一步解决架构痛点

    这系列文章讲什么? 前些时间,写过一个项目,前后端分离,没有借助任何框架,项目页面特别的多,页面都是html直接写的,许多公共html,写了好多处,有一个地方需要改就得改好多地方,js也是随意写,每个 ...

  4. pandas数据分析输出excel产生文本形式存储的百分比数据,如何处理?

    关键词: python.pandas.to_excel.文本形式存储的数据 需求描述: 我用 python pandas 写了数据统计与分析脚本,并把计算结果用 pandas 的 to_excel() ...

  5. React-router总结

    版本 v3和v4有一些差距: https://blog.csdn.net/qq_35484341/article/details/80500237 以下的总结,都是基于V4的 官方文档:https:/ ...

  6. 【LeetCode】79-单词搜索

    题目描述 给定一个二维网格和一个单词,找出该单词是否存在于网格中. 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中 "相邻" 单元格是那些水平相邻或垂直相邻的单元格.同一 ...

  7. top_down设计技巧

    写在前面的话 之前梦翼师兄和大家一起学习了层次化设计方法,大家应该懂了,哦,原来所谓的层次化设计就是将一个大的系统不断地拆分成一些便于实现的最小逻辑单元.如果大家真的只是这么想的话,那么梦翼师兄真的是 ...

  8. 玩转 SpringBoot 2 快速整合 | Thymeleaf 篇

    前言 Thymeleaf是一个适用于Web和独立环境的现代服务器端Java模板引擎. Thymeleaf的主要目标是为您的开发工作流程带来优雅的自然模板 - 可以在浏览器中正确显示的HTML,也可以用 ...

  9. null==a和a==null的区别

    在项目代码中在if判断中会经常看到一些老司机这样写:if(null == a),而我由于习惯会写成if(a == null),这两种有什么区别呢? 其实两种并没有什么区别,只是前者在细节处理上.我们在 ...

  10. 排序入门练习题3 谁考了第k名 题解

    题目出处:<信息学奥赛一本通>第二章 上机练习1 题目描述 在一次考试中,每个学生的成绩都不相同,现知道了每个学生的学号和成绩,求考第k名的学生的学号和成绩. 输入格式 输入的第一行包含两 ...