当前位置:   article > 正文

Flutter-Android闪屏页(启动页)实现,以及启动流程分析,看这篇文章就够了_flutter安卓自定义启动页

flutter安卓自定义启动页

环境

  • 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();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

首先看入口,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();
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

delegate.onAttach(this);

这是个初始化Flutter非常核心的方法。主要有以下几个功能

  1. 初始化Flutter系统。
  2. 获取或创建FlutterEngine。
  3. 创建并配置插件PlatformPlugin。
  4. 如果有需要,把FlutterEngine与FlutterActivity绑定。
  5. 通过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);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
第一步 初始化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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  1. 首先,从缓存里面读cachedEngineId,如果存在则通过id获取FlutterEngine。
  2. 如果没有cachedEngineId,则通过前面说的接口host.provideFlutterEngine获取自定义的FlutterEngine,但默认返回null。
  3. 所以最后通过,Flutter框架自己提供的FlutterEngine类初始化。

FlutterEngine构造函数简略介绍

flutterEngine =
        new FlutterEngine(
            host.getContext(),
            host.getFlutterShellArgs().toArray(),
            /*automaticallyRegisterPlugins=*/ false, 
            /*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
// ...省略
// 以下是注册插件,
this.pluginRegistry =
        new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader);

	// 由于automaticallyRegisterPlugins为false,这里其实并没有关联插件
    if (automaticallyRegisterPlugins) {
      registerPlugins();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  1. dartExecutor实例化,开启Dart 中的Isolate线程,并开始事件循环。
  2. 创建FlutterJNI用于与C++层进行交。
  3. 绑定系统默认的通道。
  4. 自动装载插件,调用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;
  }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

提供了原生的plugin方法,通过PlatformChannel调用,如:更改状态栏、剪贴板等。

第三步 将插件与activity绑定
if (host.shouldAttachEngineToActivity()) {

      flutterEngine
          .getActivityControlSurface()
          .attachToActivity(host.getActivity(), host.getLifecycle());
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  1. host.shouldAttachEngineToActivity(),默认返回的true,建立Activity和Engine的关联关系。
  2. 实例化PlatformViewsChannel,以便能使用WebView、视频播放器等原生就实现的组件。
  3. 最终调用的FlutterEnginePluginRegistry.attachToActivity方法,与Activity关联。
for (ActivityAware activityAware : activityAwarePlugins.values()) {
      if (isWaitingForActivityReattachment) {
        activityAware.onReattachedToActivityForConfigChanges(activityPluginBinding);
      } else {
        activityAware.onAttachedToActivity(activityPluginBinding);
      }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

如果开启了自动装配automaticallyRegisterPlugins = true,则第一步中FlutterEnginePluginRegistry.registerPlugins()注册的第三方插件,保存在了activityAwarePlugins中。FlutterEnginePluginRegistry.attachToActivity中将activityAwarePlugins方法遍历出来与Activity关联。
但其实Flutter默认实现是把automaticallyRegisterPlugins设置为false,所以这里并没有绑定操作。

第四步 注册第三方插件
@Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    GeneratedPluginRegister.registerGeneratedPlugins(flutterEngine);
  }
  • 1
  • 2
  • 3
  • 4

注册pubspec.yaml里的原生插件,会在这里调用。
以下是我引入了网络环境判断的插件

public final class GeneratedPluginRegistrant {
  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(new io.flutter.plugins.connectivity.ConnectivityPlugin());
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5

最终会调用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);
      }
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

启动页核心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;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

第一步,根据渲染模式加载不同FlutterView

Flutter提供了两种渲染模式, RenderMode.surface , RenderMode.texture,默认使用了Surface方式。

Android中TextureView与SurfaceView的区别。

第二步,FlutterView增加第一帧显示回调

当Flutter引擎、绘制工具等准备完成,并完成了FlutterView的第一帧绘制则会回调,这个回调后面就有很大用处。

第三步,初始化FlutterSplashView

这里开始就是启动页的初始化。以下是启动页的核心,后面一起讲。
由于flutterView是作为flutterSplashView的一个参数,所以flutterSplashView是持有了flutterView。

flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());
  • 1

第四步,FlutterView与FlutterEngine绑定在一起

此时,Flutter的框架Embedder和Engine层就关联在一起,FlutterEngine将绘制的ui反映到FlutterView上。

第五步,返回一个flutterSplashView

将flutterSplashView返回给setContentView,作为显示内容,也就是屏幕上看到的界面。

flutterSplashView.displayFlutterViewWithSplash

final class FlutterSplashView extends FrameLayout {
}
  • 1
  • 2

首先可以看到,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。
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25

第一步,添加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);
  }
  • 1
  • 2
  • 3
 private final Runnable onTransitionComplete =
      new Runnable() {
        @Override
        public void run() {
          // 关键
          removeView(splashScreenView);
          previousCompletedSplashIsolate = transitioningIsolateId;
        }
      };
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

也就是说,只要控制了onTransitionComplete.run()的调用时机,就能决定关闭的时机。

总结

  1. 该流程主要分析了原生端的启动流程,Engine和Embedder层如何关联在一起,并提供了开启dart运行环境。
  2. FlutterActivityAndFragmentDelegate代理作为Activity和Fragment的统一操作。

参考

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/144734
推荐阅读