[TOC]java
系列文章:android
(一)Flutter插件開發必備 原生SDK->Dart接口生成引擎Fluttify
介紹ios
(二)如何利用Fluttify開發一個新的Flutter插件git
(三)Fluttify輸出Flutter插件工程詳解github
注:目前Fluttify自己並不對外開放,可是內測階段能夠免費爲你生成插件,只要提供android端的jar/aar和ios端的framework/.h+.a,或者maven座標和cocoapods名稱便可,聯繫方法請看文末swift
Fluttify的輸出工程是標準的Flutter插件工程,其中輸出的原生語言是java(android)和objc(ios)。bash
android端使用java是由於從字節碼反編譯到java的時候,若是字節碼來自kotlin,那麼會有一些特殊的標記,致使一些狀況下(好比基礎類型和對應包裝類的混淆)須要多餘的工做去適配,爲了增強兼容性,因此後期選擇了java做爲生成的原生語言。async
ios端選擇objc也是相似的緣由,objc的方法轉爲swift的方法時,方法名會自動轉換,一些涉及到介詞的方法名都會被轉換爲swift風格,這也致使了一些額外的工做去轉換objc方法名到swift,因此最終選擇了objc做爲輸出語言。maven
引用自上一篇文章:函數
Fluttify
的產物是一個標準的Flutter的插件工程,因此lib
文件夾之上的結構都和普通插件同樣。lib
文件夾下會分紅android
和ios
文件夾,分別放置各平臺SDK中的類(枚舉/接口等)對應的Dart類(枚舉/接口等)。android
/ios
文件夾下還會各自生成:
function.g.dart
文件:生成的全部頂層函數;type_op.g.dart
文件:全部的as和is方法,用來判斷類型和造型;ios/android.export.g.dart
文件:導出全部的ios/android類型;platformview
文件夾:生成的全部PlatformView
;習慣上會在
lib
文件夾下再加一個dart
文件夾,放置對各平臺進行抽象的代碼,而且最後對外export
的時候,只export
這個文件夾下的文件。
lib
文件夾結構概覽: . ├── janalytics_fluttify.dart └── src ├── android │ ├── android.export.g.dart │ ├── cn ... android端對應的dart接口 │ └── type_op.g.dart ├── dart │ └── janalytics_service.dart └── ios ├── JANALYTICSBrowseEvent.g.dart ├── ...其餘生成文件 ├── functions.g.dart ├── ios.export.g.dart └── type_op.g.dart
原生端生成的文件分紅兩種。
第一種是PlatformViewFactory
類,負責PlatformView
的建立,Fluttify會掃描到SDK內全部的View類併爲其生成PlatformViewFactory
類。第二種是主Plugin類,負責全部的MethodChannel的調用處理。
示例的android端的文件夾結構,ios端相似:
.
└── me
└── yohom
└── amap_map_fluttify
├── AmapMapFluttifyPlugin.java // 主Plugin
├── DownloadProgressViewFactory.java // 如下都是PlatformViewFactory
├── MapViewFactory.java
├── TextureMapViewFactory.java
└── WearMapViewFactory.java
複製代碼
java中的類通常都會有做爲命名空間使用的包名,平時使用的時候都會先import
,再使用簡稱來引用。Fluttify實現初期,生成的dart類也是直接使用java類的簡稱,但這很容易就會出現類名衝突,因此最終決定使用全類名來生成java對應的dart類。其規則爲: java:
package com.test;
class A {}
複製代碼
轉換爲dart:
class com_test_A {}
複製代碼
在這點上objc就直接了不少,由於objc類自己就沒有命名空間,類名就是它的全名,因此objc這邊的類名不須要轉換直接用到dart類名上便可,規則爲:
@interface TestClassA
@end
複製代碼
轉換爲:
class TestClassA {}
複製代碼
所謂接口在java和objc的語境下都是表明能夠多重繼承的類型。雖然dart也有隱式接口,可是objc的接口(protocol
)能夠有實現且子類能夠不實現全部的方法,而dart一旦implements
了一個隱式接口,就必須實現全部的方法,因此dart的隱式接口不能做爲objc的protocol的等價角色。
萬幸的是dart支持mixin
,mixin
正好可以處理objc的protocol特性。
示例 java:
package com.test;
interface InterfaceA {}
class ClassA implements InterfaceA {}
複製代碼
轉換爲dart:
class com_test_ClassA extends java_lang_Object with com_test_Interface {}
複製代碼
objc:
@protocol TestInterfaceA
@end
@interface TestClassA
@end
複製代碼
轉換爲dart:
class TestClassA extends NSObject with TestInterfaceA {}
複製代碼
java,objc以及dart的方法在概念上基本一致,除了objc端的一些指針類型和值類型的區分,其餘的都差很少。這裏給一個例子闡述一下: java:
package com.test;
class TestClassA {
public String testMethod(int arg) { /* 方法內容 */ }
}
複製代碼
轉換爲dart:
class com_test_TestClassA {
String testMethod(int arg) { /* 調用原生代碼 */ }
}
複製代碼
objc:
@interface TestClassA
- (NSString*) testMethod: (NSInteger) arg;
@end
複製代碼
轉換爲dart:
class TestClassA {
String testMethod(int arg) { /* 調用原生代碼 */}
}
複製代碼
java沒有頂層函數,因此沒有須要處理的。
objc的函數實際上就是c函數,而dart也支持頂層函數,且與objc的函數語義上沒有太大的出入。
目前支持轉換java的類常量到dart的類常量。
回調分爲lambda和delegate,不過在Fluttify的生成代碼中的角色差很少。
回調的實現主要經過雙向的MethodChannel調用來實現,好比說java端有一個方法:
void setCallback(Callback callback) { /* 代碼 */ }
複製代碼
生成的dart代碼會是這樣的:
Future<void> setCallback(Callback callback) async {
await MethodChannel('some channel').invokeMethod('some method');
// 這裏會接收到native端的調用
MethodChannel('some channel callback').setMethodHandler((methodResult) {
// 處理原生的回調
callback.onXXX();
});
}
複製代碼
Dart端的全部SDK類都會間接繼承foundation_fluttify
中定義的Ref
類,這個類表明是一個引用類,內部含有一個refId
字段,保存的是原生端對應對象的id。
目前這個id的實現使用的是對象的hashCode
。android端全部的對象都會有hashCode()
方法,而ios端只有繼承NSObject
的類纔有hash
字段,若是碰到有處理結構體的須要,則用NSValue
包裝結構體後再調用其hash
字段。
當調用SDK類的方法時,會把refId
傳遞給原生,而後原生從全局HEAP
中獲取到目標對象,而後再在目標對象上進行調用。
dart端還提供了一個kNativeObjectPool
全局集合對象,這個集合對象保存了全部的原生對象的引用(即refId
),在須要釋放對象時,能夠對這個集合進行操做。
foundation_fluttify
的原生端提供了一個HEAP
全局集合,用來存放插件調用過程當中產生的原生對象。當dart端開始一個方法調用時,原生端便會先從HEAP
中獲取到目標對象,再調用對應方法。
若是須要把釋放一個對象須要把它從HEAP
中刪除,否則HEAP
會一直強引用對象致使一直佔用內存。從HEAP
中刪除後,後續的內存管理就交給系統來處理了。
本文對Fluttify輸出的插件工程的結構做了大體的介紹。這些其實也包含了不少我在實現Fluttify過程當中遇到的困難,包括java/objc/dart這些語言在語法上的統一,如何實現回調等等,還有不少不少細節的問題,更有甚者還要給SDK做者的一些騷操做騷寫法擦屁股。
最後仍是推薦一波,若是有想要生成插件的老鐵也能夠聯繫我(382146139@qq.com),目前Fluttify還處於內測階段,不會收取任何費用,有任何反饋均可以往fluttify-feedback提issue,歡迎各位的反饋。