tags: flutter
flutter多實例
ios
在混合開發中,咱們使用fluter做爲插件化開發,即起一個flutterviewcontroller,這就是一個插件,該插件與其餘模塊並無任何交互,用的數據源是經過method channel主動從從宿主app取得的.git
具體的需求是這樣的,在第二個tab中放入一個flutter作的的視頻頁面,另外第三個tab有兩個插件的入口,也是用flutter寫的github
[原生] ---> [flutter]
複製代碼
拿到需求第一步就想到,存在幾個問題shell
因而只須要使用建立代碼不就完了嗎數據庫
FlutterViewController* flutterViewController = [[FlutterViewController alloc] initWithEngine:self.engine nibName:nil bundle:nil];
複製代碼
然而事情並無那麼簡單 首先在tab中插入的flutterviewcontroller.view直接拿出來顯示不出來,使用延遲加載也無論用bash
通過大神指點要先使用present而後dismiss才能顯示出來markdown
__weak __typeof(self)weakSelf = self;
self.ctr4.modalPresentationStyle = UIModalPresentationOverCurrentContext;
[self presentViewController:weakSelf.ctr4 animated:NO completion:^{
[weakSelf dismissViewControllerAnimated:NO completion:^{
[weakSelf addChildViewController:weakSelf.ctr4];
[weakSelf.view bringSubviewToFront:weakSelf.tabbarContainer];
}];
}];
複製代碼
接下來準備添加多個flutter 然而在push過程當中發現flutter的第一次顯示的界面居然是上次tab的頁面,由於engine是同一份的,咱們建立的時候會保存一份engine。app
這裏有個前提是1.0 ios flutter engine沒法釋放,若是僅僅使用FlutterViewController.new
的方式確定是會有釋放的,可是官方提供了一種根據engine建立 fluttervc的方式,因此保留一份engine,或者說讓engine保留成一個單例狀態。ide
至此第三次嘗試失敗oop
可是從這一次的問題來看,flutter上面的界面並非跟着fluttervc走的,而是跟着engine走的,fluttervc僅僅提供了一個手勢和其餘事件入口,因此即便關閉了fluttervc或者delloc了,只要engine存在,圖形渲染就保留了上一次的界面,到此爲止多實例的fluttervc從根本上就沒有存在的必要了。
咱們知道fluttervc有個初始routername的方法,在第一次啓動的時候能夠設置這個routername
- (void)setInitialRoute:(NSString*)route 複製代碼
因而想到經過這個來設置不一樣的路由。 卻不知,這個方法和initWithEngine
搭配使用時,並無起做用,傳入到main.dart裏面的window.defaultRouteName
一直是 /
根目錄符號
- (instancetype)initWithEngine:(FlutterEngine*)engine
nibName:(NSString*)nibNameOrNil
bundle:(NSBundle*)nibBundleOrNil NS_DESIGNATED_INITIALIZER;
複製代碼
另外設置即便能夠起做用,也沒法實現多路由問題。
既然官方存在bug,那就解決吧,首先看了一圈flutter engine setInialRoute的實現,最終在shell.cc裏面,若是直接改動engine編譯有點麻煩,想到的解決方案是在main.dart裏面去原生讀取宿主路由 而後渲染對應的頁面。
MosNativeHelper.defaultRouteName().then((name){ setState(() { if(name != null){ widget.defaultRouteName = name; } }); }); 複製代碼
然而這是第一次讀取,後續怎麼更新新的頁面呢,這時候須要宿主主動發通知給flutter了
flutter有個eventchannel就是用於接收宿主的事件回調的,使用方法是先註冊事件,發送一個參數給宿主,而後監聽event,最後釋放
// 註冊一個通知 static const EventChannel eventChannel = const EventChannel('com.moschat.app/native_post'); // 渲染前的操做,相似viewDidLoad @override void initState() { super.initState(); // 監聽事件,同時發送參數12345 eventChannel.receiveBroadcastStream(12345).listen(_onEvent,onError: _onError); print("[flutter]進入到initState"); widget.defaultRouteName = window.defaultRouteName; MosNativeHelper.defaultRouteName().then((name){ setState(() { if(name != null){ widget.defaultRouteName = name; } }); }); } 複製代碼
在宿主端的代碼
添加eventchannel代理方法
NSString *channelName = @"com.moschat.app/native_post"; FlutterEventChannel *evenChannal = [FlutterEventChannel eventChannelWithName:channelName binaryMessenger:flutterViewController]; // 代理 [evenChannal setStreamHandler:MOSFlutterEngine.sharedInstance]; 複製代碼
添加代理 FlutterStreamHandler
#pragma mark - <FlutterStreamHandler> // // 這個onListen是Flutter端開始監聽這個channel時的回調,第二個參數 EventSink是用來傳數據的載體。 - (FlutterError* _Nullable)onListenWithArguments:(id _Nullable)arguments eventSink:(FlutterEventSink)events { // arguments flutter給native的參數 // 回調給flutter, 建議使用實例指向,由於該block能夠使用屢次 if (events) { self.eventsBlock = [events copy]; self.eventsBlock (@"我是標題"); } return nil; } /// flutter再也不接收 - (FlutterError* _Nullable)onCancelWithArguments:(id _Nullable)arguments { // arguments flutter給native的參數 return nil; } 複製代碼
因爲eventblock能夠回調屢次,能夠達到宿主直接發消息給flutter的做用。 main.dart中接收到消息,則能夠直接在flutter裏面刷新根路由界面。
在切換插件過程當中仍是會有閃現前一個界面的問題,咱們在pop fluttervc的時機多flutter發送一個刷新一個黑色界面的指令,則在下次啓動時會閃過一個黑色頁面的過程,這個時機能夠看作是啓動的過程大概0.3s。 第二個點是在flutter poproute的過程當中,有業務須要在中間的route就退出整個vc,此時要注意一個點是,pop vc過程當中要主動一層層返回到flutter根部頁面,不然下一次看到的仍是上一次的那個頁面。
在多實例的實踐過程當中,發現ios的engine除了內存問題外,還有根路由設置不成功的問題,從業務方案上使用單engine 單flutterviewcontroller 避免了這一問題,也達到了體驗和內存上的最佳效果。
一行代碼教你解決FlutterPlatformViews內存泄露 by AShawn
手把手教你在Flutter項目優雅的使用ORM數據庫 by williamwen1986
flutter通用基礎庫flutter_luakit_plugin by williamwen1986
github - flutter_luakit_plugin使用例子 by williamwen1986
手把手教你解決 Flutter engine 內存泄漏 by 共田君
github - 編譯產物下載 修復內存泄漏後的flutter engine(可直接使用)by 共田君
github demo - 修復內存泄漏後的flutter engine by 共田君
持續更新中...