Flutter Plugin外掛開發填坑指南

xuexiangjys發表於2020-02-16

前言

最近我在業餘時間開發了一個Flutter外掛用於Android應用內的版本更新:flutter_xupdate,發現在開發的過程中沒遇到什麼坑,但就是在釋出到flutter外掛平臺時碰到了很多問題,這裡我記錄一下,給後來的人一點建議.

Flutter Plugin外掛開發

1.建立Flutter Plugin外掛專案

這裡推薦使用Android Studio建立專案,根據提示一步一步來就行了,截圖如下:

在這裡插入圖片描述
生成的專案目錄主要包含以下內容:

  • “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舊的載入外掛的方式,通過反射進行載入.
  • onAttachedToEngineonDetachedFromEngineFlutterPlugin的介面方法,是flutter新的載入外掛的方式.
  • onAttachedToActivityonDetachedFromActivityActivityAware的介面方法,主要是用於獲取當前flutter頁面所處的Activity.
  • onMethodCallMethodCallHandler的介面方法,主要用於接收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

這裡我們用得最多的就是boolintStringMap這幾個型別了

3.外掛釋出

外掛釋出遇到的坑最多,需要額外注意.

完善文件

建議將以下文件新增到外掛專案中:

  • README.md:介紹包的檔案
  • CHANGELOG.md記錄每個版本中的更改
  • LICENSE 包含軟體包許可條款的檔案
  • 所有公共API的API文件

釋出外掛

執行下面的命令進行釋出:

flutter packages pub publish
複製程式碼

你以為就這樣就完事了?不不不,下面坑可多了!!

坑點三:許可權認證需要翻牆

由於我們要將外掛釋出到flutter外掛平臺,要知道這平臺可是google建的,需要釋出的話,就必須要登入google賬號進行認證.在我們輸入flutter packages pub publish命令之後,我們會收到一條認證連結,這就是需要我們登入google賬號.

要知道google可是被牆的,這裡我們需要翻牆軟體翻牆登入google賬號並進行認證.

坑點四:Flutter中文網搭建文件有毒

你以為登入完google賬號就完事了?想太多了!這裡有個大坑就是flutter中文網上的環境配置問題,如下圖所示:

在這裡插入圖片描述
這裡官方讓我們配置一下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.

複製程式碼

相關連結

公眾號

在這裡插入圖片描述

交流群

在這裡插入圖片描述

相關文章