Flutter多Engine之间的通信
通过Channel走一遍原生
各个Engine都通过Channel与原生建立消息通道,即消息的传递是这样的
Flutter->原生->Flutter
优点:
- 开发比较方便,功能明确
缺点:
- 超过2个Engine之间的通信的时候需要确定消息的发送方和接收方是在哪个Engine,当然可以通过给每个Engine分配id来解决这个问题
- 功能变动的时候,可能需要修改原生和Flutter的代码,比较麻烦
- 消息需要走一遍原生,所以需要经过2次encode和2次decode,效率比较低
Flutter->encodeMethodCall->原生->decodeMethodCall->
原生解析确定发送到哪个Engine->encodeMethodCall->
Flutter->decodeMethodCall->获取到发送的数据
通过HttpServer
HttpServer在本地开启一个网络服务,相当于一个本地服务器,Java和Flutter通过http请求来访问
优点:
- 消息不需要经过原生一遍转发,效率比较高
- 消息通信不需要确定发送方和接收方,因为HttpServer开启的时候绑定了端口,端口是系统唯一的
缺点:
- 需要占用本地端口,所以端口绑定的时候需要检测端口是否被占用
例子(有点懒,所以在自动创建的demo上做了修改,点击增加数字的时候发送请求)
- dart开启HttpServer
void serviceStart() async {
HttpServer.bind(InternetAddress.anyIPv4, 4049, shared: true).then((service) {
print("service ${service.address} ${service.port}");
service.listen((request) async {
//HttpResponse对象用于返回客户端
print(request.uri);
switch (request.method) {
case "POST":
var result = await request
.cast<List<int>>()
.transform(utf8.decoder)
.join()
.then(json.decode);
print("POST $request");
request.response
//获取和设置内容类型(报头)
..headers.contentType = ContentType.json
//通过调用Object.toString将Object转换为一个字符串并转成对应编码发送到客户端
..write(json.encode({
"id": "sssss",
"title": "response hello post",
}))
//结束与客户端连接
..close();
break;
case "GET":
request.response
..headers.contentType = ContentType.json
..write(json.encode(
{"id": "idxxxxxx", "title": "GET response "}))
..close();
break;
}
});
});
}
- 在另外的Engine中发送消息
void sendMessage() async {
var dio = Dio();
var response;
if (_counter % 2 == 0) {
response = await dio.get("http://127.0.0.1:4049");
} else {
response =
await dio.post("http://127.0.0.1:4049", data: {"dddd": "post 999"});
}
print("=====client response=====");
print(response.data);
}
- 完整的例子
import 'dart:convert';
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
@pragma('vm:entry-point')
void service() {
serviceStart();
}
void serviceStart() async {
HttpServer.bind(InternetAddress.anyIPv4, 4049, shared: true).then((service) {
print("service ${service.address} ${service.port}");
service.listen((request) async {
//HttpResponse对象用于返回客户端
print(request.uri);
switch (request.method) {
case "POST":
var result = await request
.cast<List<int>>()
.transform(utf8.decoder)
.join()
.then(json.decode);
print("POST $request");
request.response
//获取和设置内容类型(报头)
..headers.contentType = ContentType.json
//通过调用Object.toString将Object转换为一个字符串并转成对应编码发送到客户端
..write(json.encode({
"id": "sssss",
"title": "response hello post",
}))
//结束与客户端连接
..close();
break;
case "GET":
request.response
//获取和设置内容类型(报头)
..headers.contentType = ContentType.json
//通过调用Object.toString将Object转换为一个字符串并转成对应编码发送到客户端
..write(json.encode({"id": "idxxxxxx", "title": "GET response "}))
//结束与客户端连接
..close();
break;
}
});
});
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void sendMessage() async {
var dio = Dio();
var response;
if (_counter % 2 == 0) {
response = await dio.get("http://127.0.0.1:4049");
} else {
response =
await dio.post("http://127.0.0.1:4049", data: {"dddd": "post 999"});
}
print("=====client response=====");
print(response.data);
}
void _incrementCounter() async {
setState(() {
_counter++;
});
sendMessage();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
- 在Java层启动两个Engine
//继承FlutterActivity,默认启动的时候会执行main.dart中的main方法
class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
service()
//延迟10秒执行,防止dart中的服务还没开启,懒得做个按钮点击触发了
Handler().postDelayed({
Thread {
//简单的网络请求,当然可以换成okhttp来请求
var inp = URL("http://127.0.0.1:4049").openConnection().getInputStream()
val bis = BufferedInputStream(inp)
val buf = ByteArrayOutputStream()
var result = bis.read()
while (result != -1) {
buf.write(result)
result = bis.read()
}
var response = buf.toString("UTF-8")
buf.close()
bis.close()
inp.close()
Log.d("MainActivity", "response $response")
}.start()
}, 10000);
}
//启动一个没有UI的 Engine来启动main.dart中的service方法
fun service(){
var flutterEngine = FlutterEngine(this);
flutterEngine.dartExecutor.executeDartEntrypoint(DartEntrypoint(
FlutterMain.findAppBundlePath(),
"service"
))
}
}
结语
可以看出使用HttpServer可以让通信变成http请求,所以可以解耦多个业务之间的代码,将所有业务的数据管理存储放在一个统一的地方。
后续优化:可以定义一个数据接口,各个需要对外暴露数据的业务实现这个接口,然后注册到HttpServer中,通过路由管理接口的访问。
额外知识点
- DartEntrypoint:可以定义加载的Flutter的so和dart的入口函数。 入口函数除了main之前的方法需要添加@pragma('vm:entry-point')注解来保证不会在编译之后方法被优化了
都看到这里了,微信搜索 [ 序员说 ] 关注公众号,持续获取最新文章