Flutter混編方案

前言

為什么會有Flutter混編方案纳寂?其實(shí)這是一個很現(xiàn)實(shí)的問題。比如我們想要新寫一個App忽媒,直接選用Flutter作為移動端開發(fā)的跨平臺方案是非常好的一個選擇晦雨。但是現(xiàn)實(shí)中是我們的App可能是已經(jīng)開發(fā)了很多年的一個巨型工程隘冲,完全放棄原有的代碼而使用Flutter重寫App是不現(xiàn)實(shí)的。

開發(fā)過程中奥邮,我們最想要的是原生代碼開發(fā)和Flutter共存:既不影響項(xiàng)目工程的原生開發(fā)罗珍,又能使用Flutter去統(tǒng)一iOS/Android技術(shù)棧。

混編方案

一般來說混編方案有以下兩種:

flutter_01.png
  1. 統(tǒng)一管理方案:將iOS工程和Android工程作為Flutter工程的子工程蘸朋,由Flutter統(tǒng)一管理通殃。
  2. 三端分離方案:iOS工程画舌、Android工程已慢、Flutter工程是三個單獨(dú)的項(xiàng)目工程佑惠,將Flutter工程的編譯產(chǎn)物作為iOS工程和Android工程的依賴模塊膜楷,原有工程的管理模式不變,對原生工程沒有侵入性赌厅,無需額外配置工作特愿。

1. 統(tǒng)一管理方案

統(tǒng)一管理方案是只有一個項(xiàng)目工程勾缭,這樣的好處是代碼集中俩由,可以很方便的進(jìn)行項(xiàng)目開發(fā)幻梯,每個開發(fā)同學(xué)都可以進(jìn)行iOS礼旅、Android和Flutter的開發(fā)。當(dāng)然缺點(diǎn)也非常明顯:

  • 對原有項(xiàng)目的侵入性太大痘系,項(xiàng)目對外部環(huán)境的依賴程度增加汰翠。
  • 每個人本地都要裝有自己端的開發(fā)環(huán)境(iOS/Android)和Flutter的開發(fā)環(huán)境复唤,并且Flutter SDK版本要保持一致烛卧。
  • 耦合度會越來越高佛纫。當(dāng)項(xiàng)目越來越復(fù)雜后,整個項(xiàng)目的代碼耦合度會越來越高总放,相關(guān)工具鏈耗時也會越來越長呈宇,導(dǎo)致開發(fā)效率降低。

2. 三端分離方案

三端分離方案是iOS局雄、Android和Flutter分別作為三個獨(dú)立項(xiàng)目存在甥啄,在遠(yuǎn)端各自有各自的代碼倉庫。這種方案需要單獨(dú)創(chuàng)建Flutter項(xiàng)目炬搭,然后通過iOS(CocoaPods)和安卓的依賴管理工具將Flutter項(xiàng)目build出來的framework蜈漓、資源包等放入Native工程以供使用。這種方式可以將iOS宫盔、Android和Flutter項(xiàng)目放在一個目錄下面作為一個項(xiàng)目來管理融虽,也可以不在同一目錄下,關(guān)鍵是設(shè)置Flutter模塊依賴時相對路徑一定要設(shè)置正確有额,如下:

some/path/
  demo_android/
  demo_ios/
  demo_flutter/

以iOS端為例倒源。

2.1 創(chuàng)建Flutter工程

要將Flutter集成到現(xiàn)有項(xiàng)目中,首先創(chuàng)建Flutter模塊胳螟,命令行運(yùn)行:

cd yourproject/path/
flutter create --template module my_flutter

執(zhí)行完后糖耸,在yourproject/path/my_flutter中生成了Flutter模塊項(xiàng)目。在該目錄中,你可以運(yùn)行一些flutter命令希坚,比如flutter run --debug个束、flutter build ios

注意:

path可以自己定桩警,但是一定要和后邊Podfile文件的路徑一致飞崖。

my_flutter目錄結(jié)構(gòu)如下:

.
├── .android                // Android部分工程文件
├── .gitignore              // 忽略項(xiàng)配置文件
├── .ios                    // iOS部分工程文件
│   └── Runner.xcworkspace  // iOS工程工作區(qū)
├── lib                     // 項(xiàng)目Dart源文件    
│   └── main.dart           // Flutter項(xiàng)目代碼入口文件固歪,類似iOS的main.m逢防,RN的index.js文件
├── pubspec.yaml            // 項(xiàng)目依賴配置文件判帮,類似于iOS的Podfile悦昵,RN的package.json文件
└── test                    // 項(xiàng)目測試文件
  • 在lib目錄下放置自己的Dart代碼
  • Flutter依賴項(xiàng)添加到my_flutter/pubspec.yaml,包括Flutter軟件包和插件枚赡。
  • .ios包含一個Xcode工作區(qū),自己的iOS代碼不要添加到這里卢肃,這里的更改不會顯示到已有的iOS項(xiàng)目中莫湘,并且可能會被Flutter覆蓋。
  • .ios/.android/目錄是自動生成的忙芒,不要對其進(jìn)行源碼控制。
  • 首次拉取到集成Flutter模塊的項(xiàng)目代碼后潮峦,構(gòu)建項(xiàng)目之前,要現(xiàn)在my_flutter目錄運(yùn)行flutter pub get以生成對應(yīng)的.ios/.android/目錄齿兔。

使用Android Studio運(yùn)行Flutter工程無誤后椭盏,就可以將Flutter推到遠(yuǎn)端倉庫糟红,為后邊的混編做好準(zhǔn)備工作。

2.2 將Flutter工程集成到已有應(yīng)用程序中

官方推薦使用CocoaPods依賴管理工具來安裝Flutter SDK事扭,這種方式要求當(dāng)前項(xiàng)目的每個開發(fā)人員本地都必須安裝Flutter SDK版本。

如果你的項(xiàng)目還沒有使用CocoaPods,可以參考CocoaPods官網(wǎng)或者CocoaPods入門來給項(xiàng)目添加CocoaPods依賴管理工具涵亏。

① Podfile文件

我們要通過CocoaPods管理Flutter SDK豹爹,需要再Podfile文件中增加以下內(nèi)容:

flutter_application_path = '../my_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

// 對于每個需要集成Flutter的Podfile target,添加如下內(nèi)容
install_all_flutter_pods(flutter_application_path)

這里需要注意的是艾君,如果你的Flutter模塊目錄結(jié)構(gòu)與官方文檔推薦的不一致权她,需要自己調(diào)整相對路徑,以保證安裝正確要门。Podfile詳情案例如下:

source 'https://github.com/CocoaPods/Specs.git'
platform :ios, '8.0'
inhibit_all_warnings!

# path修改為調(diào)整后的相對路徑
flutter_application_path = './Demo/Vendors/demo_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

target 'MyApp' do

install_all_flutter_pods(flutter_application_path)

end
② 運(yùn)行pod install

pod install主要做了以下事情:

  • 解析Generated.xcconfig文件炒瘟,獲取 Flutter工程配置信息疮装,文件在my_flutter/.ios/Flutter/目錄下,文件中包含了Flutter SDK路徑受啥、Flutter工程路徑藤肢、Flutter工程入口、編譯目錄等钞澳。
  • 將Flutter SDK中的Flutter.framework通過pod添加到Native工程。
  • 使用post_install這個pod hooks來關(guān)閉Native工程的bitcode,并將Generated.xcconfig文件加入Native工程。

現(xiàn)在雖然進(jìn)行了三端分離,但是項(xiàng)目之間是直接依賴的,仍然存在一些問題:

  • 對原有項(xiàng)目仍然有侵入性,需要在項(xiàng)目中配置Podfile文件和執(zhí)行flutter命令。
  • 每個人本地仍然需要自己端的開發(fā)環(huán)境(iOS/Android)和Flutter的開發(fā)環(huán)境,并且Flutter SDK版本要保持一致藐吮。
2.3 構(gòu)建Flutter模塊

三端分離方案的關(guān)鍵是抽離Flutter工程泥从,將Flutter項(xiàng)目的構(gòu)建產(chǎn)物按照某種規(guī)則提供給原生工程使用,比如Android使用aar昼弟,iOS使用CocoaPods变骡。

這種方案是將Flutter項(xiàng)目的構(gòu)建產(chǎn)物作為原生工程的子模塊台妆,原有工程不需要本地安裝Flutter開發(fā)環(huán)境萨咳,只需要關(guān)注原生開發(fā)即可俊扳。當(dāng)我們的Flutter項(xiàng)目有了新功能或改動后,將其構(gòu)建產(chǎn)物通過拖入或改造為依賴庫的方式提供給原生項(xiàng)目使用。下面我們來一步步實(shí)現(xiàn)三端分離方案。

原生工程對Flutter的依賴主要分為兩部分:

  1. Flutter庫和引擎,也就是Flutter的framework庫和引擎庫。
  2. Flutter工程驾茴,也就是我們自己實(shí)現(xiàn)的Flutter模塊功能击碗,主要包含F(xiàn)lutter模塊lib目錄下的Dart代碼和各種資源晰房。
① 構(gòu)建

iOS集成Flutter模塊要稍微比Android麻煩一點(diǎn)。iOS項(xiàng)目工程對Flutter的依賴分別是:

  1. Flutter庫和引擎海蔽,即Flutter.framework幌衣;
  2. Flutter項(xiàng)目產(chǎn)物断部,即App.framework。

iOS項(xiàng)目的Flutter模塊依賴右冻,實(shí)際上就是這兩個產(chǎn)物儡遮,可以直接拖入項(xiàng)目工程因惭,或者封裝成一個CocoaPods私有庫供原生項(xiàng)目引用。

如何構(gòu)建Flutter產(chǎn)物呢,F(xiàn)lutter項(xiàng)目根目錄下執(zhí)行build命令:

flutter build ios --debug
flutter_02.png

這條命令執(zhí)行完后么介,會生成上面說的構(gòu)建產(chǎn)物:Flutter.frameworkApp.framework设拟。如果想要release的產(chǎn)物久脯,把--debug換成--release即可跑慕。

flutter_03.png
② 依賴使用

如果想和Android一樣搞成依賴庫,需要單獨(dú)將構(gòu)建產(chǎn)物封裝成CocoaPods私有庫如筛,通過pod的方式給項(xiàng)目引入使用。如何構(gòu)建私有庫,這里就不介紹了,如果有興趣可以參考CocoaPods入門刚夺。

如果不想這么麻煩献丑,也可以直接將產(chǎn)物拖進(jìn)項(xiàng)目的某個目錄下,直接引入使用侠姑。

原生iOS項(xiàng)目中创橄,導(dǎo)入頭文件#import <Flutter/Flutter.h>,直接使用FlutterViewController創(chuàng)建視圖控制器即可莽红,代碼如下:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
    self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
    FlutterViewController *vc = [[FlutterViewController alloc]init];
    self.window.rootViewController = vc;
    [self.window makeKeyAndVisible];
    return YES;
}
2.4. iOS端集成方案
① 開發(fā)模式

開發(fā)模式最重要要求就是便于調(diào)試妥畏,這時可以采用上面1.2.2方案,仍然是三端分離船老,但是不使用Flutter構(gòu)建產(chǎn)物咖熟。

首先在iOS指定目錄下clone Flutter項(xiàng)目代碼。之前的iOS項(xiàng)目也集成過React Native模塊柳畔,為了便于統(tǒng)一管理,我們將Flutter模塊放在React Native模塊同一目錄下郭赐。切到指定目錄下執(zhí)行clone命令:

git clone flutter項(xiàng)目地址
flutter_04.png

然后進(jìn)入到Flutter模塊根目錄下薪韩,執(zhí)行:

flutter pub get

這是管理Flutter packages的命令,會將項(xiàng)目依賴的Flutter package拉取到本地供項(xiàng)目使用捌锭。

flutter_05.png

此時Flutter模塊的準(zhǔn)備工作已經(jīng)完成俘陷,下邊需要將Flutter模塊配置給iOS項(xiàng)目工程使用。Podfile文件中增加以下內(nèi)容:

source 'https://github.com/CocoaPods/Specs.git'

platform :ios, '8.0'
inhibit_all_warnings!

# flutter模塊路徑配置观谦,路徑為你縮放至目錄的相對路徑
flutter_application_path = './Demo/Vendors/demo_flutter'
load File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')

target 'Demo' do

# iOS依賴庫
pod 'AFNetworking', '3.2.1'

# flutter
install_all_flutter_pods(flutter_application_path)

end

修改完畢后拉盾,執(zhí)行:

pod update
flutter_06.png

執(zhí)行完畢后,Flutter.framework會通過Pod添加到iOS項(xiàng)目中豁状,還有一些工程配置也會在這個命令中得以完成捉偏。

此時配置完畢,iOS項(xiàng)目添加如2.3所示native代碼泻红,運(yùn)行項(xiàng)目夭禽,即可看到Flutter頁面。

② 發(fā)布模式

開發(fā)模式主要是為了調(diào)試谊路,所以開發(fā)同學(xué)本地必須有Flutter開發(fā)環(huán)境讹躯,但是當(dāng)開發(fā)測試完需要打Release包發(fā)布或者代碼提交的服務(wù)端使用打包服務(wù)機(jī)打包的時候,F(xiàn)lutter工程的代碼是什么我們已經(jīng)不關(guān)心了,只需要提供Flutter工程的編譯產(chǎn)物給原生工程依賴使用即可潮梯。

所以此時進(jìn)入到項(xiàng)目中的Flutter工程目錄下執(zhí)行flutter build ios --debugflutter build ios --release構(gòu)建編譯產(chǎn)物骗灶,待編譯完成后有兩種方式提供給原生工程使用:

  • 直接拖入原生工程,做相應(yīng)工程配置后即可依賴使用秉馏。
flutter_07.png
  • 將構(gòu)建產(chǎn)物使用CocoaPods制作成私有庫供原生項(xiàng)目依賴使用耙旦,這也是目前比較推崇的方式。

具體關(guān)系圖:

flutter_08.png

目前iOS端是直接將Flutter編譯產(chǎn)物直接拖入項(xiàng)目使用沃饶,后續(xù)會將Flutter編譯產(chǎn)物構(gòu)建成上圖中私有庫的形式母廷,使用Cocoapods做Flutter模塊的依賴管理。

3. Native與Flutter通信

就像Native與H5交互相仿糊肤,Native與Flutter通信也是通過一個中間通信工具對象(Platform Channel)來完成的琴昆,有三種類型:

  • MethodChannel,最常用的傳遞對象馆揉,現(xiàn)在項(xiàng)目中使用的通信方式也是基于MethodChannel完成的业舍。
  • BasicMessageChannel,用戶數(shù)據(jù)信息的傳遞升酣。
  • EventChannel舷暮,用于時間監(jiān)聽傳遞等場景。
3.1 Native模塊

下面我們來看下iOS端代碼實(shí)現(xiàn)噩茄,首先定義MethodChannel的name下面,初始化過程中會使用這些name創(chuàng)建通信對象。

static NSString *const kChannelFlutterToNative = @"com.demo.flutter/native";
static NSString *const kChannelNativeToFlutter = @"com.demo.flutter/flutter";
① Native模塊傳遞信息給Flutter模塊
// native to flutter
FlutterMethodChannel *flutterChannel = [FlutterMethodChannel methodChannelWithName:kChannelNativeToFlutter binaryMessenger:flutterVC.binaryMessenger];
NSString *serviceToken = @"token";
if (serviceToken.length > 0) {
    [flutterChannel invokeMethod:@"onActivetyResult" arguments:@{@"cookie" : serviceToken}
    ];
}
② Native模塊接收Flutter模塊傳遞信息
// flutter to native
FlutterMethodChannel *nativeChannel = [FlutterMethodChannel methodChannelWithName:kChannelFlutterToNative binaryMessenger:flutterVC.binaryMessenger];
[nativeChannel setMethodCallHandler:^(FlutterMethodCall * _Nonnull call, FlutterResult  _Nonnull result) {
    if ([call.method isEqualToString:@"openWebViewPage"]) { 
        //打開webView
        NSString *url = [call.arguments[@"message"] description];
        self.loadWebView(url);
    } else if ([call.method isEqualToString:@"handleTrackingCrash"]) { 
        //觸發(fā)native埋點(diǎn)
        [self trackingFlutterErrorWithData:call.arguments];
    }
}];
3.2 Flutter模塊

Flutter工程代碼實(shí)現(xiàn)绩聘,首先也要初始化MethodChannel對象沥割,初始化用的字符串要與上面iOS Native工程使用的保持一致。

static final flutterToNativeChannel = const MethodChannel('com.demo.flutter/native');
static final nativeToFlutterChannel = const MethodChannel('com.demo.flutter/flutter');
① Flutter模塊接收Native模塊傳遞信息
Future<dynamic> handle(MethodCall call) async {
  switch(call.method) {
    case 'onActivetyResult':
      onDataChange(call.arguments);
      break;
  }
}
ConstantsUtil.nativeToFlutterChannel.setMethodCallHandler(handle);
② Flutter傳遞信息給Native模塊
Widget renderBottomRow(i) {
  return GestureDetector(
    onTap: () => openWebView(ConstantsUtil.flutterToNativeChannel, listData[i]['url']),
    child: Container(
        ...
    ),
  );
}

Future<Null> openWebView(MethodChannel channel, String url) async {
  Map<String,String> param = {'message':url};
  await channel.invokeMethod('openWebViewPage', param);
}

4. 調(diào)試

作為軟件開發(fā)凿菩,調(diào)試過程必不可少机杜,那么在混編模式下有什么調(diào)試方案和技巧呢?

在2.4所提到的開發(fā)模式下衅谷,我們本地既有Native工程代碼椒拗,又有Flutter工程代碼纺棺,想要同時調(diào)試Native代碼和Flutter代碼兵琳,一般有兩種方案:

4.1 iOS和Flutter同時調(diào)試,不支持?jǐn)帱c(diǎn)方案

① Xcode打開iOS項(xiàng)目糊识,運(yùn)行項(xiàng)目并打開Flutter項(xiàng)目頁面肢执。

② 終端命令行輸入:flutter devices枉阵,打印出已連接到計(jì)算機(jī)的設(shè)備。

flutter_09.png

③ Android Studio打開嵌在iOS項(xiàng)目中的Flutter項(xiàng)目预茄,控制臺選擇Terminal選項(xiàng)卡兴溜,輸入:

flutter attach -d 894DADC8-A12B-47FC-B8A7-EE29F0D2B086

flutter attach的作用是將當(dāng)前Flutter項(xiàng)目連接到某個正在運(yùn)行的應(yīng)用程序上侦厚。回車后拙徽,控制臺會輸出:

Syncing files to device iPhone 11...                                    
 6,247ms (!) 

這表示連接成功刨沦,具體Android Studio項(xiàng)目詳情截圖如下:

flutter_10.png

Flutter項(xiàng)目代碼修改后:

  • r是熱加載,局部刷新膘怕,刷新所有改動的Flutter代碼文件想诅,此時就可以看到代碼改動后的結(jié)果;
  • R是熱重啟岛心,全部刷新来破,刷新所有的Flutter文件。如過Hot reload刷新無效忘古,可以嘗試使用Hot restart徘禁。
  • dq都是終止連接,結(jié)束調(diào)試髓堪。

Hot reloadHot restart區(qū)別:

  • Hot reload送朱,將所有代碼更改加載到VM中,并重新構(gòu)建Widget樹干旁,但是不會重新運(yùn)行main()initState()驶沼。
  • Hot restart,同樣將所有代碼更改加載到VM中争群,然后重新啟動Flutter應(yīng)用回怜,從而丟失應(yīng)用狀態(tài)。
4.2 iOS和Flutter同時調(diào)試换薄,支持?jǐn)帱c(diǎn)方案

① Android Studio打開嵌在iOS項(xiàng)目中的Flutter項(xiàng)目鹉戚,工具欄點(diǎn)擊Flutter Attach

flutter_11.png

此時控制臺Debug選項(xiàng)卡log輸出:

Waiting for a connection from Flutter on iPhone 11...

② Xcode打開iOS項(xiàng)目专控,運(yùn)行項(xiàng)目并打開Flutter項(xiàng)目頁面《舨停控制臺Debug選項(xiàng)卡log如下輸出代表連接完成伦腐,可以進(jìn)行斷點(diǎn)調(diào)試。

Debug service listening on ws://127.0.0.1:54615/cDjoWoEjEok=/ws
Syncing files to device iPhone 11...
flutter_12.png

同樣在控制臺上邊也可以通過點(diǎn)擊Hot reloadHot restart按鈕來實(shí)現(xiàn)代碼修改的更新操作失都。

5. 遇到的問題

5.1 找不到GeneratedPluginRegistrant文件

如果原生代碼中使用了GeneratedPluginRegistrant類柏蘑,還要從Flutter項(xiàng)目中將這個類文件拿出來和Flutter項(xiàng)目產(chǎn)物放在一塊提供給原生項(xiàng)目使用,不然會報錯找不到這個類粹庞。

5.2 啟動崩潰咳焚,Library not loaded: @rpath/Flutter.framework/Flutter

項(xiàng)目啟動崩潰,控制臺log日志如下:

dyld: Library not loaded: @rpath/Flutter.framework/Flutter
  Referenced from: /Users/zzz/Library/Developer/CoreSimulator/Devices/F5A071EC-2F1A-47E8-9C71-8E1269E01568/data/Containers/Bundle/Application/72BC9387-1FFB-467F-97FE-21767A5861B0/Demo.app/Demo
  Reason: image not found

根據(jù)提示Flutter.framework沒有被加載庞溜,我們點(diǎn)擊Xocde的TARGETS -> General革半,找到 Frameworks,Libraries,and Embedded Content選項(xiàng)的Flutter.framework碑定,將Embed值由Do Not Embed改為Embed Without Signing,重新運(yùn)行項(xiàng)目即可又官。

另外App.framework也需要將Embed值由Do Not Embed改為Embed Without Signing延刘,不然項(xiàng)目運(yùn)行后進(jìn)入flutter頁面是看到你寫的功能頁面的。

5.3 項(xiàng)目嵌入Flutter編譯產(chǎn)物后六敬,使用模擬器運(yùn)行項(xiàng)目報錯
Building for iOS Simulator, but the linked and embedded framework 'App.framework' was built for iOS.
flutter_13.png

這是因?yàn)閄code 11.4更改了框架的鏈接和嵌入方式碘赖,導(dǎo)致了在iOS設(shè)備和模擬器之間切換的問題。想要避免這個啟動錯誤最簡單的操作就是更改Workspace Settings外构。點(diǎn)擊Xocde菜單欄File --> Workspace Settings...普泡,在彈出對話框中將Build System值改為Legacy Build System即可。重新運(yùn)行項(xiàng)目审编,即可正常在模擬器啟動應(yīng)用撼班。

flutter_14.png
5.4 Android Studio運(yùn)行Flutter項(xiàng)目,提示Waiting for another flutter command to release the startup lock...

項(xiàng)目異常關(guān)閉或者使用任務(wù)管理器強(qiáng)制關(guān)閉后一般會出現(xiàn)這個問題割笙,原因是在Flutter編譯運(yùn)行過程中會創(chuàng)建一個文件鎖lockfile权烧,而異常關(guān)閉或者強(qiáng)制關(guān)閉或?qū)е逻@個鎖沒有釋放而一直存在,導(dǎo)致啟動過程中出現(xiàn)waiting問題伤溉。

解決方案也很簡單般码,找到這個文件刪除即可。

rm ./flutter/bin/cache/lockfile
5.5 應(yīng)用程序提交App Store時報錯
Unsupported Architecture. Your executable contains unsupported architecture '[x86_64, i386]

意思比較明白乱顾,打包的應(yīng)用程序包含了不被支持的模擬器架構(gòu)(x86_64和 i386)板祝。解決方案當(dāng)然就是刪掉這兩個模擬器架構(gòu)。

選擇Xcode的Targets --> Build Phases走净,點(diǎn)擊+號按鈕選擇New Run Script Phase券时,在輸入框中填入以下代碼:

APP_PATH="${TARGET_BUILD_DIR}/${WRAPPER_NAME}"

# This script loops through the frameworks embedded in the application and
# removes unused architectures.

find "$APP_PATH" -name '*.framework' -type d | while read -r FRAMEWORK
do
    FRAMEWORK_EXECUTABLE_NAME=$(defaults read "$FRAMEWORK/Info.plist" CFBundleExecutable)
    FRAMEWORK_EXECUTABLE_PATH="$FRAMEWORK/$FRAMEWORK_EXECUTABLE_NAME"
    echo "Executable is $FRAMEWORK_EXECUTABLE_PATH"

    EXTRACTED_ARCHS=()
    for ARCH in $ARCHS
    do
        echo "Extracting $ARCH from $FRAMEWORK_EXECUTABLE_NAME"
        lipo -extract "$ARCH" "$FRAMEWORK_EXECUTABLE_PATH" -o "$FRAMEWORK_EXECUTABLE_PATH-$ARCH"
        EXTRACTED_ARCHS+=("$FRAMEWORK_EXECUTABLE_PATH-$ARCH")
    done

    echo "Merging extracted architectures: ${ARCHS}"
    lipo -o "$FRAMEWORK_EXECUTABLE_PATH-merged" -create "${EXTRACTED_ARCHS[@]}"
    rm "${EXTRACTED_ARCHS[@]}"

    echo "Replacing original executable with thinned version"
    rm "$FRAMEWORK_EXECUTABLE_PATH"
    mv "$FRAMEWORK_EXECUTABLE_PATH-merged" "$FRAMEWORK_EXECUTABLE_PATH"
done

這段腳本將會在打包過程中執(zhí)行,刪除掉Archive包中不被支持的模擬器架構(gòu)(x86_64和 i386)伏伯。

5.6 配置Flutter環(huán)境變量后執(zhí)行Flutter命令仍然報zsh: command not found: flutter問題

配置Flutter環(huán)境變量后執(zhí)行Flutter命令正常橘洞,退出終端工具ZSH,再次打開終端工具ZSH執(zhí)行Flutter命令说搅,會提示zsh: command not found: flutter問題炸枣。

原因是如果你使用的是ZSH,終端啟動時 ~/.bash_profile 將不會被加載弄唧,解決辦法就是修改 ~/.zshrc 适肠,在其中添加:source ~/.bash_profile

6. 異常監(jiān)控

要想知道Flutter模塊在原生應(yīng)用中是否正常使用候引,異常監(jiān)控絕對少不了侯养。下面我們來看下Flutter異常如何收集和上報。

trackError方法是回調(diào)Native異常上報代碼澄干,在Native中上傳逛揩。目前iOS端使用的是埋點(diǎn)上傳的方式記錄當(dāng)前Flutter模塊的異常柠傍,后邊還會做進(jìn)一步優(yōu)化,上傳到統(tǒng)一異常收集平臺展示息尺。

Future<Null> trackError (MethodChannel channel, String exception, String stack) async {
  Map<String,String> param = {'exception' : exception,
                              'stack' : stack};
  await channel.invokeMethod('handleTrackingCrash', param);
}
6.1 Dart異常

對于Dart異常携兵,我們可以使用全局onError函數(shù)去捕獲:

runZoned(() {
  runApp(MyApp());
  if (Platform.isAndroid) {
    SystemChrome.setSystemUIOverlayStyle(SystemUiOverlayStyle.dark);
  }
}, onError: (error, stackTrace) {
  // This is a pure Dart error
  trackError(ConstantsUtil.flutterToNativeChannel, error.toString(), stackTrace.toString());
});

這里只要Dart代碼有Error就會觸發(fā)onError回調(diào)方法。

6.2 Flutter異常

除了Dart異常外搂誉,F(xiàn)lutter也能拋出其他異常徐紧,比如調(diào)用原生代碼發(fā)生的平臺異常,這種類型的異常也同樣是需要上報的炭懊。

為了捕獲 Flutter 異常并级,需要重寫FlutterError.onError屬性。在開發(fā)環(huán)境下侮腹,可以將異常格式化輸出到控制臺嘲碧。在生產(chǎn)環(huán)境下,可以把異常信息傳遞Native模塊做異常上報父阻。

FlutterError.onError = (FlutterErrorDetails errorDetails) {
  trackError(ConstantsUtil.flutterToNativeChannel, errorDetails.exception.toString(), errorDetails.stack.toString());
};

如果想要了對異常上報做進(jìn)一步了解愈涩,請點(diǎn)擊實(shí)用教程查看。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末加矛,一起剝皮案震驚了整個濱河市履婉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斟览,老刑警劉巖毁腿,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異苛茂,居然都是意外死亡已烤,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門妓羊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來胯究,“玉大人,你說我怎么就攤上這事躁绸√破” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵涨颜,是天一觀的道長。 經(jīng)常有香客問我茧球,道長庭瑰,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任抢埋,我火速辦了婚禮弹灭,結(jié)果婚禮上督暂,老公的妹妹穿的比我還像新娘。我一直安慰自己穷吮,他們只是感情好逻翁,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著捡鱼,像睡著了一般八回。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上驾诈,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天缠诅,我揣著相機(jī)與錄音,去河邊找鬼乍迄。 笑死管引,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的闯两。 我是一名探鬼主播褥伴,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼漾狼!你這毒婦竟也來了重慢?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤邦投,失蹤者是張志新(化名)和其女友劉穎伤锚,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體志衣,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡屯援,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了念脯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片狞洋。...
    茶點(diǎn)故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖绿店,靈堂內(nèi)的尸體忽然破棺而出吉懊,到底是詐尸還是另有隱情,我是刑警寧澤假勿,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布借嗽,位于F島的核電站,受9級特大地震影響转培,放射性物質(zhì)發(fā)生泄漏恶导。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一浸须、第九天 我趴在偏房一處隱蔽的房頂上張望惨寿。 院中可真熱鬧邦泄,春花似錦、人聲如沸裂垦。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蕉拢。三九已至特碳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間企量,已是汗流浹背测萎。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留届巩,地道東北人硅瞧。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像恕汇,于是被迫代替她去往敵國和親腕唧。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評論 2 353