前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >NA嵌入Flutter页面

NA嵌入Flutter页面

原创
作者头像
杨充
修改于 2021-08-17 02:32:17
修改于 2021-08-17 02:32:17
3.8K0
举报
文章被收录于专栏:lib库lib库
目录介绍
  • 01.Android承载flutter容器
  • 02.过时的NA跳转flutter方案
  • 03.升级版本NA跳转Flutter处理
  • 04.如何处理NA跳转flutter传参
  • 05.思考遇到的几个问题分析
  • 06.Flutter页面关闭时Crash
  • 07.Android引入flutter本质
  • 08.Flutter启动加载流程和优化

00.推荐

01.Android承载flutter容器

  • Android中如何承载flutter页面呢
    • 第一种情况:从Android中弄一个容器,打开一个新的页面,装载一个新的flutter页面。
    • 第二种情况:从Android中弄一个容器,在NA的页面中,装载一个flutter页面。【一个页面,有一部分是NA,有一部分是Flutter】
  • 如何将Flutter编写的页面嵌入到Activity中
    • 官方提供了两种方式:通过FlutterView和FlutterFragment。

02.过时的NA跳转flutter方案

2.1 使用FlutterView
  • NA添加FlutterView
    • 在NA创建一个Activity,在onCreate中创建FlutterView然后添加到布局中。
    • Flutter.createView()方法返回的是一个FlutterView,它继承自View,我们可以把它当做一个普通的View。
    • Flutter.createView()方法的第三个参数传入了"yc_route"字符串,表示路由名称,它确定了Flutter中要显示的Widget。private void addFlutterView() { // 通过FlutterView引入Flutter编写的页面 // Flutter.createView()方法返回的是一个FlutterView,它继承自View,我们可以把它当做一个普通的View // Flutter.createView()方法的第三个参数传入了"yc_route"字符串,表示路由名称,它确定了Flutter中要显示的Widget flutterView = Flutter.createView(this, getLifecycle(), INIT_ROUTE); FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams( FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT); //添加到布局中 frameLayout.addView(flutterView, layoutParams); //addContentView(flutterView, layout); }
  • Flutter添加页面
    • 在runApp()方法中通过window.defaultRouteName可以获取到在Flutter.createView()方法中传入的路由名称,即"yc_route",
    • 之后编写了一个_widgetForRoute()方法,根据传入的route字符串显示相应的Widget。import 'dart:ui'; import 'package:flutter/material.dart';
代码语言:txt
AI代码解释
复制
void main() => runApp(_widgetForRoute(window.defaultRouteName));
代码语言:txt
AI代码解释
复制
Widget _widgetForRoute(String route) {
代码语言:txt
AI代码解释
复制
  switch (route) {
代码语言:txt
AI代码解释
复制
    case 'yc_route':
代码语言:txt
AI代码解释
复制
      return  MyHomePage(title: '匹配到了,这个是flutter页面');
代码语言:txt
AI代码解释
复制
  }
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
  • 跳转flutter所在activity黑屏
    • debug包这种情况比较明显,但是release加载很快,可以在进入Flutter页面的时候提供一个加载loading
2.2 使用FlutterFragment
  • NA添加FlutterView
    • 在NA创建一个Activity,在onCreate中创建FlutterFragment然后添加到布局中。
    • Flutter.createFragment()方法传入的参数同样表示路由名称,用于确定Flutter要显示的Widget,返回一个FlutterFragment,该类继承自Fragment,将该Fragment添加到Activity中就可以了。private void addFlutterFragment(){ // 通过FlutterFragment引入Flutter编写的页面 FragmentTransaction tx = getSupportFragmentManager().beginTransaction(); // Flutter.createFragment()方法传入的参数同样表示路由名称,用于确定Flutter要显示的Widget // 返回一个FlutterFragment,该类继承自Fragment,将该Fragment添加到Activity中就可以了。 FlutterFragment flutterFragment = Flutter.createFragment(INIT_ROUTE); tx.replace(R.id.rl_flutter, flutterFragment); tx.commit(); }
  • Flutter添加页面,这个同上
2.3 需要注意的问题
  • Flutter版本升级兼容问题
    • 由于Flutter版本的更新,上面介绍的内容中存在一些API已经被废弃的情况。简单查了一下了解到这个错误是Flutter 1.12版本废弃了io.flutter.facade包导致的,Flutter.createView和Flutter.createFragment这两个api找不到,固现在已经不使用呢……
  • NA跳转flutter如何添加参数
    • NA,这个传递参数只需要在路由后面拼接参数即可。
    • Flutter,这个接收参数只需要解析参数即可。
    • 下面升级版本FlutterView的使用案例中会说到,可以接着往下看……

03.升级版本NA跳转Flutter处理

3.1 使用新版本FlutterView
  • 新版本简单说明
    • 通过FlutterView引入Flutter页面,以前我们是通过io.flutter.facade包中Flutter类的createView()方法创建出一个FlutterView,然后添加到Activity的布局中,但是由于io.flutter.facade包的废弃,该方法已经无法使用。
    • 官方的文档有说明目前不提供在View级别引入Flutter的便捷API,因此如果可能的话,我们应该避免使用FlutterView,但是通过FlutterView引入Flutter页面也是可行的。
    • 需要注意,这里的FlutterView位于io.flutter.embedding.android包中,和此前我们所创建的FlutterView(位于io.flutter.view包中)是不一样的。
  • NA添加FlutterView
    • 在NA创建一个Activity,在onCreate中创建FlutterView然后添加到布局中。
    • 调用FlutterView的attachToFlutterEngine()方法,这个方法的作用就是将Flutter编写的UI页面显示到FlutterView中,注意到这里传入了一个flutterEngine参数,它又是什么呢?flutterEngine的类型为FlutterEngine,字面意思就是Flutter引擎,它负责在Android端执行Dart代码,将Flutter编写的UI显示到FlutterView的容器中。private void addFlutterView() { flutterEngine = new FlutterEngine(this); binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger(); flutterEngine.getNavigationChannel().setInitialRoute("yc"); flutterEngine.getDartExecutor().executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ); // 通过FlutterView引入Flutter编写的页面 // 这里的FlutterView位于io.flutter.embedding.android包中 // 和此前我们所创建的FlutterView(位于io.flutter.view包中)是不一样的。 // 通过查看FlutterView的源码可以发现它继承自FrameLayout,因此像一个普通的View那样添加就可以了。 flutterView = new FlutterView(this); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); rlFlutter.addView(flutterView, lp);
代码语言:txt
AI代码解释
复制
    //flutterEngine.getNavigationChannel().setInitialRoute("yc");
代码语言:txt
AI代码解释
复制
    // 关键代码,将Flutter页面显示到FlutterView中
代码语言:txt
AI代码解释
复制
    // 这个方法的作用就是将Flutter编写的UI页面显示到FlutterView中
代码语言:txt
AI代码解释
复制
    // flutterEngine的类型为FlutterEngine,字面意思就是Flutter引擎
代码语言:txt
AI代码解释
复制
    // 它负责在Android端执行Dart代码,将Flutter编写的UI显示到FlutterView/FlutterActivity/FlutterFragment中。
代码语言:txt
AI代码解释
复制
    flutterView.attachToFlutterEngine(flutterEngine);
代码语言:txt
AI代码解释
复制
    // FlutterEngine加载的路由名称为"/",我们可以通过下面的代码指定初始路由名称
代码语言:txt
AI代码解释
复制
    // 传参的情况没有变化,直接在路由名称后面拼接参数就可以
代码语言:txt
AI代码解释
复制
    // todo 放在这里不生效,思考为什么
代码语言:txt
AI代码解释
复制
    // flutterEngine.getNavigationChannel().setInitialRoute("yc");
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
  • Flutter添加页面
    • 在runApp()方法中通过window.defaultRouteName可以获取到在Flutter.createView()方法中传入的路由名称,即"yc_route",
    • 之后编写了一个_widgetForRoute()方法,根据传入的route字符串显示相应的Widget。import 'dart:ui'; import 'package:flutter/material.dart';
代码语言:txt
AI代码解释
复制
void main() => runApp(_widgetForRoute(window.defaultRouteName));
代码语言:txt
AI代码解释
复制
Widget _widgetForRoute(String route) {
代码语言:txt
AI代码解释
复制
  switch (route) {
代码语言:txt
AI代码解释
复制
    case 'yc_route':
代码语言:txt
AI代码解释
复制
      return  MyHomePage(title: '匹配到了,这个是flutter页面');
代码语言:txt
AI代码解释
复制
  }
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
3.2 使用新版本FlutterFragment
  • NA有几种添加方式
    • FlutterFragment.createDefault()
      • 通过FlutterFragment.createDefault()创建出FlutterFragment,创建出的Fragment显示的路由名称为"/",如果我们需要指定其他路由名称就不能使用这个方法了。
    • FlutterFragment.withNewEngine()
      • 通过FlutterFragment.withNewEngine()获取到NewEngineFragmentBuilder对象,使用建造者模式构造出FlutterFragment对象,可以通过initialRoute()方法指定初始路由名称。
      • 使用的withNewEngine()方法从名称上也能看出每次都是创建一个新的FlutterEngine对象来显示Flutter UI,但是从官方文档中可以了解到每个FlutterEngine对象在显示出Flutter UI之前是需要一个warm-up(简单理解为预热)期的,这会导致屏幕呈现短暂的空白,解决方式就是预先创建并启动FlutterEngine,完成warm-up过程,然后将这个FlutterEngine缓存起来,之后使用这个FlutterEngine来显示出Flutter UI。
    • FlutterFragment.withCachedEngine
      • 执行的FlutterEngineCache.getInstance().put("my_engine_id", flutterEngine)就是将FlutterEngine缓存起来,这里传入的"my_engine_id"就相当于缓存名称。
      • 之后调用FlutterFragment.withCachedEngine("my_engine_id").build();获取缓存的FlutterFragment对象
  • NA添加FlutterFragment
    • 在NA创建一个Activity,在onCreate中创建FlutterFragment然后添加到布局中。
    • Flutter.createFragment()方法传入的参数同样表示路由名称,用于确定Flutter要显示的Widget,返回一个FlutterFragment,该类继承自Fragment,将该Fragment添加到Activity中就可以了。private void addFlutterView() { // 通过FlutterFragment引入Flutter编写的页面 // 通过FlutterFragment.createDefault()创建出FlutterFragment // 需要注意这里的FlutterFragment位于io.flutter.embedding.android包中 //FlutterFragment flutterFragment = FlutterFragment.createDefault();
代码语言:txt
AI代码解释
复制
    // 通过FlutterFragment.withNewEngine()获取到NewEngineFragmentBuilder对象
代码语言:txt
AI代码解释
复制
    FlutterFragment.NewEngineFragmentBuilder fragmentBuilder = FlutterFragment.withNewEngine();
代码语言:txt
AI代码解释
复制
    // 使用建造者模式构造出FlutterFragment对象,可以通过initialRoute()方法指定初始路由名称。
代码语言:txt
AI代码解释
复制
    // 传递参数只需要在路由名称后面进行拼接。
代码语言:txt
AI代码解释
复制
    FlutterFragment.NewEngineFragmentBuilder initialRoute = fragmentBuilder.initialRoute("yc");
代码语言:txt
AI代码解释
复制
    FlutterFragment flutterFragment = initialRoute.build();
代码语言:txt
AI代码解释
复制
    getSupportFragmentManager()
代码语言:txt
AI代码解释
复制
            .beginTransaction()
代码语言:txt
AI代码解释
复制
            .add(R.id.rl_flutter, flutterFragment)
代码语言:txt
AI代码解释
复制
            .commit();
代码语言:txt
AI代码解释
复制
    // 存在的问题
代码语言:txt
AI代码解释
复制
    // 使用的withNewEngine()方法从名称上也能看出每次都是创建一个新的FlutterEngine对象来显示Flutter UI,
代码语言:txt
AI代码解释
复制
    // 但是从官方文档中我们可以了解到每个FlutterEngine对象在显示出Flutter UI之前
代码语言:txt
AI代码解释
复制
    // 是需要一个warm-up(不知道能不能翻译为预热)期的,这会导致屏幕呈现短暂的空白,
代码语言:txt
AI代码解释
复制
    // 解决方式就是预先创建并启动FlutterEngine,完成warm-up过程,然后将这个FlutterEngine缓存起来,
代码语言:txt
AI代码解释
复制
    // 之后使用这个FlutterEngine来显示出Flutter UI。
代码语言:txt
AI代码解释
复制
    // 解决方案看:FlutterFragmentCachedActivity
代码语言:txt
AI代码解释
复制
    // 如何获取到FlutterEngine对象呢?FlutterFragment中定义了一个getFlutterEngine()方法,
代码语言:txt
AI代码解释
复制
    // 从方法名来看大概就是获取FlutterEngine对象。
代码语言:txt
AI代码解释
复制
    // 尝试过创建MethodChannel时传入flutterFragment.getFlutterEngine().getDartExecutor(),
代码语言:txt
AI代码解释
复制
    // 运行后会直接抛出空指针异常,异常产生的位置在FlutterFragment的getFlutterEngine()方法中
代码语言:txt
AI代码解释
复制
    // 错误原因是这里的delegate为null,全局搜索一下,发现在FlutterFragment的onAttach()方法中会对delegate赋值,也就是说明此时没有执行onAttach()方法。
代码语言:txt
AI代码解释
复制
    // 猜测这就是由于上面提到过的FlutterEngine的warm-up机制,这是一个耗时过程,
代码语言:txt
AI代码解释
复制
    // 因此FlutterFragment并不会立刻执行onAttach()方法,导致我们在Activity的onCreate()方法中直接使用FlutterFragment的getFlutterEngine()方法会抛出异常。
代码语言:txt
AI代码解释
复制
    // todo 调用下面这句话会空指针崩溃
代码语言:txt
AI代码解释
复制
    // FlutterEngine flutterEngine = flutterFragment.getFlutterEngine();
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
  • Flutter添加页面
    • 这个同上
3.3 使用新版本FlutterActivity
  • 原生引入Flutter页面方式
    • 使用FlutterActivity,这里的FlutterActivity也是位于io.flutter.embedding.android包下的。
  • 首先在清单文件添加代码<activity android:name="io.flutter.embedding.android.FlutterActivity" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:hardwareAccelerated="true" android:theme="@style/AppTheme" android:windowSoftInputMode="adjustResize" />
  • 直接启动这个Activity,代码如下所示/** * 和介绍的创建FlutterFragment的三种方式是对应的 * * FlutterActivity显示的Flutter路由是在创建Intent对象时指定的, * 优点就是使用起来更简单,缺点就是不够灵活, * 无法像FlutterView/FlutterFragment那样只是作为原生页面中的一部分展示, * 因此这种方式更适合整个页面都是由Flutter编写的场景。 */ private void test(){ // 方式一、FlutterActivity显示的路由名称为"/",不可设置 /*startActivity( FlutterActivity.createDefaultIntent(this) );*/
代码语言:txt
AI代码解释
复制
    // 方式二、FlutterActivity显示的路由名称可设置,每次都创建一个新的FlutterEngine对象
代码语言:txt
AI代码解释
复制
    startActivity(
代码语言:txt
AI代码解释
复制
            FlutterActivity
代码语言:txt
AI代码解释
复制
                    .withNewEngine()
代码语言:txt
AI代码解释
复制
                    .initialRoute("yc")
代码语言:txt
AI代码解释
复制
                    .build(this)
代码语言:txt
AI代码解释
复制
    );
代码语言:txt
AI代码解释
复制
    // 方式三、FlutterActivity显示的路由名称可设置,使用缓存好的FlutterEngine对象
代码语言:txt
AI代码解释
复制
    /*startActivity(
代码语言:txt
AI代码解释
复制
            FlutterActivity
代码语言:txt
AI代码解释
复制
                    .withCachedEngine("my_engine_id")
代码语言:txt
AI代码解释
复制
                    .build(this)
代码语言:txt
AI代码解释
复制
    );*/
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
  • 使用这种方式特点
    • 这种方式不需要我们自己创建一个Activity,FlutterActivity显示的Flutter路由是在创建Intent对象时指定的,优点就是使用起来更简单,缺点就是不够灵活,无法像FlutterView/FlutterFragment那样只是作为原生页面中的一部分展示,因此这种方式更适合整个页面都是由Flutter编写的场景。
3.4 补充说明问题
  • 将Flutter版本更新到了1.17,发现上述代码运行后FlutterView无法显示,这个是为什么呢?
    • 和官方提供的示例flutter_view进行了对比,才发现缺少了下面的代码:@Override protected void onResume() { super.onResume(); // flutterEngine.getLifecycleChannel()获取到的是一个LifecycleChannel对象,类比于MethodChannel, // 作用大概就是将Flutter和原生端的生命周期相互联系起来。 flutterEngine.getLifecycleChannel().appIsResumed(); }
代码语言:txt
AI代码解释
复制
@Override
代码语言:txt
AI代码解释
复制
protected void onPause() {
代码语言:txt
AI代码解释
复制
    super.onPause();
代码语言:txt
AI代码解释
复制
    flutterEngine.getLifecycleChannel().appIsInactive();
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
@Override
代码语言:txt
AI代码解释
复制
protected void onStop() {
代码语言:txt
AI代码解释
复制
    super.onStop();
代码语言:txt
AI代码解释
复制
    flutterEngine.getLifecycleChannel().appIsPaused();
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
  • 可能和生命周期有关系
    • flutterEngine.getLifecycleChannel()获取到的是一个LifecycleChannel对象,类比于MethodChannel,作用大概就是将Flutter和原生端的生命周期相互联系起来。
    • 这里分别在onResume()、onPause()和onStop()方法中调用了LifecycleChannel的appIsResumed()、appIsInactive()和appIsPaused()方法,作用就是同步Flutter端与原生端的生命周期。添加上述代码后,FlutterView就可以正常显示了。
  • 为何在之后版本要添加
    • 可能是FlutterVIew的渲染机制有了一些变化,在接收到原生端对应生命周期方法中发送的通知才会显示,具体原理还是要对比一下现在和以前的源码。

04.如何处理NA跳转flutter传参

4.1 NA如何传递参数给Flutter?
  • 如果需要在页面跳转时传递参数呢,如何在Flutter代码中获取到原生代码中的参数呢?其实很简单,只需要在route后面拼接上参数就可以了。
  • 以创建FlutterView的方式为例。NavigationChannel navigationChannel = flutterEngine.getNavigationChannel(); String route = "yc?{\"name\":\"杨充\"}"; navigationChannel.setInitialRoute(route);
  • 以创建FlutterFragment的方式为例FlutterFragment.NewEngineFragmentBuilder fragmentBuilder = FlutterFragment.withNewEngine(); // 使用建造者模式构造出FlutterFragment对象,可以通过initialRoute()方法指定初始路由名称。 // 传递参数只需要在路由名称后面进行拼接。 String route = "yc?{\"author\":\"杨充\"}"; FlutterFragment.NewEngineFragmentBuilder initialRoute = fragmentBuilder.initialRoute(route); FlutterFragment flutterFragment = initialRoute.build();
4.2 传递参数注意事项
  • 将路由名称和参数间用“?”隔开,就像浏览器中的url一样,参数使用了Json格式传递,原因就是方便Flutter端解析,而且对于一些复杂的数据,比如自定义对象,使用Json序列化也很好实现。
4.3 Flutter接收传递参数
  • 这时候Flutter端通过window.defaultRouteName获取到的就是路由名称+参数了,我们需要将路由名称和参数分开,这就只是单纯的字符串处理。 Widget _widgetForRoute() { //var route = window.defaultRouteName; Map<String, dynamic> router = parseRouter(); var route = router["route"]; switch (route) { case 'yc': return AboutMePage(title: '匹配到了,这个是flutter页面',params : router); } }
代码语言:txt
AI代码解释
复制
  Map<String, dynamic> parseRouter(){
代码语言:txt
AI代码解释
复制
    String url = window.defaultRouteName;
代码语言:txt
AI代码解释
复制
    // route名称,路由path路径名称
代码语言:txt
AI代码解释
复制
    String route = url.indexOf('?') == -1 ? url : url.substring(0, url.indexOf('?'));
代码语言:txt
AI代码解释
复制
    // 参数Json字符串
代码语言:txt
AI代码解释
复制
    String paramsJson = url.indexOf('?') == -1 ? '{}' : url.substring(url.indexOf('?') + 1);
代码语言:txt
AI代码解释
复制
    // 解析参数
代码语言:txt
AI代码解释
复制
    Map<String, dynamic> params = json.decode(paramsJson);
代码语言:txt
AI代码解释
复制
    params["route"] = route;
代码语言:txt
AI代码解释
复制
    return params;
代码语言:txt
AI代码解释
复制
  }
代码语言:txt
AI代码解释
复制
```
  • 通过"?"将路由名称和参数分开,将参数对应的Json字符串解析为Map对象,需要导入dart:convert包。

05.思考遇到的几个问题分析

5.1 setInitialRoute生效问题
  • flutterEngine.getNavigationChannel().setInitialRoute("yc")生效问题//第一种是生效的 private void addFlutterView() { flutterEngine = new FlutterEngine(this); binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger(); flutterEngine.getNavigationChannel().setInitialRoute("yc"); flutterEngine.getDartExecutor().executeDartEntrypoint( DartExecutor.DartEntrypoint.createDefault() ); flutterView = new FlutterView(this); FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); rlFlutter.addView(flutterView, lp); flutterView.attachToFlutterEngine(flutterEngine); }
代码语言:txt
AI代码解释
复制
//第二种是不生效的
代码语言:txt
AI代码解释
复制
private void addFlutterView() {
代码语言:txt
AI代码解释
复制
    flutterEngine = new FlutterEngine(this);
代码语言:txt
AI代码解释
复制
    binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger();
代码语言:txt
AI代码解释
复制
    flutterEngine.getDartExecutor().executeDartEntrypoint(
代码语言:txt
AI代码解释
复制
            DartExecutor.DartEntrypoint.createDefault()
代码语言:txt
AI代码解释
复制
    );
代码语言:txt
AI代码解释
复制
    flutterView = new FlutterView(this);
代码语言:txt
AI代码解释
复制
    FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
代码语言:txt
AI代码解释
复制
            ViewGroup.LayoutParams.MATCH_PARENT,
代码语言:txt
AI代码解释
复制
            ViewGroup.LayoutParams.MATCH_PARENT);
代码语言:txt
AI代码解释
复制
    rlFlutter.addView(flutterView, lp);
代码语言:txt
AI代码解释
复制
    // todo 放在这里不生效,思考为什么
代码语言:txt
AI代码解释
复制
    flutterEngine.getNavigationChannel().setInitialRoute("yc");
代码语言:txt
AI代码解释
复制
    flutterView.attachToFlutterEngine(flutterEngine);
代码语言:txt
AI代码解释
复制
    // todo 放在这里不生效,思考为什么
代码语言:txt
AI代码解释
复制
    // flutterEngine.getNavigationChannel().setInitialRoute("yc");
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
5.2 flutterFragment.getFlutterEngine()空指针
  • 使用场景分析private void createChannel() { // todo 调用下面这句话会空指针崩溃 FlutterEngine flutterEngine = flutterFragment.getFlutterEngine(); BinaryMessenger binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger(); nativeChannel = new MethodChannel(binaryMessenger, METHOD_CHANNEL, StandardMethodCodec.INSTANCE); }
代码语言:txt
AI代码解释
复制
//源码
代码语言:txt
AI代码解释
复制
@Nullable
代码语言:txt
AI代码解释
复制
public FlutterEngine getFlutterEngine() {
代码语言:txt
AI代码解释
复制
    return delegate.getFlutterEngine();
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
  • 错误原因是这里的delegate为null
    • 翻看了一下源码,发现在FlutterFragment的onAttach()方法中会对delegate赋值,也就是说明此时没有执行onAttach()方法。
  • 问题分析
    • FlutterEngine的warm-up机制,这是一个耗时过程,因此FlutterFragment并不会立刻执行onAttach()方法,导致我们在Activity的onCreate()方法中直接使用FlutterFragment的getFlutterEngine()方法会抛出异常。
  • 如何解决问题
    • 想要解决问题,那就要等到FlutterFragment执行完onAttach()方法在调用getFlutterEngine。那么怎么去监听这个方法执行完呢?

06.Flutter页面关闭时Crash

  • 报错日志如下所示 Caused by: java.lang.RuntimeException: Cannot execute operation because FlutterJNI is not attached to native. at io.flutter.embedding.engine.FlutterJNI.ensureAttachedToNative(FlutterJNI.java:259) at io.flutter.embedding.engine.FlutterJNI.onSurfaceDestroyed(FlutterJNI.java:369) at io.flutter.embedding.engine.renderer.FlutterRenderer.stopRenderingToSurface(FlutterRenderer.java:219) at io.flutter.embedding.android.FlutterTextureView.disconnectSurfaceFromRenderer(FlutterTextureView.java:223) at io.flutter.embedding.android.FlutterTextureView.access$400(FlutterTextureView.java:33) at io.flutter.embedding.android.FlutterTextureView$1.onSurfaceTextureDestroyed(FlutterTextureView.java:84) at android.view.TextureView.releaseSurfaceTexture(TextureView.java:261) at android.view.TextureView.onDetachedFromWindowInternal(TextureView.java:232) at android.view.View.dispatchDetachedFromWindow(View.java:22072) at android.view.ViewGroup.dispatchDetachedFromWindow(ViewGroup.java:4747) at android.view.ViewGroup.removeAllViewsInLayout(ViewGroup.java:6606) at android.view.ViewGroup.removeAllViews(ViewGroup.java:6552) at com.yc.fluttercontainer.FlutterEngineActivity.onDestroy(FlutterEngineActivity.java:292)
  • 报错的代码如下所示@Override protected void onDestroy() { super.onDestroy(); if (flutterEngine != null) { flutterEngine.destroy(); } mFlutterContainer.removeAllViews(); mFlutterView.removeAllViews(); if (mRenderSurface != null) { // 打断内存泄漏 ((FixFlutterTextureView) mRenderSurface).setSurfaceTextureListener(null); } }
  • https://blog.csdn.net/cxz200367/article/details/105998930

07.Android引入flutter本质

  • 如何理解Android引入flutter页面
    • Android项目引入Flutter本质上是将Flutter编写的Widget嵌入到Activity中,类似于WebView,容器Activity相当于WebView,route相当于url,有两种方式FlutterView和FlutterFragment。页面间的跳转和传参可以借助MethodChannel来实现。

08.Flutter启动加载优化

8.1 分析flutter的启动页面流程
  • 通过flutter引擎,整个flutter引擎的相关初始化工作在onCreate方法里开始的protected void onCreate(@Nullable Bundle savedInstanceState) { this.switchLaunchThemeForNormalTheme(); super.onCreate(savedInstanceState); this.lifecycle.handleLifecycleEvent(Event.ON_CREATE); this.delegate = new FlutterActivityAndFragmentDelegate(this); //创建绑定引擎等 delegate.onAttach(this); //用于插件、框架恢复状态 delegate.onActivityCreated(savedInstanceState); //设置窗口背景透明,隐藏 status bar configureWindowForTransparency(); //从这里分析,这里是咱们的入口 setContentView(createFlutterView()); this.configureStatusBarForFullscreenFlutterExperience(); }
  • 然后接着往下看,会调用到FlutterActivityAndFragmentDelegate类的onCreateView方法
    • FlutterActivityAndFragmentDelegate类,flutter的初始化、启动等操作都是委托给它的。
    • 大致了解到,创建了一个FlutterSurfaceView 它继承自surfaceView(我们的flutter页面也是渲染在这个surface上的)。之后我们用它初始化一个FlutterView,@NonNull View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.v("FlutterActivityAndFragmentDelegate", "Creating FlutterView."); this.ensureAlive(); if (this.host.getRenderMode() == RenderMode.surface) { //flutter 应用在surface上显示,所以会进入到这里 FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(this.host.getActivity(), this.host.getTransparencyMode() == TransparencyMode.transparent); this.host.onFlutterSurfaceViewCreated(flutterSurfaceView); //用flutterSurfaceView 初始化了一个 FlutterView this.flutterView = new FlutterView(this.host.getActivity(), flutterSurfaceView); } else { //否则,应用在TextureView上显示 FlutterTextureView flutterTextureView = new FlutterTextureView(this.host.getActivity()); this.host.onFlutterTextureViewCreated(flutterTextureView); //用flutterTextureView 初始化了一个 FlutterView this.flutterView = new FlutterView(this.host.getActivity(), flutterTextureView); }
代码语言:txt
AI代码解释
复制
    this.flutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
代码语言:txt
AI代码解释
复制
    //创建一个闪屏view - FlutterSplashView
代码语言:txt
AI代码解释
复制
    this.flutterSplashView = new FlutterSplashView(this.host.getContext());
代码语言:txt
AI代码解释
复制
    if (VERSION.SDK_INT >= 17) {
代码语言:txt
AI代码解释
复制
        this.flutterSplashView.setId(View.generateViewId());
代码语言:txt
AI代码解释
复制
    } else {
代码语言:txt
AI代码解释
复制
        this.flutterSplashView.setId(486947586);
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    //显示闪屏页
代码语言:txt
AI代码解释
复制
    this.flutterSplashView.displayFlutterViewWithSplash(this.flutterView, this.host.provideSplashScreen());
代码语言:txt
AI代码解释
复制
    Log.v("FlutterActivityAndFragmentDelegate", "Attaching FlutterEngine to FlutterView.");
代码语言:txt
AI代码解释
复制
    //所创建surface 绑定到engine上
代码语言:txt
AI代码解释
复制
    this.flutterView.attachToFlutterEngine(this.flutterEngine);
代码语言:txt
AI代码解释
复制
    return this.flutterSplashView;
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```
  • 随后我们再创建一个FlutterSplashView (继承FrameLayout)。重要看调用displayFlutterViewWithSplash()方法。
    • 看到这里可知,通过splashScreen(是个接口),具体看接口实现类,然后创建一个splashScreenView,最后添加到flutter的布局中public void displayFlutterViewWithSplash(@NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) { if (this.splashScreenView != null) { this.removeView(this.splashScreenView); } //省略大量代码 this.flutterView = flutterView; this.addView(flutterView); this.splashScreen = splashScreen; if (splashScreen != null) { if (this.isSplashScreenNeededNow()) { Log.v(TAG, "Showing splash screen UI."); this.splashScreenView = splashScreen.createSplashView(this.getContext(), this.splashScreenState); //添加 splashScreenView this.addView(this.splashScreenView); flutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener); } } }
    • 那么什么时候移除这个启动Splash布局呢?在创建FlutterSplashView时,添加了一个完成事件的监听,当flutter加载成功后才将它移除。public FlutterSplashView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.onTransitionComplete = new Runnable() { public void run() { FlutterSplashView.this.removeView(FlutterSplashView.this.splashScreenView); FlutterSplashView.this.previousCompletedSplashIsolate = FlutterSplashView.this.transitioningIsolateId; } }; this.setSaveEnabled(true); }
  • 得出结论
    • 可以发现在闪屏页的显示到引擎的启动及flutter 页面的显示会有一个很长的过程,而直到flutter 页面的显示,这个闪屏页才会被移除掉。
8.2 如何优化flutter启动屏
  • 第一种方案
    • Flutter由于引擎的创建和初始化需要一定时间,所以也提供了一个过渡方案(默认是白屏)。如下所示,你可以设置一下背景AndroidManifest.xml下的 <meta-data android:name="io.flutter.embedding.android.SplashScreenDrawable" android:resource="@drawable/launch_background"/>
  • 第二种方案@Nullable @Override public SplashScreen provideSplashScreen() { //创建自定义flutter启动屏view return new FlutterSplashView(); }
代码语言:txt
AI代码解释
复制
public class FlutterSplashView implements SplashScreen {
代码语言:txt
AI代码解释
复制
    @Nullable
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public View createSplashView(@NonNull Context context, @Nullable Bundle savedInstanceState) {
代码语言:txt
AI代码解释
复制
        View v = new View(context);
代码语言:txt
AI代码解释
复制
        v.setBackgroundColor(Color.WHITE);
代码语言:txt
AI代码解释
复制
        return v;
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
    @Override
代码语言:txt
AI代码解释
复制
    public void transitionToFlutter(@NonNull Runnable onTransitionComplete) {
代码语言:txt
AI代码解释
复制
        onTransitionComplete.run();
代码语言:txt
AI代码解释
复制
    }
代码语言:txt
AI代码解释
复制
}
代码语言:txt
AI代码解释
复制
```

fluter Utils 工具类库:https://github.com/yangchong211/YCFlutterUtils

flutter 混合项目代码案例:https://github.com/yangchong211/YCHybridFlutter

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
仿真软件哪个好?ABAQUS软件、CST软件介绍
在现代科技与工业领域,仿真软件已成为工程师和设计师的标配工作平台,其价值渗透于工程设计、工业制造、教育科研等关键领域。这类工具通过构建数字化模型模拟真实场景,助力用户实现设计优化、成本控制与教学创新—— 例如在航空航天领域,工程师可借助仿真软件预判飞行器气动性能;在汽车制造中,设计师能通过模拟碰撞测试优化车身结构,避免动辄百万的物理试验成本。
思茂信息
2025/06/18
650
仿真软件哪个好?ABAQUS软件、CST软件介绍
多物理场建模,实现高精度仿真计算 COMSOL Multiphysics 6.1
COMSOL Multiphysics 6.1是一款由COMSOL公司推出的多物理场建模和仿真计算软件,主要应用于机械、电气、热力学、化学等领域。该软件集成了多种高级的功能和工具,为用户提供了全方位的数字化设计解决方案。
用户10521990
2023/04/23
9360
多物理场建模,实现高精度仿真计算 COMSOL Multiphysics 6.1
物理场仿真研究 Comsol 软件安装包下载,Comsol 安装激活
在当今数字化时代,仿真分析越来越重要,尤其在科学研究、工程设计和教育教学领域。Comsol Multiphysics 软件是一款功能强大的多物理场仿真软件,它提供了丰富的物理建模和仿真工具,为用户提供了一种完整的科学研究和工作解决方案。本文将探讨 Comsol 软件的主要特色功能和使用方法,并提供一个具体的案例,演示如何使用 Comsol 软件进行仿真分析。
用户10436734
2023/04/20
4060
comsol快速入门教程
我自己参照官网的教程,写了个比较通俗易懂的入门教程,字多图多慎看(大三的时候写的)。
desperate633
2018/08/23
2.2K2
数字化仿真有新起点COMSOL Multiphysics6更强大实用性和可扩展性
COMSOL Multiphysics 6是一款由COMSOL公司开发的多物理场仿真软件,可用于模拟和优化工程、制造、科学研究和设计领域。该软件可以处理多种物理场,如热、电、磁、声、流体以及结构等物理场。COMSOL Multiphysics 6是一款高度灵活的仿真软件,使其可以适应广泛的领域和工程应用。
用户10480228
2023/04/07
5010
数字化仿真有新起点COMSOL Multiphysics6更强大实用性和可扩展性
Comsol Multiphysics (多物理模拟仿真软件)Comsol Multiphysics安装下载,绿色安装包
作为一款著名的物理建模和仿真软件,COMSOL Multiphysics全系列为工程师和科学家提供了全面的模拟和优化工具,支持多种物理现象、材料、能源和工程领域。下面将介绍COMSOL Multiphysics全系列的主要模块和功能。
网创青年阿金
2023/04/21
8700
Comsol Multiphysics (多物理模拟仿真软件)Comsol Multiphysics安装下载,绿色安装包
仿真工具 Abaqus 2022中文版下载安装教程+Abaqus全版本安装包
Abaqus 2022是世界上最受欢迎和广泛使用的有限元分析软件之一,它为数值分析和工程仿真带来前所未有的效率和精度。以下是Abaqus 2022的主要特点:
用户10521990
2023/04/25
7000
仿真工具 Abaqus 2022中文版下载安装教程+Abaqus全版本安装包
多物理场仿真软件COMSOL6.1最新激活版,COMSOL6.1下载安装激活
COMSOL是一款基于多物理场的仿真模拟软件,在全球各著名高校,COMSOL Multiphysic已经成为教授有限元方法以及多物理场耦合分析的标准工具,在全球500强企业中,COMSOL Multiphysic被视作提升核心竞争力,增强创新能力,加速研发的重要工具。COMSOL包含了结构力学模块、化学工程模块、热传递模块、CAD导入模块、地球科学模块、射频模块等。
用户10436734
2023/04/13
7650
高级数值仿真软件下载Multiphysics下载:COMSOL Multiphysics 6.1
COMSOL Multiphysics是一款多物理场仿真软件,它可以用于模拟和优化各种工程和科学应用,例如结构力学、电磁场、流体力学、化学反应、热传导等。下面是COMSOL Multiphysics 6.1的功能介绍和安装配置:
用户10519159
2023/04/25
6300
高级数值仿真软件下载Multiphysics下载:COMSOL Multiphysics 6.1
COMSOL Multiphysics 6.0高性能模拟计算软件,打造精准科学模型
COMSOL Multiphysics 6.0是一款用于多物理场建模和仿真计算的软件,该软件是由瑞典COMSOL公司开发,旨在帮助工程师、研究人员和科学家快速准确地解决复杂的科学和工程计算问题。COMSOL Multiphysics 6.0支持多种应用领域,包括机械力学、声学、电磁学、流体力学、化学反应等。
用户10313071
2023/04/19
8450
COMSOL Multiphysics 6.0高性能模拟计算软件,打造精准科学模型
comsol和ansys哪个好?数值仿真软件COMSOL中文版下载安装教程
首选COMSOL,界面化的建模,结构式的场分析流程,上手要远远优于ANSYS。comsol在学术界有广大的用户基础,而ANSYS在工业界的地位不可撼动。
用户10436734
2023/04/01
6K0
comsol和ansys哪个好?数值仿真软件COMSOL中文版下载安装教程
物理场仿真优化Comsol软件安装包下载,Comsol 6.1中文版下载安装
Comsol Multiphysics是一款由Comsol公司开发的专业有限元分析软件平台,旨在解决工程和科学领域中多物理场及其相互作用问题。其可广泛应用于电力、电子、机械、热力学等领域,能够进行多物理场的耦合仿真及优化。
用户10413399
2023/04/18
6010
Comsol 软件下载,计算机仿真软件Comsol 6.1激活版下载安装教程
Comsol Multiphysics是一款广泛应用于科学计算和工程领域的计算机仿真软件。该软件具有多物理场耦合、自适应网格划分等强大的功能,可支持二维和三维物理场模拟,并被广泛应用于电子、机械、化学、生物等领域的工程设计、优化和分析。由于Comsol软件具有较为复杂的模型设置和求解过程,而且是一款国际化软件,因此在中文市场的推广和应用仍然面临一些挑战。
用户10410624
2023/04/12
1.3K0
COMSOL 5.6:打造多物理场仿真新标杆+全版本安装包
COMSOL Multiphysics是一种先进的工程仿真软件,运用多物理场建模技术进行多学科领域的仿真和分析,可以对各种应用中的现象进行建模和求解。COMSOL使用基于有限元分析(FEA)的方法进行仿真计算,提供高度灵活的建模环境以及多种求解方式和后处理功能。COMSOL Multiphysics 5.6是该软件的最新版本,增加了许多新功能和改进,使其成为目前市场上最强大的工程仿真软件之一。
用户10480228
2023/04/16
5760
COMSOL 5.6:打造多物理场仿真新标杆+全版本安装包
有限元分析软件入门:哪款最易上手?
如果你是一位初学者,对于有限元分析(FEA)这个领域充满好奇,想要了解哪款软件易上手、功能强大,那么本文就是为你准备的。在这里,我们将引导你了解有限元分析的基本概念,并推荐几款适合初学者的优秀软件。
思茂信息
2025/06/13
1590
有限元分析软件入门:哪款最易上手?
comsol软件是干嘛的?多物理场仿真Comsol软件安装包下载
Comsol是一个基于有限元法的多物理场仿真软件。它是由Comsol公司开发的一种高级技术计算语言和交互式环境。在工程、物理、化学、生物和医学等领域中都有着广泛的应用。本文将探讨Comsol的独特竞争力和使用方法,并通过实例案例进行详细阐述。
用户10410624
2023/04/22
5360
多体动力学模拟仿真系统软件Adams,Adams软件中文版下载安装教程
ADAMS软件使用交互式图形环境和零件库、约束库、力库,创建完全参数化的机械系统几何模型,其求解器采用多刚体系统动力学理论中的拉格朗日方程方法,建立系统动力学方程,对虚拟机械系统进行静力学、运动学和动力学分析,输出位移、速度、加速度和反作用力曲线。ADAMS软件的仿真可用于预测机械系统的性能、运动范围、碰撞检测、峰值载荷以及计算有限元的输入载荷等。
用户10436734
2023/04/01
9560
多体动力学模拟仿真系统软件Adams,Adams软件中文版下载安装教程
HyperWorks 2022中文纯净绿色版
HyperWorks是一款功能强大的开放式架构仿真软件。Altair HyperWorks 2020中文版拥有先进的技术以及高性能、高效和创新的产品,为用户提供了设计、仿真和制造等服务。Altair HyperWorks软件支持电磁分析设计、材料建模制造、多物理场分析等功能,用户可以进行庞大且复杂的有限元模型创建操作。
用户10518048
2023/04/19
8440
HyperWorks 2022中文纯净绿色版
Comsol软件是干嘛的?Comsol多物理场仿真软件下载安装,功能介绍
在现代科学技术的发展中,模拟软件是不可或缺的一部分。理论与实践相结合,计算机仿真软件可以帮助我们更好地理解、预测、创新和优化各种物理现象和工程应用。本文将以Comsol Multiphysics® 为例,介绍其独特功能,并探讨其在实际应用中的价值。
优木软件
2023/05/06
8770
Python 科学计算与数据科学核心内容大纲
密码学人CipherHUB
2025/03/04
1653
Python 科学计算与数据科学核心内容大纲
推荐阅读
相关推荐
仿真软件哪个好?ABAQUS软件、CST软件介绍
更多 >
LV.9
这个人很懒,什么都没有留下~
目录
  • 目录介绍
  • 00.推荐
  • 01.Android承载flutter容器
  • 02.过时的NA跳转flutter方案
    • 2.1 使用FlutterView
    • 2.2 使用FlutterFragment
    • 2.3 需要注意的问题
  • 03.升级版本NA跳转Flutter处理
    • 3.1 使用新版本FlutterView
    • 3.2 使用新版本FlutterFragment
    • 3.3 使用新版本FlutterActivity
    • 3.4 补充说明问题
  • 04.如何处理NA跳转flutter传参
    • 4.1 NA如何传递参数给Flutter?
    • 4.2 传递参数注意事项
    • 4.3 Flutter接收传递参数
  • 05.思考遇到的几个问题分析
    • 5.1 setInitialRoute生效问题
    • 5.2 flutterFragment.getFlutterEngine()空指针
  • 06.Flutter页面关闭时Crash
  • 07.Android引入flutter本质
  • 08.Flutter启动加载优化
    • 8.1 分析flutter的启动页面流程
    • 8.2 如何优化flutter启动屏
  • fluter Utils 工具类库:https://github.com/yangchong211/YCFlutterUtils
  • flutter 混合项目代码案例:https://github.com/yangchong211/YCHybridFlutter
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档