flutter插件开发(一)--基础
- flutter插件功能:用于和Android或iOS原生进行交互,比如,获取当前电量,打开关闭蓝牙,以及视频播放器。
- 通信原理:这个网上是有一些资料的,而且感觉还是比较偏理论,建议大家自行学习。这里提供一个学习链接: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;
}
}
下面就进入原生层了:
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);
}
}