flutter 多實例實戰

tags: flutter flutter多實例

在混合開發中,我們使用fluter作為插件化開發,即起一個flutterviewcontroller,這就是一個插件,該插件與其他模塊并沒有任何交互,用的數據源是通過method channel主動從從宿主app取得的.

具體的需求是這樣的,在第二個tab中放入一個flutter做的的視頻頁面,另外第三個tab有兩個插件的入口,也是用flutter寫的

廣告一下
紙上得來終覺淺,實踐之后才真懂
建了一個flutter qq群,群號:217429001 有興趣的加入哦

第二個tabflutter
兩個插件
 [原生]  ---> [flutter]

痛點問題

拿到需求第一步就想到,存在幾個問題

  1. 如何同時打開多個插件,或者從一個插件打開另一個插件,即保持多個flutter vc并存
  2. 多個flutter啟動后如何保證內存

第一次嘗試 創建

于是只需要使用創建代碼不就完了嗎

   FlutterViewController* flutterViewController = [[FlutterViewController alloc] initWithEngine:self.engine nibName:nil bundle:nil];

然而事情并沒有那么簡單
首先在tab中插入的flutterviewcontroller.view直接拿出來顯示不出來,使用延遲加載也不管用

第二次嘗試 - 解決顯示問題

經過大神指點要先使用present然后dismiss才能顯示出來

  __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。

第三次嘗試 顯示錯誤

這里有個前提是1.0 ios flutter engine無法釋放,如果僅僅使用FlutterViewController.new的方式肯定是會有釋放的,但是官方提供了一種根據engine創建 fluttervc的方式,所以保留一份engine,或者說讓engine保留成一個單例狀態。

至此第三次嘗試失敗

但是從這一次的問題來看,flutter上面的界面并不是跟著fluttervc走的,而是跟著engine走的,fluttervc僅僅提供了一個手勢和其他事件入口,所以即使關閉了fluttervc或者delloc了,只要engine存在,圖形渲染就保留了上一次的界面,到此為止多實例的fluttervc從根本上就沒有存在的必要了。

第四次嘗試 單實例實現多vc樣式

我們知道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;

另外設置即使可以起作用,也無法實現多路由問題。

第五次嘗試 改造setInialRoute

既然官方存在bug,那就解決吧,首先看了一圈flutter engine setInialRoute的實現,最終在shell.cc里面,如果直接改動engine編譯有點麻煩,想到的解決方案是在main.dart里面去原生讀取宿主路由
然后渲染對應的頁面。

    MosNativeHelper.defaultRouteName().then((name){
      setState(() {
        if(name != null){
            widget.defaultRouteName = name;
        }
      });
    });

然而這是第一次讀取,后續怎么更新新的頁面呢,這時候需要宿主主動發通知給flutter了

第六次嘗試 宿主發消息給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 避免了這一問題,也達到了體驗和內存上的最佳效果。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,156評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,401評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,069評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,873評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,635評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,128評論 1 323
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,203評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,365評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,881評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,733評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,935評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,475評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,172評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,582評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,821評論 1 282
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,595評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,908評論 2 372

推薦閱讀更多精彩內容