前言
眾所周知,Xamarin應該是.net下的跨平台開發工具。2016年之前還處於收費狀態,后被微軟收購后開源。但似乎有個現象,開源后的Xamarin發展似乎有些停滯,而且維護Xamarin的團隊又很固執不願變通。社區多次建議UI層應該統一繪圖引擎,而不是映射原生控件。Xamarin.Forms給人的感覺就是性能差,動畫僵硬,效果不好實現。以至於谷歌的Flutter火爆之后,Xamarin社區很多人便轉移陣地,你在百度搜索Xamarin,第一個關鍵詞就是xamarin還有人用么。那么對於C#/.net開發者來說,唯一的問題就是界面,就有了開源項目Xamarin.Flutter,但過完年沒多久,項目就Archived了,adamped說僅靠他和幾個開發人員的業余時間很難搞定。但技術上確實可行,因為Skia在.net下本來就有綁定SkiaSharp,而Flutter底層便使用的Skia引擎。UIWidgets就是在Unity3D下的Flutter實現。那對於我們.net開發者有沒有一種可能就是界面使用Flutter,業務邏輯采用C#呢,答案是可行的,請看如下分解。
環境
為了能夠使用Flutter調用C#,需要如下工具和環境
- Flutter環境
- vs2015或者vs2017,並且包含Xamarin安裝。
- Android Studio, Android SDK,NDKr15c(https://dl.google.com/android/repository/android-ndk-r15c-windows-x86_64.zip)
- jdk 1.8
- Embeddinator-4000
前面四項不在贅述,請參考官方文檔,確保Flutter,Xamarin可以運行項目,Android ndk請使用r15c版本,目前好像僅支持r15c,其他未測試。
Embeddinator-4000是將C#轉化為各平台本機代碼的工具,可以使用Nuget安裝
Install-Package Embeddinator-4000 -Version 0.4.0
將工具路徑添加到系統環境PATH,建議添加nuget的全局緩存,類似C:\Users\Administrator\.nuget\packages\embeddinator-4000\0.4.0\tools
如果工具使用出錯,請檢查sdk,或ndk版本,並確認Xamarin設置sdk,ndk路徑正確。並將如下信息添加進注冊表
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\Software\Novell]
[HKEY_CURRENT_USER\Software\Novell\Mono for Android]
"AndroidNdkDirectory"="C:\\Program Files (x86)\\Android\\android-sdk\\ndk-bundle"
"AndroidSdkDirectory"="C:\\Program Files (x86)\\Android\\android-sdk"
"JavaSdkDirectory"="C:\\Program Files\\Android\\jdk\\microsoft_dist_openjdk_1.8.0.25\\"`
因為目前visualstudio的注冊表信息都是獨立文件形式,這可能是工具本身的BUG,參考https://github.com/mono/Embeddinator-4000/issues/707
使用方法
1. 創建C#類庫(這里只實現Android 下的方法,iOS類推)
這里我們先創建一個類庫,我們使用Android Class Lib,如果不是Android 的方法,也可以創建普通的類庫
編譯后得到Test4Flutter.dll, 運行如下命令
Embeddinator-4000.exe --gen=java -out=test .\Test4Flutter.dll -p=Android -c
出現MSBUILD : error MSB1008: 只能指定一個項目。請確保路徑下沒有空格,或者復制dll到其他路徑重試
在test目錄下我們得到一個Test4Flutter.aar文件
2.創建一個Flutter插件
Android 里添加Module,具體參考文檔,將Module添加進依賴。
public void onMethodCall(MethodCall call, Result result) {
if (call.method.equals("getPlatformVersion")) {
result.success("Android " + android.os.Build.VERSION.RELEASE);
}
else if (call.method.equals("getHelloString")){
result.success(test.getHelloString());
}
else if(call.method.equals("add")){
result.success(test.add((int)call.argument("x"),(int)call.argument("y")));
}
else {
result.notImplemented();
}
}
static Future<String> getHelloString() async {
return await _channel.invokeMethod('getHelloString');
}
static Future<int> add(int x,int y) async{
final int ret = await _channel.invokeMethod('add',<String, dynamic>{
'x': x,
'y': y,
});
return ret;
}
example內調用測試
調用方法時控制台會打印如下
I/mono-stdout( 5946): call net function ->GetHelloString
I/mono-stdout( 5946): call net function ->Add
I/mono-stdout( 5946): call net function ->Add
結語
很多時候我們應該摒棄語言之爭,語言並無好壞,甚至可以將其融合。