This article will take a look at the past life of the Flutter hybrid stack from the perspective of Android. These are the changes from the 1.0 release to the 1.9 release.

This paper will analyze it from the following aspects:

  • What is a Flutter hybrid stack
  • Why is this a problem
  • A framework for dealing with mixed stacks
  • The official treatment plan

What is a hybrid stack

The rise of a new technology is bound to move forward step by step. To survive Flutter in the mature environment of Android and iOS, it is necessary to integrate with Native.

Some mature apps, if they want to use the Flutter technology, will not completely use the Flutter rewrite, which is too expensive. Therefore, the project of Native + Flutter appears. Native pages coexist with Flutter pages. They even appear alternately on the user’s phone.

So what is a hybrid stack?

Now that you have your answer, let’s take a fresh look at the hybrid stack from an Android perspective.

Let’s use the Demo provided by the Flutter Boost framework to see the effect of mixing stacks (debug package)

Two, why the problem

Now that we know about the mixed stack problem, let’s consider why it occurs.

What is the goal

The current situation is that there are multiple Flutter pages in one Activity. We want the Flutter Page to correspond to the Android Activity one by one. This way, we can easily merge the two Page stacks into one stack without affecting performance. This is our goal when dealing with the mixed stack.

As shown in the mix stack above, Android hosts Flutter with FlutterViews. By default, different FlutterViews create different engines for Flutter. If a Flutter Page corresponds to an Activity, this results in multiple resource creation and non-sharing of memory.

If extreme, Native => Flutter => Native =>… What happens to a Flutter? The consequences can be disastrous, of course, and such problems can be avoided in business, but they must be considered by the framers.

Now let’s see how we can solve the problem. Need to start from the principle, need to read the source code

Flutter architecture diagram

Start with the classic Diagram of the Flutter architecture

As you can see from the figure, there are three layers of architecture, each providing different capabilities

  • Framework: This layer provides the controls commonly used for Flutter, as well as some preparation for drawing. See how Flutter goes from load to display
  • Engine: This layer provides Skia, the 2D graphics rendering library for Flutter, and Dart VM, an object-oriented language for garbage collection, and hosts them in the Shell. There are also apis for interaction with Native, such as Platform Channels. See this article, The Engine Architecture
  • Embedder: This layer provides Embedder apis for different platforms such as Android and iOS. This allows Flutter to run on different embedded platforms. Here’s an article about Custom Embedders Custom Flutter Engine Embedders

Flutter from loading to display mp.weixin.qq.com/s/ncViI0KGi…

The Engine architecture github.com/flutter/flu…

Custom Flutter Engine Embedders github.com/flutter/flu…

Flutter in Android

We look at the Flutter program created automatically generated in the Android project, internal use IO. Flutter. The app package FlutterActivity, shall not discuss IO. Flutter. Embedding. The Android package related content, This will be analyzed later.

Using Flutter in Android looks like this

As shown above, there are some classes listed in the diagram, which are explained here

  • FlutterView: Android control for displaying Flutter pages. A FlutterView can display multiple Flutter widgets.“An Android View containing a Flutter app”. It contains the FlutterNativeView.
  • FlutterNativeView: Each FlutterNativeView contains a FlutterNativeView. The main function of this class is to communicate between Android and Flutter and keep the life cycle synchronized with the Activity and FlutterView. The interior contains DartExecutor.
  • DartExecutor: As the name suggests, this class is the Dart VM that handles Java and C/C++ calls. The official note reads:“Configures, bootstraps, and starts executing Dart code”.

In fact, this is how Flutter works. Flutter is presented by FlutterView. For each FlutterNativeView, a FlutterNativeView and a Dart VM will be created. Dart code memory within different FlutterViews cannot be shared.

The source code to read

I encourage you to read the code

io.flutter.app.FlutterActivit

Io.flutter.app.FlutterActivityDelegate

io.flutter.view.FlutterMain

io.flutter.view.FlutterView

io.flutter.view.FlutterNativeView

io.flutter.embedding.engine.dart.DartExecutor

io.flutter.embedding.engine.FlutterJNI

What can we do

From the introduction above, you should still know how Flutter works on Android. If you read the source code, you should be more impressed. What can we do with such a mechanism? The Flutter Boost framework gives us some ideas about how to solve this problem, but I would like readers to think about it for themselves. What would you do if you implemented the Flutter framework yourself?

Three, hybrid stack processing framework

Take a look at what the community has to offer.

There are a lot of articles on the web, and there are some links at the end of the article that interested readers can take a look at. This analysis only takes Flutter Boost as an example.

The treatment of Flutter Boost can be divided into two versions, using two schemes, which can be used as two representative ideas of the hybrid stack scheme.

Flutter Boost 0.0.4+ version

Alibaba/flutter_boost 0.0.420 github.com/alibaba/flu…

FlutterView multiplexing scheme

Framework from the FlutterActivityDelegate#onCreate method, rewrite the process of creating FlutterView, reuse FlutterView to achieve.

Let’s first look at how this version is accessed, initialized in Application

    FlutterBoostPlugin.init(new IPlatform() {
        ...
          
        /** * Gets the app entry Activity, which should always be provided inside the framework at the bottom of the stack during app interaction. Use */ for subsequent FlutterView creation
        @Override
        public Activity getMainActivity(a) {
            if(MainActivity.sRef ! =null) {
                return MainActivity.sRef.get();
            }
            return null; }... });Copy the code

Let’s look at how FlutterActivityDelegate#onCreate is handled.

# FlutterActivityDelegate.java @Override public void onCreate(Bundle savedInstanceState) { ... / / get flutterView flutterView = viewFactory. CreateFlutterView (activity); If (flutterView == null) {if (flutterView == null) { Create FlutterNative, DartExecutor FlutterNativeView nativeView = viewFactory. CreateFlutterNativeView (); flutterView = new FlutterView(activity, null, nativeView); flutterView.setLayoutParams(matchParent); activity.setContentView(flutterView); launchView = createLaunchView(); if (launchView ! = null) { addLaunchView(); }}... }Copy the code

The implementation of createFlutterView is in FlutterActivity.

# FlutterActivity.java
    @Override
    public FlutterView createFlutterView(Context context) {
        return null;
    }
Copy the code

The Flutter Boost framework overrides the createFlutterView method

# BoostFlutterActivity.java
		public FlutterView createFlutterView(Context context) {
        return FlutterBoostPlugin.viewProvider().createFlutterView(this);
    }
Copy the code

What’s really returned is what’s constructed here

# flutterViewProvider.java.override public BoostFlutterView createFlutterView(IFlutterViewContainer container) {// In MPlatform provided during the Application, will cache the Activity for this Activity Activity. = mPlatform getMainActivity (); if(activity == null) { Debuger.log("create Flutter View not with MainActivity"); activity = container.getActivity(); } // If null, create and then cache, If (mFlutterView == null) {// BoostFlutterView inherits from FlutterView mFlutterView = new BoostFlutterView(Activity, null, createFlutterNativeView(container)); } return mFlutterView; }Copy the code

This reuses the FlutterView.

Reusing FlutterView requires attaching and detach the view during Activity switching. This version uses the screenshot scheme.

Flutter Boost 0.1.5+ version

FlutterEngine reuse

In terms of access mode, initialization in Application provides a callback method and BoostFlutterEngine is an instance of FlutterEngine.

    FlutterBoost.init(new Platform() {
      	...
        @Override
        public IFlutterEngineProvider engineProvider(a) {
            return new BoostEngineProvider() {
                @Override
                public BoostFlutterEngine createEngine(Context context) {
                    return new BoostFlutterEngine(context, new DartExecutor.DartEntrypoint(
                            context.getResources().getAssets(),
                            FlutterMain.findAppBundlePath(context),
                            "main"), "/"); }}; }... });Copy the code

Look at the BoostFlutterActivity implementation

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState); . mFlutterEngine = createFlutterEngine(); mFlutterView = createFlutterView(mFlutterEngine); setContentView(mFlutterView); . }...protected BoostFlutterEngine createFlutterEngine(a){
        return FlutterBoost.singleton().engineProvider().provideEngine(this);
    }
Copy the code

What’s really returned is what’s constructed here

# com.idlefish.flutterboost.BoostEngineProvider @Override public BoostFlutterEngine provideEngine(Context context) { Utils.assertCallOnMainThread(); if (mEngine == null) { FlutterShellArgs flutterShellArgs = new FlutterShellArgs(new String[0]); FlutterMain.ensureInitializationComplete( context.getApplicationContext(), flutterShellArgs.toArray()); / / here is the method called initialization time to rewrite the content mEngine = createEngine (context) getApplicationContext ()); final IStateListener stateListener = FlutterBoost.sInstance.mStateListener; if(stateListener ! = null) { stateListener.onEngineCreated(mEngine); } } return mEngine; }Copy the code

The handling of flutter is basically the same as that of IO. Flutter. Embedding uses FlutterSurfaceView and FlutterTextureView in flutter. Let’s take a look at the ways that Flutter officially provides us.

The official way

Experimental: Adding Flutter to Android github.com/flutter/flu…

This method, unlike the Android project automatically generated by the Flutter project, mostly uses content from the IO. Flutter. Embedding package and provides a way to use the cached FlutterEngine. The FlutterEngineCache class is used to cache the key-value of FlutterEngine.

In flutter. Jar, it can be seen that there are two FlutterActivities, FlutterViews, etc.

FlutterActivity

io.flutter.app.FlutterActivity

io.flutter.embedding.FlutterActivity

FlutterView

io.flutter.view.FlutterView

io.flutter.embedding.FlutterView

Here is a brief introduction

# io.flutter.embedding.FlutterActivity.java @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); . delegate = new FlutterActivityAndFragmentDelegate(this); Create Flutter // Provide the FlutterEngine and bind it to the Activity, create and configure PlatformPlugin, delegate. OnAttach (this); . // Create a FlutterView and bind it to the Activity setContentView(createFlutterView()); . } @NonNull private View createFlutterView() { return delegate.onCreateView( null /* inflater */, null /* container */, null /* savedInstanceState */); }Copy the code

How is a View created when it is loaded onto an Activity

# io.flutter.embedding.android.FlutterActivityAndFragmentDelegate.java @NonNull View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { ... FlutterView = new FlutterView(host.getActivity(), host.getRenderMode(), host.getTransparencyMode()); FlutterSplashView = new FlutterSplashView(host.getContext()); FlutterSplashView(host.getContext()); . // Before FlutterView displays the first frame, First show provides the splashScreen flutterSplashView. DisplayFlutterViewWithSplash (flutterView, host provideSplashScreen ()); return flutterSplashView; }Copy the code

How is FlutterView implemented

  private void init(a) {
		// Use SurfaceView/TextureView based on renderMode. SurfaceView supports poor animation
    switch (renderMode) {
      case surface:
        FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(getContext(), transparencyMode == TransparencyMode.transparent);
        renderSurface = flutterSurfaceView;
        addView(flutterSurfaceView);
        break;
      case texture:
        FlutterTextureView flutterTextureView = new FlutterTextureView(getContext());
        renderSurface = flutterTextureView;
        addView(flutterTextureView);
        break;
    }
    setFocusable(true);
    setFocusableInTouchMode(true);
  }
Copy the code

That’s the general process.

summary

The hybrid stack processing of Flutter has also made great progress in more than a year. It is possible to have a deeper understanding of the scheme of the hybrid stack by combing the whole development process. Finally, the article about the mixed stack is attached

Flutter hybrid stack processing

Code on began to Flutter with it mixed development – FlutterBoos my.oschina.net/yunqi/blog/…

The Path to Flutter: Hybrid development film juejin.cn/post/684490…

Micro store Flutter hybrid stack management technology practice juejin.cn/post/684490…

Flutter implementation principle and cross-platform development event in hornet’s Nest juejin.cn/post/684490…

Make Flutter truly support viet-level mixed development mp.weixin.qq.com/s?__biz=MzI…