Flutter与Android的相互通信
Posted Andy__Wu
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter与Android的相互通信相关的知识,希望对你有一定的参考价值。
前言
本来这篇文章应该讲一下Flutter的插件开发,但是在插件开发的基础是PlatformChannel,也就是Flutter与android/ios Native的通信,理解了这一个知识点,Flutter的插件开发也就不在话下。
1.PlatformChannel概述
Flutter不能完成所有Native的功能,因此需要Flutter与Native的通信,Flutter提供了一套Platform Channel的机制,来满足Flutter与Native通信的需求。
下面是PlatformChannel架构。
图中可以看到,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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
注释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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
注释1处创建了MethodChannel,它需要传入MethodChannel的Name,这个Name要保证和Android端设置的Name是一样的。当点击按钮时会触发showDialog方法。注释2处用于调用Android中的方法,第一个参数是方法的名称,第二个参数arguments只能是Map或者JSON类型的,是我们需要传递给Android端的数据。
运行程序,当我们点击"调用Dialog"按钮时,效果如下所示。
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");
);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
和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<StatefulWidget> createState()
// TODO: implement createState
return MyAppState();
class MyAppState extends State<MyApp>
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与Android的相互通信的主要内容,如果未能解决你的问题,请参考以下文章
Android Flutter:手把手教你如何进行Android 与 Flutter的相互通信
FlutterFlutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 MethodChannel 通信 )
FlutterFlutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 EventChannel 通信 )
FlutterFlutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 BasicMessageChannel 通信 )