这里主要介绍下Flutter与Android之间的交互以及实现原理
第一步:分析编译后的产物
我是从事Android的开发者,所以主要分析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++层实现,来一张架构图可能更容易理解。
本人在简单学习完Dart语法以及Flutter的UI控件以后写了个demo,我运行到真机上后,用Android自带的工具(DDMS)分析了一下UI布局,发现Flutter渲染出来的都是Android原生控件,这可能就是为什么Flutter渲染的页面能做到几乎跟原生一样流畅,截图如下
希望这篇文章能解开一部分小伙伴心中的疑惑,加油!!!