Flutter小知识--从桌面启动到展示FlutterUI都发生了什么

766 阅读3分钟

从点击桌面icon,到展示flutter第一帧,中间还有个展示页面,这中间发生了什么?
本文基于flutter-android平台进行分析,先放一张启动效果镇楼:

splash.gif

创建一个分析demo

首先启动AndroidStudio , File-> New -> New Flutter Project -> New Flutter Application,
然后输入项目名hello_flutter, 点击next,输入你的包名,然后点击finish,样例程序创建完成。
默认文件结构如下:

hello_flutter
	-.ides
	-android
		-app/src/main
		-gradle
	-ios
	-lib
	-test
	-pubspec.yaml

点击icon启动效果大致于镇楼图类似,下面我们基于启动代码进行分析。

AndroidManifest.xml

基于之前的知识,Flutter在android平台上运行时,会生成一个FlutterView并添加到Activity的容器view中。 既然是基于android平台,那么启动入口肯定在AndroidManifest.xml中配置。

<application
    android:name="io.flutter.app.FlutterApplication"
    android:label="hello_flutter"
    android:icon="@mipmap/ic_launcher">
    <activity
        android:name=".MainActivity"
        android:launchMode="singleTop"
        android:theme="@style/LaunchTheme"
        android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
        android:hardwareAccelerated="true"
        android:windowSoftInputMode="adjustResize">
        <!-- This keeps the window background of the activity showing
             until Flutter renders its first frame. It can be removed if
             there is no splash screen (such as the default splash screen
             defined in @style/LaunchTheme). --> //我是注释1
        <meta-data
            android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
            android:value="true" />
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>
            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
</application>

根据注释1 可以修改启动页展示的图片,本文不详述,后面会再写一篇关于 flutter启动页相关的内容。 由上面配置已知MainActivity是我们的启动入口,那么接下来一起看下。

public class MainActivity extends FlutterActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
  }
}

继承了FlutterActivity,看下 继承了FlutterActivity的 onCreate 做了什么:

public class FlutterActivity extends Activity implements Provider, PluginRegistry, ViewFactory {
    private static final String TAG = "FlutterActivity";
    private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
    private final FlutterActivityEvents eventDelegate;
    private final Provider viewProvider;
    private final PluginRegistry pluginRegistry;

    public FlutterActivity() {
        this.eventDelegate = this.delegate;
        this.viewProvider = this.delegate;
        this.pluginRegistry = this.delegate;
    }

    ...

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        this.eventDelegate.onCreate(savedInstanceState);//注释2
    }

    ...
}

注释2 eventDelegate其实就是FlutterActivityDelegate , 它的onCreate源码如下:

public void onCreate(Bundle savedInstanceState) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        Window window = activity.getWindow();
        window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
        window.setStatusBarColor(0x40000000);
        window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);
    }

    String[] args = getArgsFromIntent(this.activity.getIntent());
    FlutterMain.ensureInitializationComplete(this.activity.getApplicationContext(), args);
    this.flutterView = this.viewFactory.createFlutterView(this.activity);
    if (this.flutterView == null) {
    	//实现了 BinaryMessenger接口,可以用于flutter与native双向通信
        FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView();
        //创建flutterview,并添加到activity当中
        this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView);
        this.flutterView.setLayoutParams(matchParent);
        this.activity.setContentView(this.flutterView);
        this.launchView = this.createLaunchView();//创建一个LaunchView,这里是我们这次启动流程的重点
        if (this.launchView != null) {
            this.addLaunchView();//添加LaunchView()
        }
    }

    if (!this.loadIntent(this.activity.getIntent())) {
        String appBundlePath = FlutterMain.findAppBundlePath(this.activity.getApplicationContext());
        if (appBundlePath != null) {
            this.runBundle(appBundlePath);
        }

    }
}

private View createLaunchView() {
    if (!this.showSplashScreenUntilFirstFrame()) {
        return null;
    } else {
    	//根据activity配置的theme中的windowBackground来生成drawable,并设置给view。这里篇幅有限,就不展开了。
        Drawable launchScreenDrawable = this.getLaunchScreenDrawableFromActivityTheme();
        if (launchScreenDrawable == null) {
            return null;
        } else {
            View view = new View(this.activity);
            view.setLayoutParams(matchParent);
            view.setBackground(launchScreenDrawable);
            return view;
        }
    }
}

private void addLaunchView() {
    if (this.launchView != null) {
    	//给activity设置一个额外的content view,它添加到已经存在的view之后,已存在的对象上面的flutterview。
        this.activity.addContentView(this.launchView, matchParent);
        //给flutterview设置回调,当第一帧要显示时先动画淡出LaunchView,然后将其移除掉。
        this.flutterView.addFirstFrameListener(new FirstFrameListener() {
            public void onFirstFrame() {
                FlutterActivityDelegate.this.launchView.animate().alpha(0.0F).setListener(new AnimatorListenerAdapter() {
                    public void onAnimationEnd(Animator animation) {
                        ((ViewGroup)FlutterActivityDelegate.this.launchView.getParent()).removeView(FlutterActivityDelegate.this.launchView);
                        FlutterActivityDelegate.this.launchView = null;
                    }
                });
                FlutterActivityDelegate.this.flutterView.removeFirstFrameListener(this);
            }
        });
        this.activity.setTheme(android.R.style.Theme_Black_NoTitleBar);
    }
}

/*
 * 这里对应AndroidMenifest中的配置,如果设置是true,就希望在显示flutter第一帧之前展示一个启动页。
 */
private Boolean showSplashScreenUntilFirstFrame() {
    try {
        ActivityInfo activityInfo = this.activity.getPackageManager().getActivityInfo(this.activity.getComponentName(), 129);
        Bundle metadata = activityInfo.metaData;
        return metadata != null && metadata.getBoolean("io.flutter.app.android.SplashScreenUntilFirstFrame");
    } catch (NameNotFoundException var3) {
        return false;
    }
}

注释的比较清楚,大致流程相信看完注释就已经了解了。
总结 : 点击Icon启动 -> MainActivity.onCreate -> FlutterActivity.onCreate -> FlutterActivityDelegate.onCreate -> 创建并添加FlutterView -> 额外添加LauncherView -> FlutterView设置监听,将要展示第一帧时候移除 LauncherView-> 展示FlutterView

后续会再补一份关于flutter splashpage相关的文章,敬请期待。


如果你觉得这篇文章对你有益,还请帮忙转发和点赞,万分感谢。

Flutter烂笔头