前言
為什么會有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ù)棧。
混編方案
一般來說混編方案有以下兩種:
- 統(tǒng)一管理方案:將iOS工程和Android工程作為Flutter工程的子工程蘸朋,由Flutter統(tǒng)一管理通殃。
- 三端分離方案: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的依賴主要分為兩部分:
- Flutter庫和引擎,也就是Flutter的framework庫和引擎庫。
- Flutter工程驾茴,也就是我們自己實(shí)現(xiàn)的Flutter模塊功能击碗,主要包含F(xiàn)lutter模塊lib目錄下的Dart代碼和各種資源晰房。
① 構(gòu)建
iOS集成Flutter模塊要稍微比Android麻煩一點(diǎn)。iOS項(xiàng)目工程對Flutter的依賴分別是:
- Flutter庫和引擎海蔽,即Flutter.framework幌衣;
- 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
這條命令執(zhí)行完后么介,會生成上面說的構(gòu)建產(chǎn)物:Flutter.framework
和App.framework
设拟。如果想要release的產(chǎn)物久脯,把--debug
換成--release
即可跑慕。
② 依賴使用
如果想和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)目地址
然后進(jìn)入到Flutter模塊根目錄下薪韩,執(zhí)行:
flutter pub get
這是管理Flutter packages的命令,會將項(xiàng)目依賴的Flutter package拉取到本地供項(xiàng)目使用捌锭。
此時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
執(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 --debug
或flutter build ios --release
構(gòu)建編譯產(chǎn)物骗灶,待編譯完成后有兩種方式提供給原生工程使用:
- 直接拖入原生工程,做相應(yīng)工程配置后即可依賴使用秉馏。
- 將構(gòu)建產(chǎn)物使用CocoaPods制作成私有庫供原生項(xiàng)目依賴使用耙旦,這也是目前比較推崇的方式。
具體關(guān)系圖:
目前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è)備。
③ 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項(xiàng)目代碼修改后:
- 按
r
是熱加載,局部刷新膘怕,刷新所有改動的Flutter代碼文件想诅,此時就可以看到代碼改動后的結(jié)果; - 按
R
是熱重啟岛心,全部刷新来破,刷新所有的Flutter文件。如過Hot reload
刷新無效忘古,可以嘗試使用Hot restart
徘禁。 - 按
d
和q
都是終止連接,結(jié)束調(diào)試髓堪。
Hot reload
和Hot 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
。
此時控制臺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...
同樣在控制臺上邊也可以通過點(diǎn)擊Hot reload
和Hot 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.
這是因?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)用撼班。
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í)用教程查看。