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阅读 158,117评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,963评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,897评论 0 240
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,805评论 0 203
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,208评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,535评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,797评论 2 311
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,493评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,215评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,477评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,988评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,325评论 2 252
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,971评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,807评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,544评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,455评论 2 266

推荐阅读更多精彩内容