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#surfaceio.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;    SettingsChannel.PlatformBrightness brightness = isNightModeOn            ? SettingsChannel.PlatformBrightness.dark : SettingsChannel.PlatformBrightness.light;    //通过flutterEngine的SettingsChannel发送变更音讯。    flutterEngine        .getSettingsChannel()        .startMessage()        .setTextScaleFactor(getResources().getConfiguration().fontScale)        .setUse24HourFormat(DateFormat.is24HourFormat(getContext()))        .setPlatformBrightness(brightness)        .send();  }  //......}

能够看到,当系统配置产生变更时 FlutterView 本人在安卓端其实不做什么事的,次要就是负责把事件告诉到 flutterEngine 端去,而后 flutterEngine 再传递到 dart 响应,从而触发新的绘制刷新成果。

因为整体都是这个模式,所以 FlutterView 中的非典型办法咱们不再剖析,类比即可。上面咱们看下事件是怎么派发的,如下:

@Overridepublic boolean dispatchKeyEvent(KeyEvent event) {  //......  return (isAttachedToFlutterEngine() && androidKeyProcessor.onKeyEvent(event))      || super.dispatchKeyEvent(event);}@Overridepublic boolean onTouchEvent(@NonNull MotionEvent event) {  //......  return androidTouchProcessor.onTouchEvent(event);}

啥感觉?androidTouchProcessor 实例就是后面剖析的 FlutterView 中 attachToFlutterEngine 办法里实例化的,实质就是通过 flutterEngine 的 KeyEventChannel 进行事件散发。到此也就应证了咱们后面说的,FlutterView 只是一个在安卓端治理的 View,外部的渲染有专门的 View 负责,外部的事件全副通过原生散发到 flutterEngine 进行 dart 代码的触发解决,而后交回原生平台渲染。以 FlutterSurfaceView 为例整体交互流程图很像上面这样:

通过如上超级形象图其实咱们就大略明确了 Flutter 框架的精华(当然,细节还是很简单的),也印证了一个纯 Flutter Android App 在原生平台侧的层级构造是上面这样:

FlutterImageView 相干剖析

剖析完 FlutterRenderer、FlutterSurfaceView、FlutterTextureView 及 FlutterView 之后咱们再来看看 FlutterImageView,其实他和下面的 FlutterSurfaceView 等工作流程很像,也是 FlutterView 外部的一种绘制成载体,只是有一些本人的独有特点。FlutterImageView 的次要作用是通过android.media.ImageReader把 Flutter UI 绘制到android.graphics.Canvas上。FlutterView 中 addView 为 FlutterImageView 的形式其实有两种,一种是后面介绍过的,通过 FlutterView 结构函数参数为 FlutterImageView 的办法实现,另一种是通过调用 FlutterView 中的 convertToImageView 办法实现。上面是 FlutterImageView 源码中的外围片段:

@TargetApi(19)public class FlutterImageView extends View implements RenderSurface {  //......  //原生控件的绘制操作  @Override  protected void onDraw(Canvas canvas) {    super.onDraw(canvas);    //绘制前先更新bitmap数据源    if (currentImage != null) {      updateCurrentBitmap();    }    //把bitmap画到canvas下面    if (currentBitmap != null) {      canvas.drawBitmap(currentBitmap, 0, 0, null);    }  }  @TargetApi(29)  private void updateCurrentBitmap() {    if (android.os.Build.VERSION.SDK_INT >= 29) {      final HardwareBuffer buffer = currentImage.getHardwareBuffer();      currentBitmap = Bitmap.wrapHardwareBuffer(buffer, ColorSpace.get(ColorSpace.Named.SRGB));      buffer.close();    } else {      final Plane[] imagePlanes = currentImage.getPlanes();      if (imagePlanes.length != 1) {        return;      }      final Plane imagePlane = imagePlanes[0];      final int desiredWidth = imagePlane.getRowStride() / imagePlane.getPixelStride();      final int desiredHeight = currentImage.getHeight();      if (currentBitmap == null          || currentBitmap.getWidth() != desiredWidth          || currentBitmap.getHeight() != desiredHeight) {        currentBitmap =            Bitmap.createBitmap(                desiredWidth, desiredHeight, android.graphics.Bitmap.Config.ARGB_8888);      }      ByteBuffer buffer = imagePlane.getBuffer();      buffer.rewind();      currentBitmap.copyPixelsFromBuffer(buffer);    }  }  //......}

能够看到,FlutterImageView 是一个一般原生 View,也实现了 RenderSurface 接口从而实现相似 FlutterSurfaceView 的个性。它的存在次要是解决咱们既须要渲染一个 Flutter UI 又想同时渲染一个 PlatformView(对于 PlatformView 咱们前面会有专题文章)的场景,因为 PlatformView 默认实现是在原生 FlutterView 上进行 addView 操作,当咱们想在 PlatformView 上持续盖一个 Flutter 本人渲染的控件就须要应用 FlutterImageView,通过 FlutterImageView 实现了 Surface(ImageReader) 和 Surface 的重叠。

总结

通过这么一个篇幅的剖析,咱们能够简略粗犷的总结为下图模式:

这下你懂了吗?