Flutter集成到原生的原理

FlutterActivity等是如何把flutter UI集成到原生页面中去的?带着这个疑问,通过源码来分析一下它们的原理。

  • EntryPoint解析

    我们知道可以通过FlutterEngine的dartExecutor执行executeDartEntrypoint来设置切入点,这个切入点就是dart代码的入口。先来看一下默认的切入点:

    @NonNull
    public static DartEntrypoint createDefault() {
      FlutterLoader flutterLoader = FlutterInjector.instance().flutterLoader();
    
      if (!flutterLoader.initialized()) {
        throw new AssertionError(
            "DartEntrypoints can only be created once a FlutterEngine is created.");
      }
      return new DartEntrypoint(flutterLoader.findAppBundlePath(), "main");
    }
    

    DartEntrypoint构造方法的第二个参数是切入点函数名,注意是不带参数的函数,所以默认的最开始执行的dart函数是main()。

    如果需要更改函数名,就要自定义构造对象:

    engine.dartExecutor.executeDartEntrypoint(
        DartExecutor.DartEntrypoint(
        FlutterInjector.instance().flutterLoader().findAppBundlePath(),
        "showCell"))
    

    那么dart文件有那么多,该选择哪个文件中的切入点函数呢?Flutter要求程序第一个执行的文件必须是lib/main.dart,如果没有此文件的话会抛出异常:

    $ flutter build apk
    $ Target file "lib/main.dart" not found.
    $ Process finished with exit code 1
    

    根据官方的介绍,executeDartEntrypoint执行后就在flutterEngine中持有了一个0尺寸的View,等到attachToActivity后就显示在界面上。

    public void executeDartEntrypoint(@NonNull DartEntrypoint dartEntrypoint) {
      if (isApplicationRunning) {
        Log.w(TAG, "Attempted to run a DartExecutor that is already running.");
        return;
      }
    
      Log.v(TAG, "Executing Dart entrypoint: " + dartEntrypoint);
    
      flutterJNI.runBundleAndSnapshotFromLibrary(
          dartEntrypoint.pathToBundle,
          dartEntrypoint.dartEntrypointFunctionName,
          dartEntrypoint.dartEntrypointLibrary,
          assetManager);
    
      isApplicationRunning = true;
    }
    

    最终会调用JNI方法:

    private native void nativeRunBundleAndSnapshotFromLibrary(
        long nativeShellHolderId,
        @NonNull String bundlePath,
        @Nullable String entrypointFunctionName,
        @Nullable String pathToEntrypointFunction,
        @NonNull AssetManager manager);
    
  • FlutterEngine

    首先看一下FlutterEngine的构造方法:

    public FlutterEngine(
        @NonNull Context context,
        @Nullable FlutterLoader flutterLoader,
        @NonNull FlutterJNI flutterJNI,
        @NonNull PlatformViewsController platformViewsController,
        @Nullable String[] dartVmArgs,
        boolean automaticallyRegisterPlugins,
        boolean waitForRestorationData) {
      AssetManager assetManager;
      try {
        assetManager = context.createPackageContext(context.getPackageName(), 0).getAssets();
      } catch (NameNotFoundException e) {
        assetManager = context.getAssets();
      }
      this.dartExecutor = new DartExecutor(flutterJNI, assetManager);
      this.dartExecutor.onAttachedToJNI();
    
      DeferredComponentManager deferredComponentManager =
          FlutterInjector.instance().deferredComponentManager();
    
      accessibilityChannel = new AccessibilityChannel(dartExecutor, flutterJNI);
      deferredComponentChannel = new DeferredComponentChannel(dartExecutor);
      keyEventChannel = new KeyEventChannel(dartExecutor);
      lifecycleChannel = new LifecycleChannel(dartExecutor);
      localizationChannel = new LocalizationChannel(dartExecutor);
      mouseCursorChannel = new MouseCursorChannel(dartExecutor);
      navigationChannel = new NavigationChannel(dartExecutor);
      platformChannel = new PlatformChannel(dartExecutor);
      restorationChannel = new RestorationChannel(dartExecutor, waitForRestorationData);
      settingsChannel = new SettingsChannel(dartExecutor);
      systemChannel = new SystemChannel(dartExecutor);
      textInputChannel = new TextInputChannel(dartExecutor);
    
      if (deferredComponentManager != null) {
        deferredComponentManager.setDeferredComponentChannel(deferredComponentChannel);
      }
    
      this.localizationPlugin = new LocalizationPlugin(context, localizationChannel);
    
      this.flutterJNI = flutterJNI;
      if (flutterLoader == null) {
        flutterLoader = FlutterInjector.instance().flutterLoader();
      }
    
      if (!flutterJNI.isAttached()) {
        flutterLoader.startInitialization(context.getApplicationContext());
        flutterLoader.ensureInitializationComplete(context, dartVmArgs);
      }
    
      flutterJNI.addEngineLifecycleListener(engineLifecycleListener);
      flutterJNI.setPlatformViewsController(platformViewsController);
      flutterJNI.setLocalizationPlugin(localizationPlugin);
      flutterJNI.setDeferredComponentManager(FlutterInjector.instance().deferredComponentManager());
    
      // It should typically be a fresh, unattached JNI. But on a spawned engine, the JNI instance
      // is already attached to a native shell. In that case, the Java FlutterEngine is created around
      // an existing shell.
      if (!flutterJNI.isAttached()) {
        attachToJni();
      }
    
      // TODO(mattcarroll): FlutterRenderer is temporally coupled to attach(). Remove that coupling if
      // possible.
      this.renderer = new FlutterRenderer(flutterJNI);
    
      this.platformViewsController = platformViewsController;
      this.platformViewsController.onAttachedToJNI();
    
      this.pluginRegistry =
          new FlutterEngineConnectionRegistry(context.getApplicationContext(), this, flutterLoader);
    
      // Only automatically register plugins if both constructor parameter and
      // loaded AndroidManifest config turn this feature on.
      if (automaticallyRegisterPlugins && flutterLoader.automaticallyRegisterPlugins()) {
        registerPlugins();
      }
    }
    

    梳理一下:

    1. dartExecutor是在这里执行onAttachedToJNI方法的,这是执行executeDartEntrypoint的前提条件。
    2. 一系列的Channel创建。
    3. flutterLoader创建方法内部会返回一个new FlutterLoader()。
    4. platformViewsController是传递参数new PlatformViewsController()。
    5. pluginRegistry是FlutterEngineConnectionRegistry。
    6. 最后一段代码是否自动注册Plugin,默认是自动注册。
  • FlutterActivity初始化

    以startActivity为例,此时我们来看一下FlutterActivity是如何使用FlutterEngine的。

    @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();
    }
    

    delegate.onAttach()中:

    void onAttach(@NonNull Context context) {
      ensureAlive();
    
      // When "retain instance" is true, the FlutterEngine will survive configuration
      // changes. Therefore, we create a new one only if one does not already exist.
      if (flutterEngine == null) {
        setupFlutterEngine();
      }
    
      if (host.shouldAttachEngineToActivity()) {
        Log.v(TAG, "Attaching FlutterEngine to the Activity that owns this delegate.");
        flutterEngine.getActivityControlSurface().attachToActivity(this, host.getLifecycle());
      }
    
      platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
    
      host.configureFlutterEngine(flutterEngine);
    }
    

    setupFlutterEngine如下:

    void setupFlutterEngine() {
      Log.v(TAG, "Setting up FlutterEngine.");
    
      // First, check if the host wants to use a cached FlutterEngine.
      String cachedEngineId = host.getCachedEngineId();
      if (cachedEngineId != null) {
        flutterEngine = FlutterEngineCache.getInstance().get(cachedEngineId);
        isFlutterEngineFromHost = true;
        if (flutterEngine == null) {
          throw new IllegalStateException(
              "The requested cached FlutterEngine did not exist in the FlutterEngineCache: '"
                  + cachedEngineId
                  + "'");
        }
        return;
      }
    
      // Second, defer to subclasses for a custom FlutterEngine.
      flutterEngine = host.provideFlutterEngine(host.getContext());
      if (flutterEngine != null) {
        isFlutterEngineFromHost = true;
        return;
      }
    
      // Our host did not provide a custom FlutterEngine. Create a FlutterEngine to back our
      // FlutterView.
      Log.v(
          TAG,
          "No preferred FlutterEngine was provided. Creating a new FlutterEngine for"
              + " this FlutterFragment.");
      flutterEngine =
          new FlutterEngine(
              host.getContext(),
              host.getFlutterShellArgs().toArray(),
              /*automaticallyRegisterPlugins=*/ false,
              /*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
      isFlutterEngineFromHost = false;
    }
    

    可见这里创建了flutterEngine,首先会从cache中取,如果没有缓存则从provideFlutterEngine方法中返回,如果该方法没有重写创建则new一个,注意这里new生成的FlutterEngine的automaticallyRegisterPlugins属性设置为false,即不会自动注册plugin。

    创建完FlutterEngine之后如果需要则把它关联到Activity,FlutterActivity是一定会关联的,如果是FlutterFragment则根据setArguments传参决定。

    getActivityControlSurface()方法返回的是pluginRegistry,是FlutterEngineConnectionRegistry:

    @Override
    public void attachToActivity(@NonNull Activity activity, @NonNull Lifecycle lifecycle) {
      Log.v(
          TAG,
          "Attaching to an Activity: "
              + activity
              + "."
              + (isWaitingForActivityReattachment ? " This is after a config change." : ""));
      if (this.exclusiveActivity != null) {
        this.exclusiveActivity.detachFromFlutterEngine();
      }
      // If we were already attached to an app component, detach from it.
      detachFromAppComponent();
    
      if (this.exclusiveActivity != null) {
        throw new AssertionError("Only activity or exclusiveActivity should be set");
      }
      this.activity = activity;
      attachToActivityInternal(activity, lifecycle);
    }
    

    首先会和之前的FlutterEngine接触绑定,然后attach新的:

    private void attachToActivityInternal(@NonNull Activity activity, @NonNull Lifecycle lifecycle) {
      this.activityPluginBinding = new FlutterEngineActivityPluginBinding(activity, lifecycle);
    
      // Activate the PlatformViewsController. This must happen before any plugins attempt
      // to use it, otherwise an error stack trace will appear that says there is no
      // flutter/platform_views channel.
      flutterEngine
          .getPlatformViewsController()
          .attach(activity, flutterEngine.getRenderer(), flutterEngine.getDartExecutor());
    
      // Notify all ActivityAware plugins that they are now attached to a new Activity.
      for (ActivityAware activityAware : activityAwarePlugins.values()) {
        if (isWaitingForActivityReattachment) {
          activityAware.onReattachedToActivityForConfigChanges(activityPluginBinding);
        } else {
          activityAware.onAttachedToActivity(activityPluginBinding);
        }
      }
      isWaitingForActivityReattachment = false;
    }
    

    然后走到PlatformViewsController的attach中:

    public void attach(
        Context context, TextureRegistry textureRegistry, @NonNull DartExecutor dartExecutor) {
      if (this.context != null) {
        throw new AssertionError(
            "A PlatformViewsController can only be attached to a single output target.\n"
                + "attach was called while the PlatformViewsController was already attached.");
      }
      this.context = context;
      this.textureRegistry = textureRegistry;
      platformViewsChannel = new PlatformViewsChannel(dartExecutor);
      platformViewsChannel.setPlatformViewsHandler(channelHandler);
    }
    

    回到delegate.onAttach()中,最后执行host.configureFlutterEngine方法:

    GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
    
    @Keep
    public final class GeneratedPluginRegistrant {
      public static void registerWith(@NonNull FlutterEngine flutterEngine) {
      }
    }
    

    这个类是Flutter tool自动生成代码的,用来注册自定义的Android相关的plugin类,即只有定义了继承自FlutterPlugin的类时registerWith内才会有代码,比如:

    public static void registerWith(@NonNull FlutterEngine flutterEngine) {
      flutterEngine.getPlugins().add(new io.flutter.plugins.sensors.SensorsPlugin());
      flutterEngine.getPlugins().add(new io.flutter.plugins.urllauncher.UrlLauncherPlugin());
    }
    

    回到onCreate中,configureWindowForTransparency这里就用到了前面intent设置的background属性:

    private void configureWindowForTransparency() {
      BackgroundMode backgroundMode = getBackgroundMode();
      if (backgroundMode == BackgroundMode.transparent) {
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
      }
    }
    

    setContentView中传递的View是由createFlutterView生成的,接下来看重点的createFlutterView。

  • createFlutterView

    @NonNull
    private View createFlutterView() {
      return delegate.onCreateView(
          null /* inflater */, null /* container */, null /* savedInstanceState */);
    }
    
    @NonNull
    View onCreateView(
        LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
      Log.v(TAG, "Creating FlutterView.");
      ensureAlive();
    
      if (host.getRenderMode() == RenderMode.surface) {
        FlutterSurfaceView flutterSurfaceView =
            new FlutterSurfaceView(
                host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent);
    
        // Allow our host to customize FlutterSurfaceView, if desired.
        host.onFlutterSurfaceViewCreated(flutterSurfaceView);
    
        // Create the FlutterView that owns the FlutterSurfaceView.
        flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
      } else {
        FlutterTextureView flutterTextureView = new FlutterTextureView(host.getActivity());
    
        // Allow our host to customize FlutterSurfaceView, if desired.
        host.onFlutterTextureViewCreated(flutterTextureView);
    
        // Create the FlutterView that owns the FlutterTextureView.
        flutterView = new FlutterView(host.getActivity(), flutterTextureView);
      }
    
      // Add listener to be notified when Flutter renders its first frame.
      flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
    
      flutterSplashView = new FlutterSplashView(host.getContext());
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
        flutterSplashView.setId(View.generateViewId());
      } else {
        // TODO(mattcarroll): Find a better solution to this ID. This is a random, static ID.
        // It might conflict with other Views, and it means that only a single FlutterSplashView
        // can exist in a View hierarchy at one time.
        flutterSplashView.setId(486947586);
      }
      flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
    
      Log.v(TAG, "Attaching FlutterEngine to FlutterView.");
      flutterView.attachToFlutterEngine(flutterEngine);
    
      return flutterSplashView;
    }
    

    可以看到,会根据渲染模式创建两种不同的FlutterView:FlutterSurfaceView、FlutterTextureView,渲染模式根据:

    @Override
    public RenderMode getRenderMode() {
      return getBackgroundMode() == BackgroundMode.opaque ? RenderMode.surface : RenderMode.texture;
    }
    

    不透明的就是RenderMode.surface。

    针对两种不同的View会分别执行onFlutterTextureViewCreated和onFlutterTextureViewCreated方法,这两个方法没有默认实现交由子类按需使用。

    不同参数的FlutterView构造方法都会执行一个init方法:

    private void init() {
      Log.v(TAG, "Initializing FlutterView");
    
      if (flutterSurfaceView != null) {
        Log.v(TAG, "Internally using a FlutterSurfaceView.");
        addView(flutterSurfaceView);
      } else if (flutterTextureView != null) {
        Log.v(TAG, "Internally using a FlutterTextureView.");
        addView(flutterTextureView);
      } else {
        Log.v(TAG, "Internally using a FlutterImageView.");
        addView(flutterImageView);
      }
    
      // FlutterView needs to be focusable so that the InputMethodManager can interact with it.
      setFocusable(true);
      setFocusableInTouchMode(true);
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        setImportantForAutofill(View.IMPORTANT_FOR_AUTOFILL_YES_EXCLUDE_DESCENDANTS);
      }
    }
    

    FlutterView继承自FrameLayout,可以看到这里会把前面传递来的的FlutterSurfaceView或FlutterTextureView给添加进去。FlutterSplashView也是个FrameLayout,它的displayFlutterViewWithSplash方法如下:

    public void displayFlutterViewWithSplash(
        @NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) {
      // If we were displaying a previous FlutterView, remove it.
      if (this.flutterView != null) {
        this.flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener);
        removeView(this.flutterView);
      }
      // If we were displaying a previous splash screen View, remove it.
      if (splashScreenView != null) {
        removeView(splashScreenView);
      }
    
      // Display the new FlutterView.
      this.flutterView = flutterView;
      addView(flutterView);
    
      this.splashScreen = splashScreen;
    
      // Display the new splash screen, if needed.
      if (splashScreen != null) {
        if (isSplashScreenNeededNow()) {
          Log.v(TAG, "Showing splash screen UI.");
          // This is the typical case. A FlutterEngine is attached to the FlutterView and we're
          // waiting for the first frame to render. Show a splash UI until that happens.
          splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
          addView(this.splashScreenView);
          flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
        } else if (isSplashScreenTransitionNeededNow()) {
          Log.v(
              TAG,
              "Showing an immediate splash transition to Flutter due to previously interrupted transition.");
          splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
          addView(splashScreenView);
          transitionToFlutter();
        } else if (!flutterView.isAttachedToFlutterEngine()) {
          Log.v(
              TAG,
              "FlutterView is not yet attached to a FlutterEngine. Showing nothing until a FlutterEngine is attached.");
          flutterView.addFlutterEngineAttachmentListener(flutterEngineAttachmentListener);
        }
      }
    }
    

    可见,FlutterSplashView是当Manifest内配置了启动View的metadata时在flutterView上方再加一个splashView。

    所以到此,我们知道了,其实Activity显示的实质性的flutter内容就是前面的FlutterSurfaceView或FlutterTextureView。

    接下来,flutterView.attachToFlutterEngine(flutterEngine)把flutterView和flutterEngine绑定在一起,这也是原生和flutter的绑定,attachToFlutterEngine方法中的renderSurface.attachToRenderer(flutterRenderer)把FlutterSurfaceView或FlutterTextureView和FlutterRender绑定在一起,以FlutterSurfaceView为例:

    public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {
      Log.v(TAG, "Attaching to FlutterRenderer.");
      if (this.flutterRenderer != null) {
        Log.v(
            TAG,
            "Already connected to a FlutterRenderer. Detaching from old one and attaching to new one.");
        this.flutterRenderer.stopRenderingToSurface();
        this.flutterRenderer.removeIsDisplayingFlutterUiListener(flutterUiDisplayListener);
      }
    
      this.flutterRenderer = flutterRenderer;
      isAttachedToFlutterRenderer = true;
    
      this.flutterRenderer.addIsDisplayingFlutterUiListener(flutterUiDisplayListener);
    
      // If we're already attached to an Android window then we're now attached to both a renderer
      // and the Android window. We can begin rendering now.
      if (isSurfaceAvailableForRendering) {
        Log.v(
            TAG,
            "Surface is available for rendering. Connecting FlutterRenderer to Android surface.");
        connectSurfaceToRenderer();
      }
    }
    
    private void connectSurfaceToRenderer() {
      if (flutterRenderer == null || getHolder() == null) {
        throw new IllegalStateException(
            "connectSurfaceToRenderer() should only be called when flutterRenderer and getHolder() are non-null.");
      }
    
      flutterRenderer.startRenderingToSurface(getHolder().getSurface());
    }
    
    public void startRenderingToSurface(@NonNull Surface surface) {
      if (this.surface != null) {
        stopRenderingToSurface();
      }
    
      this.surface = surface;
    
      flutterJNI.onSurfaceCreated(surface);
    }
    

    可见,FlutterRender负责调用flutterJNI来将flutter代码成对应的Android原生View(这个过程是通过JNI实现的),然后flutterView持有这个surface,调用setContentView把flutterView放入Window中。

  • 总结

    总之,Flutter的显示原理就是把Flutter的代码通过JNI转换成对应效果的原生View。

    FlutterActivity里面有一个FlutterActivityAndFragmentDelegate来代理各种行为,创建一个FlutterView,用来作为转换后的View的父容器,然后它会和FlutterEngine绑定,FlutterEngine持有FlutterRenderer,FlutterRenderer又是和FlutterJNI绑定在一起的,最后通过FlutterJNI来调用JNI代码转换dart代码,FlutterJNI在FlutterEngine构造的时候又通过dartExecutor.onAttachedToJNI()方法把设置的切入点和FlutterJNI绑定在一起,FlutterJNI在查找dart代码执行入口的时候就会用到这个切入点。

    另外,FlutterEngine里面还持有了各种和Flutter打交道的channel,除了界面显示,其他的一些比如生命周期、各种事件等的交互都是通过FlutterEngine来处理的。

你可能感兴趣的:(Flutter集成到原生的原理)