Flutter多Engine之间的通信

2,639 阅读3分钟

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')注解来保证不会在编译之后方法被优化了

都看到这里了,微信搜索 [ 序员说 ] 关注公众号,持续获取最新文章