前言

最近我在业余时间开发了一个Flutter插件用于Android应用内的版本更新:​​flutter_xupdate​​​,发现在开发的过程中没遇到什么坑,但就是在发布到​​flutter插件平台​​时碰到了很多问题,这里我记录一下,给后来的人一点建议.

Flutter Plugin插件开发

1.创建Flutter Plugin插件项目

这里推荐使用Android Studio创建项目,根据提示一步一步来就行了,截图如下:

Flutter Plugin插件开发填坑指南_android

生成的项目目录主要包含以下内容:


  • “android”目录是插件API在Android平台的实现。
  • “ios”目录是插件API在iOS平台的实现。
  • “example”目录是使用插件的一个示例项目。
  • “lib”目录的文件,主要是创建“MethodChannel”,然后接收并处理来自原生平台发来的消息

2.实现插件功能


这里我主要介绍一下Android端的API实现.


坑点一: Flutter插件加载存在两个版本

由于Flutter自动依赖插件的方式存在两个版本(Registrar和FlutterPluginBinding), 因此我们在实现Android的插件的时候,为了能提高兼容性,最好把这两种都实现一遍.所以,Android的插件需要实现​​FlutterPlugin​​​, ​​ActivityAware​​​, ​​MethodCallHandler​​​这三个接口, 以我的​​flutter_xupdate​​插件为例,实现如下:

public class FlutterXUpdatePlugin implements FlutterPlugin, ActivityAware, MethodCallHandler {
private static final String PLUGIN_NAME = "com.xuexiang/flutter_xupdate";

private MethodChannel mMethodChannel;
private Application mApplication;
private WeakReference<Activity> mActivity;

//此处是新的插件加载注册方式
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
mMethodChannel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), PLUGIN_NAME);
mApplication = (Application) flutterPluginBinding.getApplicationContext();
mMethodChannel.setMethodCallHandler(this);
}

@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
mMethodChannel.setMethodCallHandler(null);
mMethodChannel = null;
}

public FlutterXUpdatePlugin initPlugin(MethodChannel methodChannel, Registrar registrar) {
mMethodChannel = methodChannel;
mApplication = (Application) registrar.context().getApplicationContext();
mActivity = new WeakReference<>(registrar.activity());
return this;
}

@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull Result result) {
switch (call.method) {
case "getPlatformVersion":
result.success("Android " + android.os.Build.VERSION.RELEASE);
break;
case "initXUpdate":
initXUpdate(call, result);
break;
...
default:
result.notImplemented();
break;
}
}

@Override
public void onAttachedToActivity(ActivityPluginBinding binding) {
mActivity = new WeakReference<>(binding.getActivity());
}

@Override
public void onDetachedFromActivityForConfigChanges() {

}

@Override
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {

}

@Override
public void onDetachedFromActivity() {
mActivity = null;
}

//此处是旧的插件加载注册方式
public static void registerWith(Registrar registrar) {
final MethodChannel channel = new MethodChannel(registrar.messenger(), PLUGIN_NAME);
channel.setMethodCallHandler(new FlutterXUpdatePlugin().initPlugin(channel, registrar));
}
}

以上代码需要注意的是:


  • ​registerWith​​静态方法是flutter旧的加载插件的方式,通过反射进行加载.
  • ​onAttachedToEngine​​​和​​onDetachedFromEngine​​​是​​FlutterPlugin​​的接口方法,是flutter新的加载插件的方式.
  • ​onAttachedToActivity​​​和​​onDetachedFromActivity​​​是​​ActivityAware​​的接口方法,主要是用于获取当前flutter页面所处的Activity.
  • ​onMethodCall​​​是​​MethodCallHandler​​的接口方法,主要用于接收Flutter端对原生方法调用的实现.

坑点二:原生和flutter之间数据交互类型有限制

在进行插件的开发时,就必定会涉及到原生和flutter之间的数据交互.这里需要注意的是,就像我们在进行react-native和JNI的开发时,并不是什么类型的数据都是支持交互的.下面我给出原生和flutter之间可交互的数据类型:

Dart

Android

iOS

null

null

nil (NSNull when nested)

bool

java.lang.Boolean

NSNumber numberWithBool:

int

java.lang.Integer

NSNumber numberWithInt:

int, if 32 bits not enough

java.lang.Long

NSNumber numberWithLong:

double

java.lang.Double

NSNumber numberWithDouble:

String

java.lang.String

NSString

Uint8List

byte[]

FlutterStandardTypedData typedDataWithBytes:

Int32List

int[]

FlutterStandardTypedData typedDataWithInt32:

Int64List

long[]

FlutterStandardTypedData typedDataWithInt64:

Float64List

double[]

FlutterStandardTypedData typedDataWithFloat64:

List

java.util.ArrayList

NSArray

Map

java.util.HashMap

NSDictionary

这里我们用得最多的就是​​bool​​​、​​int​​​、​​String​​​、​​Map​​这几个类型了

3.插件发布


插件发布遇到的坑最多,需要额外注意.


完善文档

建议将以下文档添加到插件项目中:


  • ​README.md​​:介绍包的文件
  • ​CHANGELOG.md​​记录每个版本中的更改
  • ​LICENSE​​ 包含软件包许可条款的文件
  • 所有公共API的API文档

发布插件

运行下面的命令进行发布:

flutter packages pub publish

你以为就这样就完事了?不不不,下面坑可多了!!

坑点三:权限认证需要访问google账号

由于我们要将插件发布到​​flutter插件平台​​​,要知道这平台可是google建的,需要发布的话,就必须要登录google账号进行认证.在我们输入​​flutter packages pub publish​​命令之后,我们会收到一条认证链接,这就是需要我们登录google账号.

要知道google在国内是无法访问的,这里我们需要想办法(方法是什么你懂的)登录google账号并进行认证.

坑点四:Flutter中文网搭建文档有毒

你以为登录完google账号就完事了?想太多了!这里有个大坑就是flutter中文网上的环境配置问题,如下图所示:

Flutter Plugin插件开发填坑指南_android_02

这里官方让我们配置一下Flutter的临时镜像,一般人刚接触的时候都是按照官方文档一步一步来,相信这一步肯定也少不了.可就是这么不起眼的一步,就让我在认证一步一直卡着.在网上找了半天的解决方法都没有任何用.中间也有人说是因为配置了镜像的问题,不敢我怎么也不相信是这个问题导致的.

这里我们去掉镜像配置就可以通过认证了.

坑点五:科学上网工具对命令终端不起作用

本来以为好不容易认证通过了,这下总能上传成功吧,结果意外又出现了,我一直卡在​​Uploading...​​,怎么也上传不成功.

Uploading...
Failed to upload the package.

在网上百度了,说是科学上网工具对命令终端不起作用,需要给命令行设置代理.

export https_proxy=http://127.0.0.1:1087
export http_proxy=http://127.0.0.1:1087
set https_proxy=https://127.0.0.1:1087
set http_proxy=http://127.0.0.1:1087

因为我使用的是科学上网工具小飞机mac版,所以我的代理端口是​​1087​​.

可是直接这样设置也是无法上传成功的.需要我们借助​​privoxy​​工具完成终端的代理,操作如下:

  • 安装privoxy
brew install privoxy
  • 修改privoxy配置
vim /usr/local/etc/privoxy/config

加入这两个配置(注意第一行的端口号要根据你的科学上网的代理来看,我这里没有改,它默认的是 1087),另外注意不要忘了最后有一个空格和点号。

listen-address 0.0.0.0:1087
forward-socks5 / localhost:1080 .
  • 启动 privoxy
sudo /usr/local/sbin/privoxy /usr/local/etc/privoxy/config

启动之后我们查看一下是否启动了:

netstat -na | grep 1087

出现类似如下结果, 就证明启动成功了。

tcp4 0 0 127.0.0.1.1087 *.* LISTEN

这时候再重新执行一下:

export https_proxy=http://127.0.0.1:1087
export http_proxy=http://127.0.0.1:1087
set https_proxy=https://127.0.0.1:1087
set http_proxy=http://127.0.0.1:1087

最后再执行一下发布命令:

flutter packages pub publish

如果出现如下结果,就证明发布成功了!

Waiting for your authorization...

Authorization received, processing...

Successfully authorized.

Uploading...

Successfully uploaded package.

公众号

Flutter Plugin插件开发填坑指南_java_03