Fluttify輸出Flutter插件工程詳解

[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

dart端結構

引用自上一篇文章函數

Fluttify的產物是一個標準的Flutter的插件工程,因此lib文件夾之上的結構都和普通插件同樣。lib文件夾下會分紅androidios文件夾,分別放置各平臺SDK中的類(枚舉/接口等)對應的Dart類(枚舉/接口等)。android/ios文件夾下還會各自生成:

  • function.g.dart文件:生成的全部頂層函數;
  • type_op.g.dart文件:全部的asis方法,用來判斷類型和造型;
  • 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支持mixinmixin正好可以處理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端

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,歡迎各位的反饋。

相關文章
相關標籤/搜索