<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                在上一篇文章中,我從工程架構與工作模式兩個層面,與你介紹了設計 Flutter 混合框架需要關注的基本設計原則,即確定分工邊界。 在工程架構維度,由于 Flutter 模塊作為原生工程的一個業務依賴,其運行環境是由原生工程提供的,因此我們需要將它們各自抽象為對應技術棧的依賴管理方式,以分層依賴的方式確定二者的邊界。 而在工作模式維度,考慮到 Flutter 模塊開發是原生開發的上游,因此我們只需要從其構建產物的過程入手,抽象出開發過程中的關鍵節點和高頻節點,以命令行的形式進行統一管理。構建產物是 Flutter 模塊的輸出,同時也是原生工程的輸入,一旦產物完成構建,我們就可以接入原生開發的工作流了。 可以看到,在 Flutter 混合框架中,Flutter 模塊與原生工程是相互依存、互利共贏的關系: * Flutter 跨平臺開發效率高,渲染性能和多端體驗一致性好,因此在分工上主要專注于實現應用層的獨立業務(頁面)的渲染閉環; * 而原生開發穩定性高,精細化控制力強,底層基礎能力豐富,因此在分工上主要專注于提供整體應用架構,為 Flutter 模塊提供穩定的運行環境及對應的基礎能力支持。 那么,在原生工程中為 Flutter 模塊提供基礎能力支撐的過程中,面對跨技術棧的依賴管理,我們該遵循何種原則呢?對于 Flutter 模塊及其依賴的原生插件們,我們又該如何以標準的原生工程依賴形式進行組件封裝呢? 在今天的文章中,我就通過一個典型案例,與你講述這兩個問題的解決辦法。 ## 原生插件依賴管理原則 在前面[第 26](https://time.geekbang.org/column/article/127601)和[31 篇](https://time.geekbang.org/column/article/132818)文章里,我與你講述了為 Flutter 應用中的 Dart 代碼提供原生能力支持的兩種方式,即:在原生工程中的 Flutter 應用入口注冊原生代碼宿主回調的輕量級方案,以及使用插件工程進行獨立拆分封裝的工程化解耦方案。 無論使用哪種方式,Flutter 應用工程都為我們提供了一體化的標準解決方案,能夠在集成構建時自動管理原生代碼宿主及其相應的原生依賴,因此我們只需要在應用層使用 pubspec.yaml 文件去管理 Dart 的依賴。 但**對于混合工程而言,依賴關系的管理則會復雜一些**。這是因為,與 Flutter 應用工程有著對原生組件簡單清晰的單向依賴關系不同,混合工程對原生組件的依賴關系是多向的:Flutter 模塊工程會依賴原生組件,而原生工程的組件之間也會互相依賴。 如果繼續讓 Flutter 的工具鏈接管原生組件的依賴關系,那么整個工程就會陷入不穩定的狀態之中。因此,對于混合工程的原生依賴,Flutter 模塊并不做介入,完全交由原生工程進行統一管理。而 Flutter 模塊工程對原生工程的依賴,體現在依賴原生代碼宿主提供的底層基礎能力的原生插件上。 接下來,我就以網絡通信這一基礎能力為例,與你展開說明原生工程與 Flutter 模塊工程之間應該如何管理依賴關系。 ## 網絡插件依賴管理實踐 在第 24 篇文章“[HTTP 網絡編程與 JSON 解析](https://time.geekbang.org/column/article/121163)”中,我與你介紹了在 Flutter 中,我們可以通過 HttpClient、http 與 dio 這三種通信方式,實現與服務端的數據交換。 但在混合工程中,考慮到其他原生組件也需要使用網絡通信能力,所以通常是由原生工程來提供網絡通信功能的。因為這樣不僅可以在工程架構層面實現更合理的功能分治,還可以統一整個 App 內數據交換的行為。比如,在網絡引擎中為接口請求增加通用參數,或者是集中攔截錯誤等。 關于原生網絡通信功能,目前市面上有很多優秀的第三方開源 SDK,比如 iOS 的 AFNetworking 和 Alamofire、Android 的 OkHttp 和 Retrofit 等。考慮到 AFNetworking 和 OkHttp 在各自平臺的社區活躍度相對最高,因此我就以它倆為例,與你演示混合工程的原生插件管理方法。 ## 網絡插件接口封裝 要想搞清楚如何管理原生插件,我們需要先使用方法通道來建立 Dart 層與原生代碼宿主之間的聯系。 原生工程為 Flutter 模塊提供原生代碼能力,我們同樣需要使用 Flutter 插件工程來進行封裝。關于這部分內容,我在第[31](https://time.geekbang.org/column/article/132818)和[39](https://time.geekbang.org/column/article/141164)篇文章中,已經分別為你演示了推送插件和數據上報插件的封裝方法,你也可以再回過頭來復習下相關內容。所以,今天我就不再與你過多介紹通用的流程和固定的代碼聲明部分了,而是重點與你講述與接口相關的實現細節。 **首先,我們來看看 Dart 代碼部分。** 對于插件工程的 Dart 層代碼而言,由于它僅僅是原生工程的代碼宿主代理,所以這一層的接口設計比較簡單,只需要提供一個可以接收請求 URL 和參數,并返回接口響應數據的方法 doRequest 即可: ~~~ class FlutterPluginNetwork { ... static Future<String> doRequest(url,params) async { // 使用方法通道調用原生接口 doRequest,傳入 URL 和 param 兩個參數 final String result = await _channel.invokeMethod('doRequest', { "url": url, "param": params, }); return result; } } ~~~ Dart 層接口封裝搞定了,我們再來看看**接管真實網絡調用的 Android 和 iOS 代碼宿主如何響應 Dart 層的接口調用**。 我剛剛與你提到過,原生代碼宿主提供的基礎通信能力是基于 AFNetworking(iOS)和 OkHttp(Android)做的封裝,所以為了在原生代碼中使用它們,我們**首先**需要分別在 flutter\_plugin\_network.podspec 和 build.gradle 文件中將工程對它們的依賴顯式地聲明出來: 在 flutter\_plugin\_network.podspec 文件中,聲明工程對 AFNetworking 的依賴: ~~~ Pod::Spec.new do |s| ... s.dependency 'AFNetworking' end ~~~ 在 build.gradle 文件中,聲明工程對 OkHttp 的依賴: ~~~ dependencies { implementation "com.squareup.okhttp3:okhttp:4.2.0" } ~~~ **然后**,我們需要在原生接口 FlutterPluginNetworkPlugin 類中,完成例行的初始化插件實例、綁定方法通道工作。 最后,我們還需要在方法通道中取出對應的 URL 和 query 參數,為 doRequest 分別提供 AFNetworking 和 OkHttp 的實現版本。 對于 iOS 的調用而言,由于 AFNetworking 的網絡調用對象是 AFHTTPSessionManager 類,所以我們需要這個類進行實例化,并定義其接口返回的序列化方式(本例中為字符串)。然后剩下的工作就是用它去發起網絡請求,使用方法通道通知 Dart 層執行結果了: ~~~ @implementation FlutterPluginNetworkPlugin ... // 方法通道回調 - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { // 響應 doRequest 方法調用 if ([@"doRequest" isEqualToString:call.method]) { // 取出 query 參數和 URL NSDictionary *arguments = call.arguments[@"param"]; NSString *url = call.arguments[@"url"]; [self doRequest:url withParams:arguments andResult:result]; } else { // 其他方法未實現 result(FlutterMethodNotImplemented); } } // 處理網絡調用 - (void)doRequest:(NSString *)url withParams:(NSDictionary *)params andResult:(FlutterResult)result { // 初始化網絡調用實例 AFHTTPSessionManager *manager = [AFHTTPSessionManager manager]; // 定義數據序列化方式為字符串 manager.responseSerializer = [AFHTTPResponseSerializer serializer]; NSMutableDictionary *newParams = [params mutableCopy]; // 增加自定義參數 newParams[@"ppp"] = @"yyyy"; // 發起網絡調用 [manager GET:url parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) { // 取出響應數據,響應 Dart 調用 NSString *string = [[NSString alloc] initWithData:responseObject encoding:NSUTF8StringEncoding]; result(string); } failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) { // 通知 Dart 調用失敗 result([FlutterError errorWithCode:@"Error" message:error.localizedDescription details:nil]); }]; } @end ~~~ Android 的調用也類似,OkHttp 的網絡調用對象是 OkHttpClient 類,所以我們同樣需要這個類進行實例化。OkHttp 的默認序列化方式已經是字符串了,所以我們什么都不用做,只需要 URL 參數加工成 OkHttp 期望的格式,然后就是用它去發起網絡請求,使用方法通道通知 Dart 層執行結果了: ~~~ public class FlutterPluginNetworkPlugin implements MethodCallHandler { ... @Override // 方法通道回調 public void onMethodCall(MethodCall call, Result result) { // 響應 doRequest 方法調用 if (call.method.equals("doRequest")) { // 取出 query 參數和 URL HashMap param = call.argument("param"); String url = call.argument("url"); doRequest(url,param,result); } else { // 其他方法未實現 result.notImplemented(); } } // 處理網絡調用 void doRequest(String url, HashMap<String, String> param, final Result result) { // 初始化網絡調用實例 OkHttpClient client = new OkHttpClient(); // 加工 URL 及 query 參數 HttpUrl.Builder urlBuilder = HttpUrl.parse(url).newBuilder(); for (String key : param.keySet()) { String value = param.get(key); urlBuilder.addQueryParameter(key,value); } // 加入自定義通用參數 urlBuilder.addQueryParameter("ppp", "yyyy"); String requestUrl = urlBuilder.build().toString(); // 發起網絡調用 final Request request = new Request.Builder().url(requestUrl).build(); client.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, final IOException e) { // 切換至主線程,通知 Dart 調用失敗 registrar.activity().runOnUiThread(new Runnable() { @Override public void run() { result.error("Error", e.toString(), null); } }); } @Override public void onResponse(Call call, final Response response) throws IOException { // 取出響應數據 final String content = response.body().string(); // 切換至主線程,響應 Dart 調用 registrar.activity().runOnUiThread(new Runnable() { @Override public void run() { result.success(content); } }); } }); } } ~~~ 需要注意的是,**由于方法通道是非線程安全的,所以原生代碼與 Flutter 之間所有的接口調用必須發生在主線程。**而 OktHtp 在處理網絡請求時,由于涉及非主線程切換,所以需要調用 runOnUiThread 方法以確保回調過程是在 UI 線程中執行的,否則應用可能會出現奇怪的 Bug,甚至是 Crash。 有些同學可能會比較好奇,**為什么 doRequest 的 Android 實現需要手動切回 UI 線程,而 iOS 實現則不需要呢?**這其實是因為 doRequest 的 iOS 實現背后依賴的 AFNetworking,已經在數據回調接口時為我們主動切換了 UI 線程,所以我們自然不需要重復再做一次了。 在完成了原生接口封裝之后,Flutter 工程所需的網絡通信功能的接口實現,就全部搞定了。 ## Flutter 模塊工程依賴管理 通過上面這些步驟,我們以插件的形式提供了原生網絡功能的封裝。接下來,我們就需要在 Flutter 模塊工程中使用這個插件,并提供對應的構建產物封裝,提供給原生工程使用了。這部分內容主要包括以下 3 大部分: * 第一,如何使用 FlutterPluginNetworkPlugin 插件,也就是模塊工程功能如何實現; * 第二,模塊工程的 iOS 構建產物應該如何封裝,也就是原生 iOS 工程如何管理 Flutter 模塊工程的依賴; * 第三,模塊工程的 Android 構建產物應該如何封裝,也就是原生 Android 工程如何管理 Flutter 模塊工程的依賴。 接下來,我們具體看看每部分應該如何實現。 ## 模塊工程功能實現 為了使用 FlutterPluginNetworkPlugin 插件實現與服務端的數據交換能力,我們首先需要在 pubspec.yaml 文件中,將工程對它的依賴顯示地聲明出來: ~~~ flutter_plugin_network: git: url: https://github.com/cyndibaby905/44_flutter_plugin_network.git ~~~ 然后,我們還得在 main.dart 文件中為它提供一個觸發入口。在下面的代碼中,我們在界面上展示了一個 RaisedButton 按鈕,并在其點擊回調函數時,使用 FlutterPluginNetwork 插件發起了一次網絡接口調用,并把網絡返回的數據打印到了控制臺上: ~~~ RaisedButton( child: Text("doRequest"), // 點擊按鈕發起網絡請求,打印數據 onPressed:()=>FlutterPluginNetwork.doRequest("https://jsonplaceholder.typicode.com/posts", {'userId':'2'}).then((s)=>print('Result:$s')), ) ~~~ 運行這段代碼,點擊 doRequest 按鈕,觀察控制臺輸出,可以看到,接口返回的數據信息能夠被正常打印,證明 Flutter 模塊的功能表現是完全符合預期的。 :-: ![](https://img.kancloud.cn/68/55/6855481fc112697ff2cc03fdcc185883_2030x1662.png) 圖 1 Flutter 模塊工程運行示例 ## 構建產物應該如何封裝? 我們都知道,模塊工程的 Android 構建產物是 aar,iOS 構建產物是 Framework。而在第[28](https://time.geekbang.org/column/article/129754)和[42](https://time.geekbang.org/column/article/144156)篇文章中,我與你介紹了不帶插件依賴的模塊工程構建產物的兩種封裝方案,即手動封裝方案與自動化封裝方案。這兩種封裝方案,最終都會輸出同樣的組織形式(Android 是 aar,iOS 則是帶 podspec 的 Framework 封裝組件)。 如果你已經不熟悉這兩種封裝方式的具體操作步驟了,可以再復習下這兩篇文章的相關內容。接下來,我重點與你講述的問題是:**如果我們的模塊工程存在插件依賴,封裝過程是否有區別呢?** 答案是,對于模塊工程本身而言,這個過程沒有區別;但對于模塊工程的插件依賴來說,我們需要主動告訴原生工程,哪些依賴是需要它去管理的。 由于 Flutter 模塊工程把所有原生的依賴都交給了原生工程去管理,因此其構建產物并不會攜帶任何原生插件的封裝實現,所以我們需要遍歷模塊工程所使用的原生依賴組件們,為它們逐一生成插件代碼對應的原生組件封裝。 在第 18 篇文章“[依賴管理(二):第三方組件庫在 Flutter 中要如何管理?](https://time.geekbang.org/column/article/114180)”中,我與你介紹了 Flutter 工程管理第三方依賴的實現機制,其中.packages 文件存儲的是依賴的包名與系統緩存中的包文件路徑。 類似的,插件依賴也有一個類似的文件進行統一管理,即**.flutter-plugins**。我們可以通過這個文件,找到對應的插件名字(本例中即為 flutter\_plugin\_network)及緩存路徑: ~~~ flutter_plugin_network=/Users/hangchen/Documents/flutter/.pub-cache/git/44_flutter_plugin_network-9b4472aa46cf20c318b088573a30bc32c6961777/ ~~~ 插件緩存本身也可以被視為一個 Flutter 模塊工程,所以我們可以采用與模塊工程類似的辦法,為它生成對應的原生組件封裝。 對于 iOS 而言,這個過程相對簡單些,所以我們先來看看模塊工程的 iOS 構建產物封裝過程。 ### iOS 構建產物應該如何封裝? 在插件工程的 ios 目錄下,為我們提供了帶 podspec 文件的源碼組件,podspec 文件提供了組件的聲明(及其依賴),因此我們可以把這個目錄下的文件拷貝出來,連同 Flutter 模塊組件一起放到原生工程中的專用目錄,并寫到 Podfile 文件里。 原生工程會識別出組件本身及其依賴,并按照聲明的依賴關系依次遍歷,自動安裝: ~~~ #Podfile target 'iOSDemo' do pod 'Flutter', :path => 'Flutter' pod 'flutter_plugin_network', :path => 'flutter_plugin_network' end ~~~ 然后,我們就可以像使用不帶插件依賴的模塊工程一樣,把它引入到原生工程中,為其設置入口,在 FlutterViewController 中展示 Flutter 模塊的頁面了。 不過需要注意的是,由于 FlutterViewController 并不感知這個過程,因此不會主動初始化項目中的插件,所以我們還需要在入口處手動將工程里所有的插件依次聲明出來: ~~~ //AppDelegate.m: @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; // 初始化 Flutter 入口 FlutterViewController *vc = [[FlutterViewController alloc]init]; // 初始化插件 [FlutterPluginNetworkPlugin registerWithRegistrar:[vc registrarForPlugin:@"FlutterPluginNetworkPlugin"]]; // 設置路由標識符 [vc setInitialRoute:@"defaultRoute"]; self.window.rootViewController = vc; [self.window makeKeyAndVisible]; return YES; } ~~~ 在 Xcode 中運行這段代碼,點擊 doRequest 按鈕,可以看到,接口返回的數據信息能夠被正常打印,證明我們已經可以在原生 iOS 工程中順利的使用 Flutter 模塊了。 :-: ![](https://img.kancloud.cn/32/98/329866c452354bd0524fc3de798b4fc8_2124x1890.png) 圖 2 原生 iOS 工程運行示例 我們再來看看模塊工程的 Android 構建產物應該如何封裝。 ### Android 構建產物應該如何封裝? 與 iOS 的插件工程組件在 ios 目錄類似,Android 的插件工程組件在 android 目錄。對于 iOS 的插件工程,我們可以直接將源碼組件提供給原生工程,但對于 Andriod 的插件工程來說,我們只能將 aar 組件提供給原生工程,所以我們不僅需要像 iOS 操作步驟那樣進入插件的組件目錄,還需要借助構建命令,為插件工程生成 aar: ~~~ cd android ./gradlew flutter_plugin_network:assRel ~~~ 命令執行完成之后,aar 就生成好了。aar 位于 android/build/outputs/aar 目錄下,我們打開插件緩存對應的路徑,提取出對應的 aar(本例中為 flutter\_plugin\_network-debug.aar)就可以了。 我們把生成的插件 aar,連同 Flutter 模塊 aar 一起放到原生工程的 libs 目錄下,最后在 build.gradle 文件里將它顯式地聲明出來,就完成了插件工程的引入。 ~~~ //build.gradle dependencies { ... implementation(name: 'flutter-debug', ext: 'aar') implementation(name: 'flutter_plugin_network-debug', ext: 'aar') implementation "com.squareup.okhttp3:okhttp:4.2.0" ... } ~~~ 然后,我們就可以在原生工程中為其設置入口,在 FlutterView 中展示 Flutter 頁面,愉快地使用 Flutter 模塊帶來的高效開發和高性能渲染能力了: ~~~ //MainActivity.java public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); View FlutterView = Flutter.createView(this, getLifecycle(), "defaultRoute"); setContentView(FlutterView); } } ~~~ 不過**需要注意的是**,與 iOS 插件工程的 podspec 能夠攜帶組件依賴不同,Android 插件工程的封裝產物 aar 本身不攜帶任何配置信息。所以,如果插件工程本身存在原生依賴(像 flutter\_plugin\_network 依賴 OkHttp 這樣),我們是無法通過 aar 去告訴原生工程其所需的原生依賴的。 面對這種情況,我們需要在原生工程中的 build.gradle 文件里手動地將插件工程的依賴(即 OkHttp)顯示地聲明出來。 ~~~ //build.gradle dependencies { ... implementation(name: 'flutter-debug', ext: 'aar') implementation(name: 'flutter_plugin_network-debug', ext: 'aar') implementation "com.squareup.okhttp3:okhttp:4.2.0" ... } ~~~ **至此,將模塊工程及其插件依賴封裝成原生組件的全部工作就完成了,原生工程可以像使用一個普通的原生組件一樣,去使用 Flutter 模塊組件的功能了。** 在 Android Studio 中運行這段代碼,并點擊 doRequest 按鈕,可以看到,我們可以在原生 Android 工程中正常使用 Flutter 封裝的頁面組件了。 :-: ![](https://img.kancloud.cn/54/3a/543a78c6839639a28b2eb9246c0196f3_2060x1658.png) 圖 3 原生 Android 工程運行示例 當然,考慮到手動封裝模塊工程及其構建產物的過程,繁瑣且容易出錯,我們可以把這些步驟抽象成命令行腳本,并把它部署到 Travis 上。這樣在 Travis 檢測到代碼變更之后,就會自動將 Flutter 模塊的構建產物封裝成原生工程期望的組件格式了。 關于這部分內容,你可以參考我在[flutter\_module\_demo](https://github.com/cyndibaby905/44_flutter_module_demo)里的[generate\_aars.sh](https://github.com/cyndibaby905/44_flutter_module_demo/blob/master/generate_aars.sh)與[generate\_pods.sh](https://github.com/cyndibaby905/44_flutter_module_demo/blob/master/generate_pods.sh)實現。如果關于這部分內容有任何問題,都可以直接留言給我。 ## 總結 好了,關于 Flutter 混合開發框架的依賴管理部分我們就講到這里。接下來,我們一起總結下今天的主要內容吧。 Flutter 模塊工程的原生組件封裝形式是 aar(Android)和 Framework(Pod)。與純 Flutter 應用工程能夠自動管理插件的原生依賴不同,這部分工作在模塊工程中是完全交給原生工程去管理的。因此,我們需要查找記錄了插件名稱及緩存路徑映射關系的.flutter-plugins 文件,提取出每個插件所對應的原生組件封裝,集成到原生工程中。 從今天的分享可以看出,對于有著插件依賴的 Android 組件封裝來說,由于 aar 本身并不攜帶任何配置信息,因此其操作以手工為主:我們不僅要執行構建命令依次生成插件對應的 aar,還需要將插件自身的原生依賴拷貝至原生工程,其步驟相對 iOS 組件封裝來說要繁瑣一些。 為了解決這一問題,業界出現了一種名為[fat-aar](https://github.com/adwiv/android-fat-aar)的打包手段,它能夠將模塊工程本身,及其相關的插件依賴統一打包成一個大的 aar,從而省去了依賴遍歷和依賴聲明的過程,實現了更好的功能自治性。但這種解決方案存在一些較為明顯的不足: * 依賴沖突問題。如果原生工程與插件工程都引用了同樣的原生依賴組件(OkHttp),則原生工程的組件引用其依賴時會產生合并沖突,因此在發布時必須手動去掉原生工程的組件依賴。 * 嵌套依賴問題。fat-aar 只會處理 embedded 關鍵字指向的這層一級依賴,而不會處理再下一層的依賴。因此,對于依賴關系復雜的插件支持,我們仍需要手動處理依賴問題。 * Gradle 版本限制問題。fat-aar 方案對 Gradle 插件版本有限制,且實現方式并不是官方設計考慮的點,加之 Gradle API 變更較快,所以存在后續難以維護的問題。 * 其他未知問題。fat-aar 項目已經不再維護了,最近一次更新還是 2 年前,在實際項目中使用“年久失修”的項目存在較大的風險。 考慮到這些因素,fat-aar 并不是管理插件工程依賴的好的解決方案,所以**我們最好還是得老老實實地去遍歷插件依賴,以持續交付的方式自動化生成 aar。** 我把今天分享涉及知識點打包上傳到了 GitHub 中,你可以把[插件工程](https://github.com/cyndibaby905/44_flutter_plugin_network)、[Flutter 模塊工程](https://github.com/cyndibaby905/44_flutter_module_demo)、[原生 Android](https://github.com/cyndibaby905/44_AndroidDemo)和[iOS 工程](https://github.com/cyndibaby905/44_iOSDemo)下載下來,查看其 Travis 持續交付配置文件的構建執行命令,體會在混合框架中如何管理跨技術棧的組件依賴。 ## 思考題 最后,我給你留一道思考題吧。 原生插件的開發是一個需要 Dart 層代碼封裝,以及原生 Android、iOS 代碼層實現的長鏈路過程。如果需要支持的基礎能力較多,開發插件的過程就會變得繁瑣且容易出錯。我們都知道 Dart 是不支持反射的,但是原生代碼可以。我們是否可以利用原生的反射去實現插件定義的標準化呢? 提示:在 Dart 層調用不存在的接口(或未實現的接口),可以通過 noSuchMethod 方法進行統一處理。 ~~~ class FlutterPluginDemo { // 方法通道 static const MethodChannel _channel = const MethodChannel('flutter_plugin_demo'); // 當調用不存在接口時,Dart 會交由該方法進行統一處理 @override Future<dynamic> noSuchMethod(Invocation invocation) { // 從字符串 Symbol("methodName") 中取出方法名 String methodName = invocation.memberName.toString().substring(8, string.length - 2); // 參數 dynamic args = invocation.positionalArguments; print('methodName:$methodName'); print('args:$args'); return methodTemplate(methodName, args); } // 某未實現的方法 Future<dynamic> someMethodNotImplemented(); // 某未實現的帶參數方法 Future<dynamic> someMethodNotImplementedWithParameter(param); } ~~~
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看