Android与flutter通信

316 阅读4分钟

1.PlatformChannel 概述

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

image.png

图中可以看到,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.flutterdemo;

import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
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(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        GeneratedPluginRegistrant.registerWith(flutterEngine);
        MethodChannel methodChannel = new MethodChannel(flutterEngine.getDartExecutor(), "com.example.platform_channel/dialog");//2
        methodChannel.setMethodCallHandler(new MethodChannel.MethodCallHandler() {
            @Override
            public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
                if ("dialog".equals(call.method)) {
                    if (call.hasArgument("content")) {
                        result.success("成功");
                    } else {
                        result.error("error", "失败", "content is null");
                    }
                } else {
                    result.notImplemented();
                }
            }
        });
    }
}

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);
    }
  }
}

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.flutterdemo;

import android.os.Bundle;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import java.util.HashMap;
import java.util.Map;

import io.flutter.embedding.android.FlutterActivity;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugins.GeneratedPluginRegistrant;

public class MainActivity extends FlutterActivity {
    private static MethodChannel methodChannel;
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
        super.configureFlutterEngine(flutterEngine);
        GeneratedPluginRegistrant.registerWith(flutterEngine);
        methodChannel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(),"com.example.platform_channel/text");

    }

    @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(TAG,(String)o);
            }
            @Override
            public void error(String errorCode, String errorMsg, Object errorDetail) {
                Log.d(TAG,"errorCode:"+errorCode+" errorMsg:"+errorMsg+" errorDetail:"+(String)errorDetail);
            }
            @Override
            public void notImplemented() {
                Log.d(TAG,"notImplemented");
            }
        });

    }
}

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 基础(十三)Flutter 与 Android 的相互通信