Flutter Android 端 FlutterView 相关流程源码分析
Posted 工匠若水
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter Android 端 FlutterView 相关流程源码分析相关的知识,希望对你有一定的参考价值。
Flutter 系列文章连载~
- 《Flutter Android 工程结构及应用层编译源码深入分析》
- 《Flutter 命令本质之 Flutter tools 机制源码深入分析》
- 《Flutter 的 runApp 与三棵树诞生流程源码分析》
- 《Flutter Android 端 Activity/Fragment 流程源码分析》
- 《Flutter Android 端 FlutterInjector 及依赖流程源码分析》
- 《Flutter Android 端 FlutterEngine Java 相关流程源码分析》
- 《Flutter Android 端 FlutterView 相关流程源码分析》
- 《Flutter 绘制动机 VSYNC 流程源码全方位分析》
- 《Flutter 安卓 Platform 与 Dart 端消息通信方式 Channel 源码解析》
背景
前面系列文章我们分析了 FlutterActivity 等相关流程,知道一个 Flutter android App 的本质是通过 FlutterView 进行渲染。当时由于篇幅限制,我们没有进入详细分析,这里作为一个专题进行简单分析。
SDK 中同属于 FlutterView 体系的控件大致有如图这些:
下文主要围绕上图进行分析。
FlutterSplashView 相关分析
FlutterSplashView 的主要作用是在 FlutterView render 渲染出来之前显示一个SplashScreen(本质 Drawable)过渡图(可以理解成类似开屏图片)。这个控件的调用在前面《Flutter Android 端 Activity/Fragment 流程源码分析》文章中分析 FlutterActivityAndFragmentDelegate 时有看到过,在其 onCreateView 方法中先实例化了 FlutterSplashView,接着调用flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen())
,然后把这个 FlutterSplashView 控件返回给 FlutterActivity 通过 setContentView 进行设置。下面是其相关流程主要源码:
final class FlutterSplashView extends FrameLayout
//......
//步骤1、把给定的splashScreen显示在flutterView之上,直到flutterView的首帧渲染出来才过渡消失。
public void displayFlutterViewWithSplash(
@NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen)
//步骤2、一堆重复调用的复位操作。
if (this.flutterView != null)
this.flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener);
removeView(this.flutterView);
if (splashScreenView != null)
removeView(splashScreenView);
//步骤3、把flutterView添加给当前FlutterSplashView,本质是一个FrameLayout。
this.flutterView = flutterView;
addView(flutterView);
this.splashScreen = splashScreen;
//步骤4、显示一个splash screen开屏图。
if (splashScreen != null)
//步骤5、如果flutterView未渲染出来则条件成立。
if (isSplashScreenNeededNow())
Log.v(TAG, "Showing splash screen UI.");
//步骤6、splashScreen是FlutterActivity中实现的DrawableSplashScreen。
//DrawableSplashScreen中的Drawable本质来自清单文件meta-data中io.flutter.embedding.android.SplashScreenDrawable配置。
//DrawableSplashScreen implements SplashScreen,所以就是DrawableSplashScreen的createSplashView方法。
//因此splashScreenView是DrawableSplashScreenView,继承自ImageView,设置的图为Drawable。
splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
//步骤7、把ImageView添加到FlutterSplashView中。
//由于FlutterSplashView是FrameLayout,所以ImageView盖在步骤3的flutterView之上。
addView(this.splashScreenView);
//步骤8、给flutterView添加监听回调,等第一帧绘制时触发。
//回调里面做的事本质就是从开屏过渡消失到flutterView显示出来。
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
else if (isSplashScreenTransitionNeededNow())
Log.v(TAG, "Showing an immediate splash transition to Flutter due to previously interrupted transition.");
//步骤9、同步骤6、7做的事情。
splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
addView(splashScreenView);
//步骤10、由于是中间状态,所以不用监听,直接添加后就从开屏过渡消失到flutterView显示出来。
transitionToFlutter();
else if (!flutterView.isAttachedToFlutterEngine())
Log.v(TAG, "FlutterView is not yet attached to a FlutterEngine. Showing nothing until a FlutterEngine is attached.");
//步骤11、如果这时候flutter引擎还没attach上。
//那就监听attach,等attach上就开始追加开屏并显示,等到渲染第一帧开始就结束。
flutterView.addFlutterEngineAttachmentListener(flutterEngineAttachmentListener);
//单纯的判断flutterView是否渲染出来,没出来就说明需要过渡界面。
private boolean isSplashScreenNeededNow()
return flutterView != null
&& flutterView.isAttachedToFlutterEngine()
&& !flutterView.hasRenderedFirstFrame()
&& !hasSplashCompleted();
//判断是否上一个过渡动画开屏正在进行中。
private boolean isSplashScreenTransitionNeededNow()
return flutterView != null
&& flutterView.isAttachedToFlutterEngine()
&& splashScreen != null
&& splashScreen.doesSplashViewRememberItsTransition()
&& wasPreviousSplashTransitionInterrupted();
//......
//开屏过渡到flutterview显示
private void transitionToFlutter()
//......
//步骤12、splashScreen就是DrawableSplashScreen。
//本质就是DrawableSplashScreenView(即ImageView)做一个默认500ms的alpha渐变透明动画。
//动画完毕回调onTransitionComplete接口实现,从当前FrameLayout中删除开屏追加的ImageView,child只剩下FlutterView。
splashScreen.transitionToFlutter(onTransitionComplete);
//......
//等attach上后走进步骤1流程,不解释。
@NonNull
private final FlutterView.FlutterEngineAttachmentListener flutterEngineAttachmentListener =
new FlutterView.FlutterEngineAttachmentListener()
@Override
public void onFlutterEngineAttachedToFlutterView(@NonNull FlutterEngine engine)
flutterView.removeFlutterEngineAttachmentListener(this);
displayFlutterViewWithSplash(flutterView, splashScreen);
//......
;
//flutterView的第一帧绘制时触发,本质就是从开屏过渡消失到flutterView显示出来。
@NonNull
private final FlutterUiDisplayListener flutterUiDisplayListener =
new FlutterUiDisplayListener()
@Override
public void onFlutterUiDisplayed()
if (splashScreen != null)
transitionToFlutter();
//......
;
//动画做完就移除开屏view控件。
@NonNull
private final Runnable onTransitionComplete =
new Runnable()
@Override
public void run()
removeView(splashScreenView);
//......
;
//......
看完上面代码你也就明白为什么我们在 Android Studio 中查看 FlutterActivity 的安卓层级树时,只看到 Activity content 的 child 是 FlutterSplashView,FlutterSplashView 的 child 是 FlutterView,而 FlutterSplashView 的另一个 child DrawableSplashScreenView 不见的原因就是 500ms 动画之后被 remove 了。如下图:
FlutterTextureView 相关分析
在前面系列文章中分析 FlutterActivity 时我们知道,FlutterView 创建时依赖一个 FlutterTextureView 或者 FlutterSurfaceView,其判断条件的本质就是看 FlutterActivity 的 window 窗体背景是否透明(FlutterFragment 时通过 Arguments 的 flutterview_render_mode 参数来决定),不透明就是 surface,透明就是 texture。因此,我们这里就是针对其 window 透明场景来分析的。
//步骤13、在一个SurfaceTexture上绘制Flutter UI,就是单纯的渲染,不处理点击等各种事件。
//想要开始渲染,FlutterTextureView的持有者需要先调用attachToRenderer(FlutterRenderer)。
//同理,想要终止渲染,FlutterTextureView的持有者需要先调用detachFromRenderer()。
public class FlutterTextureView extends TextureView implements RenderSurface
//......
//步骤14、主要是基于标准监听的connectSurfaceToRenderer和disconnectSurfaceFromRenderer操作。
private final SurfaceTextureListener surfaceTextureListener =
new SurfaceTextureListener()
@Override
public void onSurfaceTextureAvailable(
SurfaceTexture surfaceTexture, int width, int height)
//......
if (isAttachedToFlutterRenderer)
connectSurfaceToRenderer();
//......
@Override
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface)
//......
if (isAttachedToFlutterRenderer)
disconnectSurfaceFromRenderer();
return true;
;
//......
//步骤15、在FlutterView的attachToFlutterEngine方法中被调用。
//参数来自FlutterEngine的getRenderer(),类型是FlutterRenderer,里面本质是SurfaceTexture。
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer)
//......
connectSurfaceToRenderer();
//......
//步骤16、在FlutterView的detachFromFlutterEngine方法中被调用。
//与步骤15方法成对始终。
public void detachFromRenderer()
//......
disconnectSurfaceFromRenderer();
//......
private void connectSurfaceToRenderer()
//......
renderSurface = new Surface(getSurfaceTexture());
flutterRenderer.startRenderingToSurface(renderSurface);
private void disconnectSurfaceFromRenderer()
//......
flutterRenderer.stopRenderingToSurface();
if (renderSurface != null)
renderSurface.release();
renderSurface = null;
//......
上面可以看到,FlutterTextureView 的本质就是一个标准的 TextureView,用法也完全一样,只是渲染数据是通过 FlutterJNI 进行 engine 与 Android Java 层传递而已。
FlutterSurfaceView 相关分析
与上面 FlutterTextureView 分析同理,FlutterSurfaceView 自然就是针对其 window 不透明场景来分析的。下面是类似上面概览源码:
//步骤17、在一个Surface上绘制Flutter UI,就是单纯的渲染,不处理点击等各种事件。
//想要开始渲染,FlutterSurfaceView的持有者需要先调用attachToRenderer(FlutterRenderer)。
//同理,想要终止渲染,FlutterSurfaceView的持有者需要先调用detachFromRenderer()。
public class FlutterSurfaceView extends SurfaceView implements RenderSurface
//......
private final SurfaceHolder.Callback surfaceCallback =
new SurfaceHolder.Callback()
@Override
public void surfaceCreated(@NonNull SurfaceHolder holder)
//......
connectSurfaceToRenderer();
//......
@Override
public void surfaceDestroyed(@NonNull SurfaceHolder holder)
//......
disconnectSurfaceFromRenderer();
;
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer)
//......
connectSurfaceToRenderer();
public void detachFromRenderer()
//......
disconnectSurfaceFromRenderer();
private void connectSurfaceToRenderer()
//......
flutterRenderer.startRenderingToSurface(getHolder().getSurface());
private void disconnectSurfaceFromRenderer()
//......
flutterRenderer.stopRenderingToSurface();
//......
可以看到,不多解释,和 FlutterSurfaceView 基本如出一辙。
FlutterRenderer 相关分析
FlutterRenderer 的主要职责是通过 FlutterEngine 进行渲染关联处理,与原生平台提供的 FlutterSurfaceView、FlutterTextureView 进行纯 UI 渲染,将 Flutter 像素绘制到 Android 视图层次结构。
public class FlutterRenderer implements TextureRegistry
//......
@NonNull private final FlutterJNI flutterJNI;
@Nullable private Surface surface;
//......
通过上面源码的两个属性成员就能看出来他的职责。结合上面小节可以得到一个如下职责抽象架构图:
FlutterView 相关分析
FlutterView 的作用是在 Android 设备上显示一个 Flutter UI,绘制内容来自于 FlutterEngine 提供。FlutterView 有两种不同的渲染模式(io.flutter.embedding.android.RenderMode#surface
和io.flutter.embedding.android.RenderMode#texture
),其中 surface 模式的性能比较高,但是在 z-index 上无法与其他 Android View 进行布局,没法进行 animated、transformed 变换;而 texture 模式虽然性能没有 surface 高,但是没有 surface 的那些缺点限制。一般尽可能选择 surface 模式,FlutterView 的默认构造器就是 surface 模式,FlutterActivity 的 window 不透明时默认也是 surface 模式,FlutterFragment 的默认无参数修改情况下也是 surface 模式,不信可以翻看本系列的前面相关文章。
下面我们先看下 FlutterView 的成员和构造初始化相关流程,如下代码片段:
public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseCursorViewDelegate
//用来真正渲染绘制视图的。
@Nullable private FlutterSurfaceView flutterSurfaceView;
@Nullable private FlutterTextureView flutterTextureView;
@Nullable private FlutterImageView flutterImageView;
@Nullable private RenderSurface renderSurface;
@Nullable private RenderSurface previousRenderSurface;
//......
//用来处理Android View的input and events。
@Nullable private MouseCursorPlugin mouseCursorPlugin;
@Nullable private TextInputPlugin textInputPlugin;
@Nullable private LocalizationPlugin localizationPlugin;
@Nullable private AndroidKeyProcessor androidKeyProcessor;
@Nullable private AndroidTouchProcessor androidTouchProcessor;
@Nullable private AccessibilityBridge accessibilityBridge;
//缺省构造函数,默认模式为surface,即FlutterSurfaceView渲染。
public FlutterView(@NonNull Context context)
this(context, null, new FlutterSurfaceView(context));
//省略一堆各种参数的构造函数
//......
//本质就是指定一个RenderSurface,即如下三者之一。
private void init()
if (flutterSurfaceView != null)
addView(flutterSurfaceView);
else if (flutterTextureView != null)
addView(flutterTextureView);
else
addView(flutterImageView);
//FlutterView自己需要能接收事件。
setFocusable(true);
setFocusableInTouchMode(true);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS);
//......
通过上面代码我们可以知道,FlutterView 其实就是一个普通的 Android FrameLayout,其内部依据条件被 addView 了一个 View,这个 View 都实现自 RenderSurface 接口,也就是 FlutterSurfaceView、FlutterTextureView、FlutterImageView 之一,默认为 FlutterSurfaceView 而已。所以说真正绘制渲染 FlutterEngine 数据的不是 FlutterView,而是实现 RenderSurface 接口的控件,譬如 FlutterSurfaceView。整体 View 层级关系如下图:
构造完 FlutterView 实例后,我们通过前面的系列文章可以知道,在 FlutterActivityAndFragmentDelegate 的 onCreateView 方法返回给 FlutterActivity 一个 contentView 前 FlutterView 有通过自己的 attachToFlutterEngine 方法与 FlutterEngine 关联,所以我们看下这个关联方法(对应还有一个 detachFromFlutterEngine 方法进行取消关联):
public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseCursorViewDelegate
//......
public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine)
//......
//赋值flutterEngine。
this.flutterEngine = flutterEngine;
//从flutterEngine引擎获取flutterRenderer实例。
FlutterRenderer flutterRenderer = this.flutterEngine.getRenderer();
//renderSurface进行attachToRenderer,本质譬如就是FlutterSurfaceView的attachToRenderer方法。
renderSurface.attachToRenderer(flutterRenderer);
//初始化各种plugin。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
//鼠标相关插件。
mouseCursorPlugin = new MouseCursorPlugin(this, this.flutterEngine.getMouseCursorChannel());
//输入相关插件。
textInputPlugin =
new TextInputPlugin(this, this.flutterEngine.getTextInputChannel(), this.flutterEngine.getPlatformViewsController());
//config本地变更等插件。
localizationPlugin = this.flutterEngine.getLocalizationPlugin();
//key及touch事件、accessibility辅助模式相关channel通道处理。
androidKeyProcessor =
new AndroidKeyProcessor(this, this.flutterEngine.getKeyEventChannel(), textInputPlugin);
androidTouchProcessor =
new AndroidTouchProcessor(this.flutterEngine.getRenderer(), /*trackMotionEvents=*/ false);
accessibilityBridge =
new AccessibilityBridge(
this,
flutterEngine.getAccessibilityChannel(),
(AccessibilityManager) getContext().getSystemService(Context.ACCESSIBILITY_SERVICE),
getContext().getContentResolver(),
this.flutterEngine.getPlatformViewsController());
accessibilityBridge.setOnAccessibilityChangeListener(onAccessibilityChangeListener);
//各种平台相关事件初始调度。
this.flutterEngine.getPlatformViewsController().attachAccessibilityBridge(accessibilityBridge);
this.flutterEngine
.getPlatformViewsController()
.attachToFlutterRenderer(this.flutterEngine.getRenderer());
textInputPlugin.getInputMethodManager().restartInput(this);
sendUserSettingsToFlutter();
localizationPlugin.sendLocalesToFlutter(getResources().getConfiguration());
sendViewportMetricsToFlutter();
flutterEngine.getPlatformViewsController().attachToView(this);
//......
可以看到,FlutterView 与 FlutterEngine 进行 attach 时主要做的事情就是回调设置、渲染关联、系统平台 plugin 初始化关联等。上面的各种 plugin 我们可以先不用关心细节,知道 attachToFlutterEngine 主要做这些事情即可,后面会专门分析。
接着我们按照标准 Android 平台的 View 主要方法进行分类分析,先看看 FlutterView 的 onConfigurationChanged 方法,如下:
public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseCursorViewDelegate
//......
@Nullable private LocalizationPlugin localizationPlugin;
@Override
protected void onConfigurationChanged(@NonNull Configuration newConfig)
super.onConfigurationChanged(newConfig);
//响应系统屏幕渲染或者配置发生变化,譬如分屏、暗黑、多语言啥的。
if (flutterEngine != null)
Log.v(TAG, "Configuration changed. Sending locales and user settings to Flutter.");
//调用LocalizationPlugin插件设置变更后新的Configuration。
localizationPlugin.sendLocalesToFlutter(newConfig);
//把变更发送到FlutterEngine去,通知引擎。
sendUserSettingsToFlutter();
void sendUserSettingsToFlutter()
//当前是不是暗黑模式。
boolean isNightModeOn = (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK == Configuration.UI_MODE_NIGHT_YES以上是关于Flutter Android 端 FlutterView 相关流程源码分析的主要内容,如果未能解决你的问题,请参考以下文章
FlutterFlutter 混合开发 ( Flutter 与 Native 通信 | Android 端实现 BasicMessageChannel 通信 )
Flutter Android 端 Activity/Fragment 流程源码分析
是否可以在flutter应用程序关闭时通过平台通道从android后台服务向flutter端发送消息