-
原生引入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” />
/**
-
和介绍的创建FlutterFragment的三种方式是对应的
-
FlutterActivity显示的Flutter路由是在创建Intent对象时指定的,
-
优点就是使用起来更简单,缺点就是不够灵活,
-
无法像FlutterView/FlutterFragment那样只是作为原生页面中的一部分展示,
-
因此这种方式更适合整个页面都是由Flutter编写的场景。
*/
private void test(){
// 方式一、FlutterActivity显示的路由名称为"/",不可设置
/*startActivity(
FlutterActivity.createDefaultIntent(this)
);*/
// 方式二、FlutterActivity显示的路由名称可设置,每次都创建一个新的FlutterEngine对象
startActivity(
FlutterActivity
.withNewEngine()
.initialRoute(“yc”)
.build(this)
);
// 方式三、FlutterActivity显示的路由名称可设置,使用缓存好的FlutterEngine对象
/*startActivity(
FlutterActivity
.withCachedEngine(“my_engine_id”)
.build(this)
);*/
}
-
使用这种方式特点
-
这种方式不需要我们自己创建一个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();
}
@Override
protected void onPause() {
super.onPause();
flutterEngine.getLifecycleChannel().appIsInactive();
}
@Override
protected void onStop() {
super.onStop();
flutterEngine.getLifecycleChannel().appIsPaused();
}
-
可能和生命周期有关系
-
flutterEngine.getLifecycleChannel()获取到的是一个LifecycleChannel对象,类比于MethodChannel,作用大概就是将Flutter和原生端的生命周期相互联系起来。
-
这里分别在onResume()、onPause()和onStop()方法中调用了LifecycleChannel的appIsResumed()、appIsInactive()和appIsPaused()方法,作用就是同步Flutter端与原生端的生命周期。添加上述代码后,FlutterView就可以正常显示了。
-
为何在之后版本要添加
-
可能是FlutterVIew的渲染机制有了一些变化,在接收到原生端对应生命周期方法中发送的通知才会显示,具体原理还是要对比一下现在和以前的源码。
04.如何处理NA跳转flutter传参
4.1 NA如何传递参数给Flutter?
- 如果需要在页面跳转时传递参数呢,如何在Flutter代码中获取到原生代码中的参数呢?其实很简单,只需要在route后面拼接上参数就可以了。
NavigationChannel navigationChannel = flutterEngine.getNavigationChannel();
String route = “yc?{“name”:“杨充”}”;
navigationChannel.setInitialRoute(route);
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接收传递参数
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);
}
}
Map<String, dynamic> parseRouter(){
String url = window.defaultRouteName;
// route名称,路由path路径名称
String route = url.indexOf(‘?’) == -1 ? url : url.substring(0, url.indexOf(‘?’));
// 参数Json字符串
String paramsJson = url.indexOf(‘?’) == -1 ? ‘{}’ : url.substring(url.indexOf(‘?’) + 1);
// 解析参数
Map<String, dynamic> params = json.decode(paramsJson);
params[“route”] = route;
return params;
}
- 通过"?"将路由名称和参数分开,将参数对应的Json字符串解析为Map对象,需要导入dart:convert包。
05.思考遇到的几个问题分析
5.1 setInitialRoute生效问题
//第一种是生效的
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);
}
//第二种是不生效的
private void addFlutterView() {
flutterEngine = new FlutterEngine(this);
binaryMessenger = flutterEngine.getDartExecutor().getBinaryMessenger();
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);
// todo 放在这里不生效,思考为什么
flutterEngine.getNavigationChannel().setInitialRoute(“yc”);
flutterView.attachToFlutterEngine(flutterEngine);
// todo 放在这里不生效,思考为什么
// flutterEngine.getNavigationChannel().setInitialRoute(“yc”);
}
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);
}
//源码
@Nullable
public FlutterEngine getFlutterEngine() {
return delegate.getFlutterEngine();
}
-
错误原因是这里的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);
}
}
07.Android引入flutter本质
-
如何理解Android引入flutter页面
-
Android项目引入Flutter本质上是将Flutter编写的Widget嵌入到Activity中,类似于WebView,容器Activity相当于WebView,route相当于url,有两种方式FlutterView和FlutterFragment。页面间的跳转和传参可以借助MethodChannel来实现。
08.Flutter启动加载优化
8.1 分析flutter的启动页面流程
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);
}
this.flutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
//创建一个闪屏view - FlutterSplashView
this.flutterSplashView = new FlutterSplashView(this.host.getContext());
if (VERSION.SDK_INT >= 17) {
this.flutterSplashView.setId(View.generateViewId());
} else {
this.flutterSplashView.setId(486947586);
}
//显示闪屏页
this.flutterSplashView.displayFlutterViewWithSplash(this.flutterView, this.host.provideSplashScreen());
Log.v(“FlutterActivityAndFragmentDelegate”, “Attaching FlutterEngine to FlutterView.”);
//所创建surface 绑定到engine上
this.flutterView.attachToFlutterEngine(this.flutterEngine);
return this.flutterSplashView;
}
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
95%以上Android开发知识点,真正体系化!**
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-Ty54qekm-1711692483692)]
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
[外链图片转存中…(img-XmcERAJy-1711692483693)]
[外链图片转存中…(img-ecBkPVfR-1711692483693)]
[外链图片转存中…(img-2Z9QjscO-1711692483694)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。