Flutter实践深入分析之——FlutterActivity/Fragment原理流程分析

Posted Beason_H

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Flutter实践深入分析之——FlutterActivity/Fragment原理流程分析相关的知识,希望对你有一定的参考价值。

前言

之前了解了flutter从启动到view的创建流程,本文一起来分析下Flutter android端FlutterActivity和FlutterFragment代码流程分析。

Flutter Android在创建新Flutter项目的时候并没有看到FlutterActivity/FlutterFragment,原因是Flutter Android的源码主要在maven端,也就是:io.flutter:flutter_embedding_xxx包中,配置地方是在flutter.gradle,可查看文章“深度了解Flutter APP的构建流程”。

如果使用flutter1.12版本之前FlutterActivity位于io.flutter.app.FlutterActivity,该类已经被Deprecated掉了。所以本文以我们项目中使用的版本flutter2.0.6为例,FlutterActivity位于:io.flutter.embedding.android.Flutter,下面我们直接进入FlutterActivity/FlutterFragment分析

FlutterActivity分析

在Flutter App Android端默认的MainActivity就是继承自io.flutter.embedding.android.FlutterActivity,该类展示的是一个全屏的Flutter UI。它的主要作用是:

  1. 显示Android的启动动画
  2. 下手Flutter的启动动画
  3. 配置状态栏
  4. 选择Dart执行应用程序包路径入口点(默认是main()),选择Flutter的初始路由
  5. 保存和回复实例状态

首先我们来看类声明部分

public class FlutterActivity extends Activity
    implements FlutterActivityAndFragmentDelegate.Host, LifecycleOwner {

FlutterActivityAndFragmentDelegate.Host分析

根据继承关系可以发现,FlutterActivity直接继承于Activity,同时实现了FlutterActivityAndFrameDelegate.Host,LifecycleOwner接口。Activity和LifecycleOwner不做解释,我们来看FlutterActivityAndFragmentDelegate.Host接口

class FlutterActivityAndFragmentDelegate implements ExclusiveAppComponent<Activity> {
  
  /**
   * 扩展四个接口
   * SplashScreenProvider,提供一个 SplashScreen 以在 Flutter 初始化和渲染其第一帧时显示
   * FlutterEngineProvider,提供和创建FlutterEngine
   * FlutterEngineConfigurator,在创建 FlutterEngine 后配置它,例如,添加插件
   * PlatformPlugin.PlatformPluginDelegate,PlatformPlugin 通常为 Flutter 框架请求的平台功能实现了默认行为,允许实现者自定义 Flutter 框架调用弹出 Android 端导航堆栈时所需的行为。
   */
  interface Host
      extends SplashScreenProvider,
          FlutterEngineProvider,
          FlutterEngineConfigurator,
          PlatformPlugin.PlatformPluginDelegate {
    @NonNull
    Context getContext();

    // 是否从intent获取路由
    @Nullable
    boolean shouldHandleDeeplinking();

    @Nullable
    Activity getActivity();

    // 获取Activity或者Fragment的Lifecycle
    @NonNull
    Lifecycle getLifecycle();

		// 获取宿主启动Flutter携带的参数,通过intent解析,譬如enable-dart-profiling等。
    @NonNull
    FlutterShellArgs getFlutterShellArgs();

    // 获取静态缓存的EngineId,如果没有就返回空,通过intent的cached_engine_id参数传递。
    @Nullable
    String getCachedEngineId();

    // 引擎是否跟宿主一起destory
    boolean shouldDestroyEngineWithHost();

    // detach flutter engine
    void detachFromFlutterEngine();

    // 获取dart主入口,默认main。
    @NonNull
    String getDartEntrypointFunctionName();

    // 返回app bundle dart代码存在的路径        
    @NonNull
    String getAppBundlePath();

    // 获取初始路由地址。
    // 默认先从intent中解析route的值,没有就去meta-data解析io.flutter.InitialRoute的值,没有就返回null。
    @Nullable
    String getInitialRoute();

    // 获取Flutter的渲染模式,详情见“[Flutter实践深入分析中——FlutterView相关源码分析](https://beason.blog.csdn.net/article/details/120929369)”
    @NonNull
    RenderMode getRenderMode();

    // 获取Transparency模式,用在FlutterView呈现FlutterEngine引擎渲染效果
    @NonNull
    TransparencyMode getTransparencyMode();

    // 提供一个Flutter开屏图片。
    @Nullable
    SplashScreen provideSplashScreen();

    // 返回一个用来渲染FlutterView的FlutterEngine引擎
    @Nullable
    FlutterEngine provideFlutterEngine(@NonNull Context context);

    // 创建和配置platform plugin。
    @Nullable
    PlatformPlugin providePlatformPlugin(
        @Nullable Activity activity, @NonNull FlutterEngine flutterEngine);

    // 根据需要配置FLutterEngine
    void configureFlutterEngine(@NonNull FlutterEngine flutterEngine);

    // 周期detached之前清除FlutterEngine配置
    void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine);

    // 如果 FlutterEngine 的插件系统已经连接到Activity,则返回 true,允许插件与其交互
    boolean shouldAttachEngineToActivity();

    void onFlutterSurfaceViewCreated(@NonNull FlutterSurfaceView flutterSurfaceView);

    void onFlutterTextureViewCreated(@NonNull FlutterTextureView flutterTextureView);

    // Flutter UI开始绘制时调用
    void onFlutterUiDisplayed();

    // Flutter停止绘制时调用
    void onFlutterUiNoLongerDisplayed();

    // 恢复状态
    boolean shouldRestoreAndSaveState();
  }
}

根据上面的源码分析可以知道FlutterActivityAndFragmentDelegate.Host提供的一系列相关引擎声明周期、Flutter UI绘制周期、插件相关周期等方法可以知道,它是一个中间层,它是Flutter与Activity/Fragment之间的一个接口约定,而宿主Activity/Fragment几乎不直接参加与Flutter之间的交互。这样做的好处有:

  1. 利于解耦,Flutter/Fragment不直接与Flutter交互,利于维护
  2. Flutter版本迭代快,利于项目后期Flutter SDK的升级,而不必修改任何原生代码
  3. 代码的重复利用,后面会介绍Fragment和FlutterFragmentActivity,大部分都是复用了FlutterActivityAndFragmentDelegate内部相关逻辑

下面我们再来看看FlutterActivity的onCreate方法

  @Override
  protected void onCreate(@Nullable Bundle savedInstanceState) {
    switchLaunchThemeForNormalTheme();

    super.onCreate(savedInstanceState);

    delegate = new FlutterActivityAndFragmentDelegate(this);
    delegate.onAttach(this);
    delegate.onRestoreInstanceState(savedInstanceState);

    lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);

    configureWindowForTransparency();
    setContentView(createFlutterView());
    configureStatusBarForFullscreenFlutterExperience();
  }

详细的介绍之前的文章有介绍这里不再详述,见:Flutter时间深入分析之——Flutter APP启动流程分析

FlutterActivityAndFragmentDelegate分析

从上面分析我们知道FlutterActivityAndFragmentDelegate.Host声明了大量的Activity/Fragment与Flutter平台通信的接口,那么FlutterActivityAndFragmentDelegate根据名字我们大概判断它就是Activity/Fragment的一个代理类,也是直接与Activity/Fragment进行交互的类。

下面一个个来分析FlutterActivityAndFragmentDelegate的核心方法

onAttach方法

onAttach方法在Activity的onCreate方法和Fragment的onAttach方法中被调用,核心源码:

void onAttach(@NonNull Context context) {
    // 确保delegate没有被release
    ensureAlive();

    if (flutterEngine == null) {
      setupFlutterEngine();
    }

    if (host.shouldAttachEngineToActivity()) {
      flutterEngine.getActivityControlSurface().attachToActivity(this, host.getLifecycle());
    }
    platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
    host.configureFlutterEngine(flutterEngine);
  }

从上面的代码分析,attach方法主要职责:

  1. 初始化Flutter系统,确保delegate没有被release
  2. 获取(根据EngineID获取,目前还未正式使用)或者创建FlutterEngine
  3. 平台插件获取和自定义插件的注册
  4. 引擎配置。

onStart方法

onStart方法在Activity和Fragment的onStart方法被调用,核心源码:

 // Activity和Fragment的onStart方法被调用,开始执行Dart 代码的入口(Dart代码如果没有执行的情况下)
 void onStart() {
    Log.v(TAG, "onStart()");
    ensureAlive();
    doInitialFlutterViewRun();
  }

  /**
   * 首次在FlutterView中执行Dart代码
   * 不支持在给定的FlutterView中重新加载、重启Dart。如果Dart已经运行,那么什么都不做
   */
  private void doInitialFlutterViewRun() {
    if (host.getCachedEngineId() != null) {
      return;
    }

    if (flutterEngine.getDartExecutor().isExecutingDart()) {
      return;
    }
    // 各种优先级获取初始跳转dart的路由地址。
    String initialRoute = host.getInitialRoute();
    if (initialRoute == null) {
      initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent());
      if (initialRoute == null) {
        initialRoute = DEFAULT_INITIAL_ROUTE;
      }
    }
    // 通过引擎的NavigationChannel设置初始路由信息。
    flutterEngine.getNavigationChannel().setInitialRoute(initialRoute);

    //按照优先级获取appBundlePath,默认从host获取,无则从FlutterLoader获取。
    String appBundlePathOverride = host.getAppBundlePath();
    if (appBundlePathOverride == null || appBundlePathOverride.isEmpty()) {
      appBundlePathOverride = FlutterInjector.instance().flutterLoader().findAppBundlePath();
    }

    // 配置dart的entrypoint并且执行,默认入口函数名为main,可通过meta-data的io.flutter.Entrypoint修改。
    DartExecutor.DartEntrypoint entrypoint =
        new DartExecutor.DartEntrypoint(
            appBundlePathOverride, host.getDartEntrypointFunctionName());
    flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint);
  }

onCreateView方法

看完上面的逻辑,引擎,插件,环境都已经准备妥当,接下来就是创建View,onCreateView见名知意,大概职责如下:

  1. 在 View 层次结构中创建一个新的 FlutterView
  2. 添加一个 FlutterUiDisplayListener
  3. 将 FlutterEngine 附加到新的 FlutterView
  4. 返回新的视图层次结构

我们来看看onCreateView方法:

@NonNull
  View onCreateView(
      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    // 确保 delegate没有吧release
    ensureAlive();

    // 根据不同的渲染模式选择不同的View(SurfaceView或者TextureView)
    if (host.getRenderMode() == RenderMode.surface) {
      FlutterSurfaceView flutterSurfaceView =
          new FlutterSurfaceView(
              host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent);

      // 创建FLutterView包含FlutterSurfaceView
      host.onFlutterSurfaceViewCreated(flutterSurfaceView);

      flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
    } else {
      FlutterTextureView flutterTextureView = new FlutterTextureView(host.getActivity());

      // 创建FLutterView包含FlutterTextureView
      host.onFlutterTextureViewCreated(flutterTextureView);
      flutterView = new FlutterView(host.getActivity(), flutterTextureView);
    }

    // 添加监听,当flutter渲染首帧时回调。当 Flutter 开始和停止将像素渲染到 Android 视图层次结构时调用的侦听器。
    flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
    // 创建一个FlutterSplashView开屏view
    flutterSplashView = new FlutterSplashView(host.getContext());
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
      flutterSplashView.setId(View.generateViewId());
    } else {
      flutterSplashView.setId(486947586);
    }
    // 显示开屏图标,即io.flutter.embedding.android.SplashScreenDrawable配置的drawable图
    flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());

    // FlutterView与flutterEngine关联attach。
    flutterView.attachToFlutterEngine(flutterEngine);
    // 返回被开屏view包裹的FlutterView。
    return flutterSplashView;
  }

	//回调监听定义,回调中本质是触发调用对应FlutterActivity或FlutterFragment的FlutterActivityAndFragmentDelegate.Host实现方法。
  @NonNull
  private final FlutterUiDisplayListener flutterUiDisplayListener =
      new FlutterUiDisplayListener() {
        @Override
        public void onFlutterUiDisplayed() {
          //本质在FlutterActivity中调用Activity 5.0以上的reportFullyDrawn()安卓官方方法。
          host.onFlutterUiDisplayed();
        }

        @Override
        public void onFlutterUiNoLongerDisplayed() {
          //本质在FlutterActivity中调用,默认空实现。
          host.onFlutterUiNoLongerDisplayed();
        }
      };

到这里我们FlutterActivityAndFragmentDelegate的核心代码基本上介绍完毕

从上面代码分析结果看onCreateView的职责和逻辑都比较清晰,但是唯一有个疑问就是为什么最后返回的不是FLutterView而是返回的一个包含FLutterView的FlutterSplashView,下面我们再一起来分析下FlutterSplashView,看看是如何处理FlutterSplashView和FlutterView之间的关系。

FlutterSplashView分析

FlutterSplashView按照字面理解它的主要作用就是显示 SplashScreen 的视图,直到给定的 FlutterView 呈现其第一帧。

我们来看看它的核心流程:

final class FlutterSplashView extends FrameLayout {

  // 当 FlutterEngine 附加到给定 FlutterView 或从给定 FlutterView 分离时收到通知的侦听器。
  @NonNull
  private final FlutterView.FlutterEngineAttachmentListener flutterEngineAttachmentListener =  ...
  // 当 Flutter 开始和停止将像素渲染到 Android 视图层次结构时调用的侦听器。
  @NonNull
  private final FlutterUiDisplayListener flutterUiDisplayListener = ...
    
  // 将启动画面转换为 Flutter UI
  @NonNull
  private final Runnable onTransitionComplete =
      new Runnable() {
        @Override
        public void run() {
          removeView(splashScreenView);
          previousCompletedSplashIsolate = transitioningIsolateId;
        }
      };

  /**
   * 把给定的splashScreen显示在flutterView之上,直到flutterView的首帧渲染出来才过渡消失
   */
  public void displayFlutterViewWithSplash(
      @NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) {
    // 如果上一个FlutterView显示中,先删除.
    if (this.flutterView != null) {
      this.flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener);
      removeView(this.flutterView);
    }
    // 复位
    if (splashScreenView != null) {
      removeView(splashScreenView);
    }

    // 把flutterView添加给当前FlutterSplashView,本质是一个FrameLayout。
    this.flutterView = flutterView;
    addView(flutterView);

    this.splashScreen = splashScreen;

    // 显示一个splash screen开屏图。
    if (splashScreen != null) {
      //如果flutterView未渲染出来则条件成立。
      if (isSplashScreenNeededNow()) {
        splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
        addView(this.splashScreenView);
        flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
      } else if (isSplashScreenTransitionNeededNow()) {
        splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
        addView(splashScreenView);
        transitionToFlutter();
      } else if (!flutterView.isAttachedToFlutterEngine()) {
        flutterView.addFlutterEngineAttachmentListener(flutterEngineAttachmentListener);
      }
    }
  }

  /**
   * 如果启动画面正在过渡到 Flutter 体验,则返回 true
   */
  private boolean wasPreviousSplashTransitionInterrupted() {
    return flutterView.hasRenderedFirstFrame() && !hasSplashCompleted();
  }

  /**
   * 如果特定 Flutter 体验的启动 UI 已经完成,则返回 true。
   */
  private boolean hasSplashCompleted() {
    return flutterView.getAttachedFlutterEngine().getDartExecutor().getIsolateServiceId() != null
        && flutterView
            .getAttachedFlutterEngine()
            .getDartExecutor()
            .getIsolateServiceId()
            .equals(previousCompletedSplashIsolate);
  }

  /**
   * 开屏过渡到flutterview显示
   */
  private void transitionToFlutter() {
    transitioningIsolateId =
        flutterView.getAttachedFlutterEngine().getDartExecutor().getIsolateServiceId();
    splashScreen.transitionToFlutter(onTransitionComplete);
  }
}

从上面的代码分析,FlutterSplashView逻辑并不复杂,主要就是在FlutterUI第一帧渲染之前显示默认的Drawable。等到Flutter UI第一帧绘制的时候再remove掉。内部的实现逻辑都比较简单这里不再介绍。下面继续


FlutterFragment分析

上面分析完FlutterActivity的流程,我们再来看FlutterFragment就比较简单了。

我们在使用FlutterFragment的时候,如果宿主Actvity而不是FlutterFragmentActivity的话,需要我们手动关联一下方法,这些方法都被添加了@ActvivityCallThrough注释声明,如:

  1. onPostResume()
  2. onBackPressed()
  3. onRequestPermissionsResult(int, String[], int[]) ()}
  4. onNewIntent(Intent) ()}
  5. onUserLeaveHint()
  6. onTrimMemory(int)

此外,当为此 Fragment 的startActivityForResult 时,请务必调用 Fragment.startActivityForResult(Intent, int) 而不是 Activity.startActivityForResult(Intent, int)。如果调用了该方法的 Activity 版本,则此 Fragment 将永远不会收到其 Fragment.onActivityResult(int, int, Intent) 回调。如果方便,可以考虑使用 FlutterActivity 而不是 FlutterFragment 来避免转发调用的工作。

下面看核心源码

//FlutterFragment也实现了前面分析的FlutterActivityAndFragmentDelegate.Host接口。
public class FlutterFragment extends Fragment
    implements FlutterActivityAndFragmentDelegate.Host, ComponentCallbacks2 {
  ......
  @Override
  public void onAttach(@NonNull Context context) {
    super.onAttach(context);
    //FlutterActivity的区别在于FlutterFragment在他自己的onAttach中实例化FlutterActivityAndFragmentDelegate并调用onAttach方法。
    delegate = new FlutterActivityAndFragmentDelegate(this);
    delegate.onAttach(context);
  }

  @Nullable
  @Override
  public View onCreateView(
      LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    //FlutterActivity类似,只不过是在FlutterFragment对应生命周期回调。
    return delegate.onCreateView(inflater, container, savedInstanceState);
  }
  ......
  //与FlutterActivity类似,只是这个方法不是Fragment自己框架回调,需要依赖在Activity中调用。
  //譬如FlutterFragmentActivity中对应同名方法的实现。
  //注意这里的@ActivityCallThrough注解就是这个含义。
  @ActivityCallThrough
  public void onPostResume() {
    delegate.onPostResume();
  }
  ......
}

其实我们掌握了FlutterActivity之后,FlutterFragment核心基本上没啥区别,主要都是通过代理FlutterActivityAndFragmentDelegate与Flutter平台打交道,实现UI的展示。不做细说,下面继续


FlutterFragmentActivity分析

从上面的FlutterFragment可以看到,FlutterFragmentActivity可以说是FlutterFragment的一个宿主Activity,给我们自己使用Activity绑定FlutterFragment做了很好的参考,其内部与Flutter平台的关联关系都已经处理,用户不需要再考虑上面的各种关联方法的关联。

FlutterFragmentActivity是一个基于 FragmentActivity 的 Flutter Activity。

FlutterFragmentActivity 的存在是因为生态系统中有一些 Android API 只接受一个 FragmentActivity。 如果不需要 FragmentActivity,则应考虑使用常规 FlutterActivity 代替。

我们继续看FlutterFragmentActivity核心方法

onCreate方法

 @Override
  protected void onCreate(@Nullable 以上是关于Flutter实践深入分析之——FlutterActivity/Fragment原理流程分析的主要内容,如果未能解决你的问题,请参考以下文章

Flutter实践深入分析之——FlutterActivity/Fragment原理流程分析

Flutter实践深入分析之——FlutterActivity/Fragment原理流程分析

Flutter实践深入——平台整合Hybrid composition分析

Flutter 命令本质之 Flutter tools 机制源码深入分析

Flutter 命令本质之 Flutter tools 机制源码深入分析

Flutter 命令本质之 Flutter tools 机制源码深入分析