Flutter-Android闪屏页(启动页)实现,以及启动流程分析,看这篇文章就够了
文章目录
环境
- Flutter 1.22.4
- Framework • revision 1aafb3a8b9 (4 weeks ago) • 2020-11-13 09:59:28 -0800
- Engine • revision 2c956a31c0
- Dart 2.10.4
启动页配置
启动流程
分为两个部分,Android应用启动和Flutter启动,FlutterView显示出来才表示Flutter加载完成,从MainActivity到FlutterView显示有一个时间间隔,会出现白屏现象,Flutter框架中为了消除白屏,在创建FlutterView的同时创建了FlutterSplashView,当FlutterView第一帧加载完成,则通知将FlutterSplashView移除。
源码分析
FlutterActivity
public class MainActivity extends FlutterActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public SplashScreen provideSplashScreen() {
return new SimpleSplashScreen();
}
}
首先看入口,MainActivity就只是继承了FlutterActivity,没有实现其他方法,看onCreate()方法。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// 切换背景为普通主题
switchLaunchThemeForNormalTheme();
super.onCreate(savedInstanceState);
// 发送onCreate的通知
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
delegate = new FlutterActivityAndFragmentDelegate(this);
// 非常重要的方法
delegate.onAttach(this);
delegate.onActivityCreated(savedInstanceState);
configureWindowForTransparency();
setContentView(createFlutterView());
configureStatusBarForFullscreenFlutterExperience();
}
switchLaunchThemeForNormalTheme
在Flutter构建的项目中src/main/res/values/styles.xml,包含LaunchTheme和NormalTheme主题,LaunchTheme作为Android启动时配置背景使用,NormalTheme则为Android应用启动后,Activity的默认主题。
FlutterActivityAndFragmentDelegate
比较核心的类,实际执行操作的代理类。使用一个代理类能保证FlutterActivity和FlutterFragment实现相同的逻辑。内部接口Host继承
- SplashScreenProvider提供首屏页。
- FlutterEngineProvider提供一个自定义的FlutterEngine, 默认为Null,使用系统自己的FlutterEngine。
- FlutterEngineConfigurator提供一个配置给FlutterEngine,可以在这里初始化插件配置
interface Host
extends SplashScreenProvider, FlutterEngineProvider, FlutterEngineConfigurator {
}
public interface SplashScreenProvider {
@Nullable
SplashScreen provideSplashScreen();
}
public interface FlutterEngineProvider {
@Nullable
FlutterEngine provideFlutterEngine(@NonNull Context context);
}
public interface FlutterEngineConfigurator {
void configureFlutterEngine(@NonNull FlutterEngine flutterEngine);
void cleanUpFlutterEngine(@NonNull FlutterEngine flutterEngine);
}
delegate.onAttach(this);
这是个初始化Flutter非常核心的方法。主要有以下几个功能
- 初始化Flutter系统。
- 获取或创建FlutterEngine。
- 创建并配置插件PlatformPlugin。
- 如果有需要,把FlutterEngine与FlutterActivity绑定。
- 通过configureFlutterEngine来配置FlutterEngine。
void onAttach(@NonNull Context context) {
// 确认是否实例还在
ensureAlive();
if (flutterEngine == null) {
// 第一步
setupFlutterEngine();
}
// 第二步
platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
// 第三步
if (host.shouldAttachEngineToActivity()) {
flutterEngine
.getActivityControlSurface()
.attachToActivity(host.getActivity(), host.getLifecycle());
}
// 第四步
host.configureFlutterEngine(flutterEngine);
}
第一步 初始化FlutterEngine
void setupFlutterEngine() {
// 首先
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;
}
flutterEngine = host.provideFlutterEngine(host.getContext());
if (flutterEngine != null) {
isFlutterEngineFromHost = true;
return;
}
flutterEngine =
new FlutterEngine(
host.getContext(),
host.getFlutterShellArgs().toArray(),
/*automaticallyRegisterPlugins=*/ false,
/*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
isFlutterEngineFromHost = false;
}
- 首先,从缓存里面读cachedEngineId,如果存在则通过id获取FlutterEngine。
- 如果没有cachedEngineId,则通过前面说的接口host.provideFlutterEngine获取自定义的FlutterEngine,但默认返回null。
- 所以最后通过,Flutter框架自己提供的FlutterEngine类初始化。
FlutterEngine构造函数简略介绍
flutterEngine =
new FlutterEngine(
host.getContext(),
host.getFlutterShellArgs().toArray(),
/*automaticallyRegisterPlugins=*/ false,
/*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
// ...省略
// 以下是注册插件,
this.pluginRegistry =
new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader);
// 由于automaticallyRegisterPlugins为false,这里其实并没有关联插件
if (automaticallyRegisterPlugins) {
registerPlugins();
}
- dartExecutor实例化,开启Dart 中的Isolate线程,并开始事件循环。
- 创建FlutterJNI用于与C++层进行交。
- 绑定系统默认的通道。
-
自动装载插件,调用registerPlugins()方法,绑定第三方插件,将第三方插件存入FlutterEnginePluginRegistry,由FlutterEnginePluginRegistry管理插件,而FlutterEnginePluginRegistry由FlutterEngine管理。
第二步 初始化PlatformPlugin系统平台插件
platformPlugin = host.providePlatformPlugin(host.getActivity(), flutterEngine);
public PlatformPlugin(Activity activity, PlatformChannel platformChannel) {
this.activity = activity;
this.platformChannel = platformChannel;
this.platformChannel.setPlatformMessageHandler(mPlatformMessageHandler);
mEnabledOverlays = DEFAULT_SYSTEM_UI;
}
提供了原生的plugin方法,通过PlatformChannel调用,如:更改状态栏、剪贴板等。
第三步 将插件与activity绑定
if (host.shouldAttachEngineToActivity()) {
flutterEngine
.getActivityControlSurface()
.attachToActivity(host.getActivity(), host.getLifecycle());
}
- host.shouldAttachEngineToActivity(),默认返回的true,建立Activity和Engine的关联关系。
- 实例化PlatformViewsChannel,以便能使用WebView、视频播放器等原生就实现的组件。
- 最终调用的FlutterEnginePluginRegistry.attachToActivity方法,与Activity关联。
for (ActivityAware activityAware : activityAwarePlugins.values()) {
if (isWaitingForActivityReattachment) {
activityAware.onReattachedToActivityForConfigChanges(activityPluginBinding);
} else {
activityAware.onAttachedToActivity(activityPluginBinding);
}
}
如果开启了自动装配automaticallyRegisterPlugins = true,则第一步中FlutterEnginePluginRegistry.registerPlugins()注册的第三方插件,保存在了activityAwarePlugins中。FlutterEnginePluginRegistry.attachToActivity中将activityAwarePlugins方法遍历出来与Activity关联。
但其实Flutter默认实现是把automaticallyRegisterPlugins设置为false,所以这里并没有绑定操作。
第四步 注册第三方插件
@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
}
注册pubspec.yaml里的原生插件,会在这里调用。
以下是我引入了网络环境判断的插件
public final class GeneratedPluginRegistrant {
public static void registerWith(@NonNull FlutterEngine flutterEngine) {
flutterEngine.getPlugins().add(new io.flutter.plugins.connectivity.ConnectivityPlugin());
}
}
最终会调用FlutterEngin -> FlutterEnginePluginRegistry.add方法,并且将这个Flugin与Activity关联起来。
@Override
public void add(@NonNull FlutterPlugin plugin) {
plugins.put(plugin.getClass(), plugin);
plugin.onAttachedToEngine(pluginBinding);
if (plugin instanceof ActivityAware) {
ActivityAware activityAware = (ActivityAware) plugin;
activityAwarePlugins.put(plugin.getClass(), activityAware);
if (isAttachedToActivity()) {
activityAware.onAttachedToActivity(activityPluginBinding);
}
}
}
启动页核心setContentView(createFlutterView());
@NonNull
View onCreateView(
LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
// 第一步
if (host.getRenderMode() == RenderMode.surface) {
FlutterSurfaceView flutterSurfaceView =
new FlutterSurfaceView(
host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent);
host.onFlutterSurfaceViewCreated(flutterSurfaceView);
flutterView = new FlutterView(host.getActivity(), flutterSurfaceView);
} else {
FlutterTextureView flutterTextureView = new FlutterTextureView(host.getActivity());
host.onFlutterTextureViewCreated(flutterTextureView);
flutterView = new FlutterView(host.getActivity(), flutterTextureView);
}
// 第二步
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
// 第三步
flutterSplashView = new FlutterSplashView(host.getContext());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
flutterSplashView.setId(View.generateViewId());
flutterSplashView.setId(486947586);
}
flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
// 第四步
Log.v(TAG, "Attaching FlutterEngine to FlutterView.");
flutterView.attachToFlutterEngine(flutterEngine);
// 第五步
return flutterSplashView;
}
第一步,根据渲染模式加载不同FlutterView
Flutter提供了两种渲染模式, RenderMode.surface , RenderMode.texture,默认使用了Surface方式。
Android中TextureView与SurfaceView的区别。
第二步,FlutterView增加第一帧显示回调
当Flutter引擎、绘制工具等准备完成,并完成了FlutterView的第一帧绘制则会回调,这个回调后面就有很大用处。
第三步,初始化FlutterSplashView
这里开始就是启动页的初始化。以下是启动页的核心,后面一起讲。
由于flutterView是作为flutterSplashView的一个参数,所以flutterSplashView是持有了flutterView。
flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
第四步,FlutterView与FlutterEngine绑定在一起
此时,Flutter的框架Embedder和Engine层就关联在一起,FlutterEngine将绘制的ui反映到FlutterView上。
第五步,返回一个flutterSplashView
将flutterSplashView返回给setContentView,作为显示内容,也就是屏幕上看到的界面。
flutterSplashView.displayFlutterViewWithSplash
final class FlutterSplashView extends FrameLayout {
}
首先可以看到,FlutterSplashView就是一个FrameLayout。
public void displayFlutterViewWithSplash(
@NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) {
// 第一步
this.flutterView = flutterView;
addView(flutterView);
this.splashScreen = splashScreen;
// 第二步
if (splashScreen != null) {
if (isSplashScreenNeededNow()) {
Log.v(TAG, "Showing splash screen UI.");
// 第三步
splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState);
addView(this.splashScreenView);
// 第四步
flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener);
}
}
}
splashScreen的参数,是从host.provideSplashScreen()得到的,而host其实就是FlutterActivity实现的一个接口,最终是由FlutterActivity中实现了provideSplashScreen。
第一步,添加FlutterView
前面说了,FlutterSplashView是一个FrameLayout。首先在FlutterSplashView中保存了FlutterView的引用,然后将FlutterView添加到FrameLayout中。
第二步,判断splashScreen是否为空
判断splashScreen是否为空,默认Flutter框架提供了实现View,默认是一个白屏页。
第三步,实现splashScreenView
通过splashScreen.createSplashView得到splashScreenView,splashScreen的参数,是从host.provideSplashScreen()得到的,而host其实就是FlutterActivity实现的一个接口,最终是由FlutterActivity中实现了provideSplashScreen。
第四步,flutterView监听第一帧绘制完成回调
回调函数,onTransitionComplete其实是一个Runnable方法,内部调用了removeView(splashScreenView),将splashScreenView从上层移除,这样FlutterView就显示在出来了。
移除splashScreenView的方法也有了,只要在splashScreen.transitionToFlutter的实现方法中调用onTransitionComplete.run()就能移除splashScreenView。
private void transitionToFlutter() {
splashScreen.transitionToFlutter(onTransitionComplete);
}
private final Runnable onTransitionComplete =
new Runnable() {
@Override
public void run() {
removeView(splashScreenView);
previousCompletedSplashIsolate = transitioningIsolateId;
}
};
本文地址:https://blog.csdn.net/ZZB_Bin/article/details/110930371