美文网首页
Flutter集成到原生的原理

Flutter集成到原生的原理

作者: 就叫汉堡吧 | 来源:发表于2021-12-02 14:03 被阅读0次

    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集成到原生的原理

          本文链接:https://www.haomeiwen.com/subject/iunqxrtx.html