flutter插件开发(一)--基础

353 阅读4分钟

flutter插件开发(一)--基础

  1. flutter插件功能:用于和Android或iOS原生进行交互,比如,获取当前电量,打开关闭蓝牙,以及视频播放器。
  2. 通信原理:这个网上是有一些资料的,而且感觉还是比较偏理论,建议大家自行学习。这里提供一个学习链接:www.jianshu.com/p/fafc6b3f3…

开发步骤

目前我用的版本如下:

Flutter 1.12.13+hotfix.9 • channel stable • https://github.com/flutter/flutter.git
Framework • revision f139b11009 (5 weeks ago) • 2020-03-30 13:57:30 -0700
Engine • revision af51afceb8
Tools • Dart 2.7.2

目前flutter还没有稳定下来,前后版本在兼容性上还存在很大的问题,配置起来有各种各样让人糟心的问题。另外,本文目前只实现Android端代码。原因有二:1.是因为我完全没有iOS开发基础,2.目前我还没有苹果设备,无法进行开发。

创建插件项目

方法一:命令行方式

flutter create -i objc -a java --template=plugin demo

-i, 表示指定iOS的语言, objc, swift
-a, 表示指定安卓的语言, java, kotlin

方法一:AS创建 个人感觉,IDE创建起来比较方便,也不太容易出问题,主要是比较无脑。

File -> New -> New Flutter Project ->flutter plugin

项目目录

其中

  • android以及ios文件夹是我们将要编写插件的native层的地方
  • lib文件夹是编写与插件dart代码的地方
  • example文件夹则是例子工程,编写的插件可以直接在这个项目中进行测试。

example可以当成一个独立的项目工程,打开其中的yaml文件会发现,这里直接依赖了外层plugin:

dev_dependencies:
  flutter_test:
    sdk: flutter

  base:   //直接依赖外层
    path: ../

一般我们见到的项目引入方式如下

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^0.1.2    //脱字号“^”开头的版本表示 和当前版本接口保持兼容 的最新版
  
  //或者这个样子
  flutter_boost:
    git:
      url: 'https://github.com/alibaba/flutter_boost.git'
      ref: '1.12.13'

运行项目

直接运行自动生成的示例代码,我们就可以获取到手机的版本信息,该信息就是从系统原生层获取的。

学习示例代码

示例代码是我们学习的一个好的途径。 首先打开example/lib/main.dart,这是我们运行的主页面。

  // 这里就是核心方法代码,异步获取手机的平台信息并展示
  Future<void> initPlatformState() async {
    String platformVersion;
    try {
      //就这一句话,调用我们自己写的plugin,获取信息后再展示出来。
      platformVersion = await Flutterplugindemo1.platformVersion;
    } on PlatformException {
      platformVersion = 'Failed to get platform version.';
    }
    if (!mounted) return;
    setState(() {
      _platformVersion = platformVersion;
    });
  }

ctrl键+点击platformVersion,我们就进入了我们要自己编写的plugin中的dart代码:

class Flutterplugindemo1 {
  //这里建立一个和底层(这里的底层都是指原生层,也就是java层)的通道
  //需要指定ID "flutterplugindemo1",这个ID需要和底层id一样,否则会找不到
  static const MethodChannel _channel = const MethodChannel('flutterplugindemo1');

  static Future<String> get platformVersion async {
    //通过上面创建的通道,去底层获取数据
    final String version = await _channel.invokeMethod('getPlatformVersion');
    return version;
  }
}

下面就进入原生层了:

在项目根目录,右键,打开Android模块。会自动打开一个新的AS,在这里面看Android代码比较方便。下面是plugin代码:

public class Flutterplugindemo1Plugin implements FlutterPlugin, MethodCallHandler {
  
  //这里就是建立通道的代码,需要一个通道ID,然后就可以和刚才的dart代码进行联系了。
  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    final MethodChannel channel = new MethodChannel(flutterPluginBinding.getFlutterEngine().getDartExecutor(), "flutterplugindemo1");
    channel.setMethodCallHandler(new Flutterplugindemo1Plugin());
  }

  //这个和上面的方法是等价的,适用于1.12之前版本。属于废弃方法。
  public static void registerWith(Registrar registrar) {
    final MethodChannel channel = new MethodChannel(registrar.messenger(), "flutterplugindemo1");
    channel.setMethodCallHandler(new Flutterplugindemo1Plugin());
  }

  
  @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) {
  }
}

注意,这里返回结果没有return的方式,是直接在resul.success("")的方式,点击进入,我们会发现result类是一个接口,采用接口回调的方式,我们会执行reply.reply("")方法

public void onMessage(ByteBuffer message, final BinaryReply reply) {
    final MethodCall call = codec.decodeMethodCall(message);
    try {
        handler.onMethodCall(call, new Result() {
            @Override
            public void success(Object result) {
                reply.reply(codec.encodeSuccessEnvelope(result));
            }

            @Override
            public void error(String errorCode, String errorMessage, Object errorDetails) {
                reply.reply(codec.encodeErrorEnvelope(errorCode, errorMessage, errorDetails));
            }

            @Override
            public void notImplemented() {
                reply.reply(null);
            }
        });
    } catch (RuntimeException e) {
        Log.e(TAG + name, "Failed to handle method call", e);
        reply.reply(codec.encodeErrorEnvelope("error", e.getMessage(), null));
    }
}

先跟一下codec.encodeSuccessEnvelope(result),这里先是转化为json字符串,然后encode成为二进制流。

//JSONMethodCodec.java
public ByteBuffer encodeSuccessEnvelope(Object result) {
    return JSONMessageCodec.INSTANCE
        .encodeMessage(new JSONArray().put(JSONUtil.wrap(result)));
}

//StringCodec
public ByteBuffer encodeMessage(String message) {
    if (message == null) {
        return null;
    }
    // TODO(mravn): Avoid the extra copy below.
    final byte[] bytes = message.getBytes(UTF8);
    final ByteBuffer buffer = ByteBuffer.allocateDirect(bytes.length);
    buffer.put(bytes);
    return buffer;
}

返回去跟reply代码,会发现这里是flutterJNI代码,由于个人能力有限,就不继续跟了,有兴趣的,可以自己深入学习。

//DartMessenger.java
@Override
public void send(  @NonNull String channel,  @Nullable ByteBuffer message,@Nullable BinaryMessenger.BinaryReply callback) {
if (message == null) {
  flutterJNI.dispatchEmptyPlatformMessage(channel, replyId);
} else {
  flutterJNI.dispatchPlatformMessage(channel, message, message.position(), replyId);
}
}