Flutter原理分析(Android源码)

这里主要介绍下Flutter与Android之间的交互以及实现原理
第一步:分析编译后的产物
我是从事Android的开发者,所以主要分析apk文件,如下图所见

编译生成的apk

第二步:反编译apk文件,反编译过程这里就跳过,直接分析反编译产物
这里只有一个MainActivity,而且MainActivity是集成FlutterActivity

package com.lingyun.jinyu.flutter_jinyu;

import io.flutter.embedding.android.FlutterActivity;

public class MainActivity extends FlutterActivity
{
}

从源码可以看出,MainActivity啥也没做,全部都在FlutterActivity里面实现,接下来看FlutterActivity又是如何实现的(由于FlutterActivity代码比较多,这里只粘贴关键代码)
首先,发现FlutterActivity继承的是最原始的Activity,并实现了FlutterActivityAndFragmentDelegate.Host, LifecycleOwner两个接口

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

然后,我们来看setContentView关键代码,发现调用的是createFlutterView()方法,

  protected void onCreate(Bundle paramBundle)
  {
    switchLaunchThemeForNormalTheme();
    super.onCreate(paramBundle);
    FlutterActivityAndFragmentDelegate localFlutterActivityAndFragmentDelegate = new FlutterActivityAndFragmentDelegate(this);
    this.delegate = localFlutterActivityAndFragmentDelegate;
    localFlutterActivityAndFragmentDelegate.onAttach(this);
    this.delegate.onRestoreInstanceState(paramBundle);
    this.lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
    configureWindowForTransparency();
    setContentView(createFlutterView());
    configureStatusBarForFullscreenFlutterExperience();
  }

接着,我们继续看createFlutterView()方法,发现一个viewId参数FLUTTER_VIEW_ID,最终传递给localFlutterActivityAndFragmentDelegate的onCreateView方法,FLUTTER_VIEW_ID是一个静态常量值,说明在类加载的时候就已经初始化好了

public static final int FLUTTER_VIEW_ID = ViewUtils.generateViewId(61938);

  private View createFlutterView()
  {
    FlutterActivityAndFragmentDelegate localFlutterActivityAndFragmentDelegate = this.delegate;
    int i = FLUTTER_VIEW_ID;
    boolean bool;
    if (getRenderMode() == RenderMode.surface)
      bool = true;
    else
      bool = false;
    return localFlutterActivityAndFragmentDelegate.onCreateView(null, null, null, i, bool);
  }

最终,我们会看到真正的createView代码如下

 View onCreateView(LayoutInflater paramLayoutInflater, ViewGroup paramViewGroup, Bundle paramBundle, int paramInt, boolean paramBoolean)
  {
    Log.v("FlutterActivityAndFragmentDelegate", "Creating FlutterView.");
    ensureAlive();
    paramLayoutInflater = this.host.getRenderMode();
    paramViewGroup = RenderMode.surface;
    boolean bool2 = true;
    boolean bool1 = true;
    if (paramLayoutInflater == paramViewGroup)
    {
      paramLayoutInflater = this.host.getContext();
      if (this.host.getTransparencyMode() != TransparencyMode.transparent)
        bool1 = false;
      paramLayoutInflater = new FlutterSurfaceView(paramLayoutInflater, bool1);
      this.host.onFlutterSurfaceViewCreated(paramLayoutInflater);
      this.flutterView = new FlutterView(this.host.getContext(), paramLayoutInflater);
    }
    else
    {
      paramLayoutInflater = new FlutterTextureView(this.host.getContext());
      if (this.host.getTransparencyMode() == TransparencyMode.opaque)
        bool1 = bool2;
      else
        bool1 = false;
      paramLayoutInflater.setOpaque(bool1);
      this.host.onFlutterTextureViewCreated(paramLayoutInflater);
      this.flutterView = new FlutterView(this.host.getContext(), paramLayoutInflater);
    }
    this.flutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
    Log.v("FlutterActivityAndFragmentDelegate", "Attaching FlutterEngine to FlutterView.");
    this.flutterView.attachToFlutterEngine(this.flutterEngine);
    this.flutterView.setId(paramInt);
    paramLayoutInflater = this.host.provideSplashScreen();
    if (paramLayoutInflater != null)
    {
      Log.w("FlutterActivityAndFragmentDelegate", "A splash screen was provided to Flutter, but this is deprecated. See flutter.dev/go/android-splash-migration for migration steps.");
      paramViewGroup = new FlutterSplashView(this.host.getContext());
      paramViewGroup.setId(ViewUtils.generateViewId(486947586));
      paramViewGroup.displayFlutterViewWithSplash(this.flutterView, paramLayoutInflater);
      return paramViewGroup;
    }
    if (paramBoolean)
      delayFirstAndroidViewDraw(this.flutterView);
    return this.flutterView;
  }

在这里我们发现最终返回的是FlutterView或者FlutterSplashView,而这两种View是根据渲染模式来决定,从代码能看出当渲染模式为RenderMode.surface返回的是FlutterSplashView,当渲染模式为RenderMode.texture则返回的是FlutterView,我们来继续看下FlutterSplashView里面的代码又是如何实现的,结果发现FlutterSplashView 继承的是FrameLayout容器,所以我们只看带有addView关键代码,而这个代码都在displayFlutterViewWithSplash方法实现,

final class FlutterSplashView extends FrameLayout{

//中间代码省略

public void displayFlutterViewWithSplash(FlutterView paramFlutterView, SplashScreen paramSplashScreen)
  {
    Object localObject = this.flutterView;
    if (localObject != null)
    {
      ((FlutterView)localObject).removeOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
      removeView(this.flutterView);
    }
    localObject = this.splashScreenView;
    if (localObject != null)
      removeView((View)localObject);
    this.flutterView = paramFlutterView;
    addView(paramFlutterView);
    this.splashScreen = paramSplashScreen;
    if (paramSplashScreen != null)
    {
      if (isSplashScreenNeededNow())
      {
        Log.v(TAG, "Showing splash screen UI.");
        paramSplashScreen = paramSplashScreen.createSplashView(getContext(), this.splashScreenState);
        this.splashScreenView = paramSplashScreen;
        addView(paramSplashScreen);
        paramFlutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
        return;
      }
      if (isSplashScreenTransitionNeededNow())
      {
        Log.v(TAG, "Showing an immediate splash transition to Flutter due to previously interrupted transition.");
        paramFlutterView = paramSplashScreen.createSplashView(getContext(), this.splashScreenState);
        this.splashScreenView = paramFlutterView;
        addView(paramFlutterView);
        transitionToFlutter();
        return;
      }
      if (!paramFlutterView.isAttachedToFlutterEngine())
      {
        Log.v(TAG, "FlutterView is not yet attached to a FlutterEngine. Showing nothing until a FlutterEngine is attached.");
        paramFlutterView.addFlutterEngineAttachmentListener(this.flutterEngineAttachmentListener);
      }
    }
  }
}

这里我们发现只会add两种view,一种是FlutterView(flutterView),一种是View(splashScreenView),而splashScreenView的生成代码如下

public final class DrawableSplashScreen implements SplashScreen {
  @Nullable
  @Override
  public View createSplashView(@NonNull Context context, @Nullable Bundle savedInstanceState) {
    splashView = new DrawableSplashScreenView(context);
    splashView.setSplashDrawable(drawable, scaleType);
    return splashView;
  }
}
//这里的DrawableSplashScreenView代码如下,最终发现返回的是ImageView
public static class DrawableSplashScreenView extends ImageView {
    public DrawableSplashScreenView(@NonNull Context context) {
      this(context, null, 0);
    }
}

到这里我们可以得出结论Flutter页面渲染几乎都在FlutterView里面来完成的,所以我们来重点看下FlutterView源码,在这里发现FlutterView也是集成FrameLayout容器,所以我只摘取带有addView的方法代码

public class FlutterView extends FrameLayout implements MouseCursorPlugin.MouseCursorViewDelegate {

  @Nullable private FlutterSurfaceView flutterSurfaceView;
  @Nullable private FlutterTextureView flutterTextureView;
  @Nullable private FlutterImageView flutterImageView;

//中间代码跳过
  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);
    }
  }
}

而FlutterSurfaceView 是继承SurfaceView,FlutterTextureView是继承TextureView,两个都是Android效率最高的UI渲染控件,2D,3D游戏一般都用这两个容器实现的,做过JNI开发的也都知道TextureView,SurfaceView都是可以在C/C++层去完成渲染操作,接下来我们来看下,这里是否带有JNI相关代码,结果找到了几个关键类
io.flutter.view.FlutterView
io.flutter.view.FlutterNativeView
io.flutter.embedding.engine.FlutterJNI
这里粘贴部分代码

public class FlutterJNI {
public void loadLibrary() {
    if (FlutterJNI.loadLibraryCalled) {
      Log.w(TAG, "FlutterJNI.loadLibrary called more than once");
    }

    System.loadLibrary("flutter");
    FlutterJNI.loadLibraryCalled = true;
  }
  public native boolean nativeFlutterTextUtilsIsEmoji(int codePoint);

  public native boolean nativeFlutterTextUtilsIsEmojiModifier(int codePoint);

  public native boolean nativeFlutterTextUtilsIsEmojiModifierBase(int codePoint);

  public native boolean nativeFlutterTextUtilsIsVariationSelector(int codePoint);

  public native boolean nativeFlutterTextUtilsIsRegionalIndicator(int codePoint);
}

到这里java层的源码分析就告一段落了,得出的结论就是Flutter只是一个高级UI渲染方案,UI层的渲染工作基本都是通过Flutter的渲染引擎在C/C++层实现,来一张架构图可能更容易理解。

image.png

本人在简单学习完Dart语法以及Flutter的UI控件以后写了个demo,我运行到真机上后,用Android自带的工具(DDMS)分析了一下UI布局,发现Flutter渲染出来的都是Android原生控件,这可能就是为什么Flutter渲染的页面能做到几乎跟原生一样流畅,截图如下
image.png

希望这篇文章能解开一部分小伙伴心中的疑惑,加油!!!

你可能感兴趣的:(Flutter原理分析(Android源码))