61、Flutter插件通信原理<一>

前言

Flutter优势主要体现在UI上--高性能且跨平台表现一致。但是针对平台(Android、IOS)的实现,如:获取电量、判断WiFi使用、调起WebView加载网页等,得调用特定平台的API包。Flutter Plugin就是为调用平台API而生。

下文中所提及到的"平台"指的是Android、IOS两端。

介绍

Flutter Plugin

包含针对Android(Java或Kotlin代码)或iOS(Objective-C或Swift代码)的原生实现,通过Platform Channels与FLutter(dart)层通讯并暴露API。

 

Flutter定义了三种不同类型的Channel,分别是

  1. BasicMessageChannel:用于传递字符串和半结构化的数据;
  2. MethodChannel:用于传递方法调用;
  3. EventChannel:用于数据流的通信;

Platform Channels

  • 允许Flutter UI和平台之间传递消息。
  • Platform Channel中的消息和响应是异步传递的,以确保用户界面保持响应。
  • 一个Flutter应用可以存在多个channel,使用name作为区分。
  • Platform Channel并非是线程安全,故平台跟Flutter Engine的所有交互必须在平台的主线程中执行的。
  • Flutter默认的消息编解码器是StandardMessageCodec class,现支持平台数据类型如下:

创建Flutter Plugin

接下来介绍使用Android Studio创建Flutter Plugin。使用Visual Studio Code创建的过程也是大同小异,机智的你一定能举一反三,在这里就不一一细说。

第一步:选择创建一个Flutter project

第二步:选择Flutter Project的类型为Flutter Plugin

  • Flutter Application 是创建一个纯flutter项目工程
  • Flutter Plugin 是创建一个可以暴露平台API的插件工程
  • Flutter Package 是创建一个纯Dart的组件包。
  • Fultter Module 是创建一个Flutter Module,用于被引入现有的原生App。

第三步:填写Project name等工程信息,完成Flutter Plugin创建。

工程创建完成后的目录结构如下: + lib/flutter_plugin_demo.dart 是插件包dart API实现 + android/src/main/ 是插件包API Android的实现 + ios/Classes/ 是插件包API IOS的实现 + example 是基于依赖当前插件的纯flutter示例工程,一般用作展示API调用。

注:从 Flutter 1.9 开始,iOS 新项目默认使用 Swift 语言,而非 Objective-C;Android 新项目则默认使用 Kotlin,而非 Java。如有需要,是可随时切换回之前的 Objective-C 或 Java。

具体实现类

使用Android/ios模拟器运行example工程

我们先看看示例工程在Android、iOS模拟器上的运行效果:

进入example工程目录,运行lib/main.dart。

APP在Android模拟器运行后,可以看到屏幕出现了"Running on: Android 11"。

APP在iOS模拟器运行后,可以看到屏幕出现了"Running on: iOS 15.2"。

接下来我们通过代码看看flutter是怎么显示出当前平台的系统版本。

示例工程 example/lib/main.dart

关键代码:

 

class _MyAppState extends State<MyApp> {
  String _platformVersion = 'Unknown';

  @override
  void initState() {
    super.initState();
    initPlatformState();
  }
  Future<void> initPlatformState() async {
    String platformVersion;

    try {
      platformVersion =
          await FlutterPluginDemo.platformVersion ?? 'Unknown platform version';
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }

    if (!mounted) return;

    setState(() {
      _platformVersion = platformVersion;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Text('Running on: $_platformVersion\n'),
        ),
      ),
    );
  }
}

示例工程中的lib/main.dart引入了我们刚刚创建Flutter Plugin中dart API实现flutter_plugin_demo.dart

随后使用异步的方式调用flutter_plugin_demo.dartFlutterPluginEg.platformVersion并把返回值赋值给platformVersion,随后通过setState方法把platformVersion的值赋值给当前状态组件的_platformVersion,触发UI重渲把_platformVersion的值"Android 11"显示出来。

Flutter Plugin中dart API实现 lib/flutter_plugin_eg.dart 

全量代码:

import 'dart:async';

import 'package:flutter/services.dart';

class FlutterPluginDemo {
  static const MethodChannel _channel = MethodChannel('flutter_plugin_demo');

  static Future<String?> get platformVersion async {
    final String? version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}
  • service.dart暴露与平台通讯的API,如:MethodChannelPlatform Channel的一种类型
  • _channel是FlutterPluginDemo类的属性,是一个实例化的MethodChannel,name为"flutter_plugin_demo"
  • platformVersion是FlutterPluginEg类的静态可计算属性,会异步返还一个String。
  • platformVersion中,调用_channelinvokeMethod方法,入参"getPlatformVersion"为调用平台约定的方法名。然后把invokeMethod的异步结果赋值给String version作为platformVersion的返回值。

Flutter Plugin中Android实现 

android/src/main/java/.../FlutterPluginDemoPlugin 全量代码:

package com.qctt.flutter_plugin_demo;

import androidx.annotation.NonNull;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;

/** FlutterPluginDemoPlugin */
public class FlutterPluginDemoPlugin implements FlutterPlugin, MethodCallHandler {
  /// The MethodChannel that will the communication between Flutter and native Android
  ///
  /// This local reference serves to register the plugin with the Flutter Engine and unregister it
  /// when the Flutter Engine is detached from the Activity
  private MethodChannel channel;

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_plugin_demo");
    channel.setMethodCallHandler(this);
  }

  @Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else {
      result.notImplemented();
    }
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
    channel.setMethodCallHandler(null);
  }
}
再次强调:一个Flutter应用是可以有多个 channel,并而每个 channel都可以有多个 method,所以需要重点了解平台的代码是通过怎么样去对接channel name 与method name。从上文FlutterPluginDemoPlugin源码可以看到:
  • 注册MethodChannel约定通道名"flutter_plugin_demo",并开始监听通道消息。
 public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), "flutter_plugin_demo");
    channel.setMethodCallHandler(this);
  }

 实现onMethodCall方法,判断方法名"getPlatformVersion",返回Android ${android.os.Build.VERSION.RELEASE}

@Override
  public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
    if (call.method.equals("getPlatformVersion")) {
      result.success("Android " + android.os.Build.VERSION.RELEASE);
    } else {
      result.notImplemented();
    }
  }

当channel name 和 method name 约定后,Flutter Plugin就可以在dart中方便调用平台的实现,并暴露API供Flutter项目使用。

Flutter Plugin中IOS实现

ios/Classes/FlutterPluginDemoPlugin.m

#import "FlutterPluginDemoPlugin.h"

@implementation FlutterPluginDemoPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
  FlutterMethodChannel* channel = [FlutterMethodChannel
      methodChannelWithName:@"flutter_plugin_demo"
            binaryMessenger:[registrar messenger]];
  FlutterPluginDemoPlugin* instance = [[FlutterPluginDemoPlugin alloc] init];
  [registrar addMethodCallDelegate:instance channel:channel];
}

- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
  if ([@"getPlatformVersion" isEqualToString:call.method]) {
    result([@"iOS " stringByAppendingString:[[UIDevice currentDevice] systemVersion]]);
  } else {
    result(FlutterMethodNotImplemented);
  }
}

@end

 可以看出,IOS与Android实现思路雷同:通过"flutter_plugin_demo"注册FlutterMethodChannel并开始监听。

但惊奇发现Flutter Plugin中OC默认生成的代码居然没FlutterMethodCall方法名判断,在OC断点看是能获取到"getPlatformVersion"这个方法名的。

源码demo:https://gitee.com/wywinstonwy/flutter_plugin_demo

猜你喜欢

转载自blog.csdn.net/wywinstonwy/article/details/123925721