欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-03-08 15:47:28
文章目录环境启动页配置环境Flutter 1.22.4Framework • revision 1aafb3a8b9 (4 weeks ago) • 2020-11-13 09:59:28 -0800Engine • revision 2c956a31c0Dart 2.10.4启动页配置向 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

启动页配置

启动流程

Flutter-Android闪屏页(启动页)实现,以及启动流程分析,看这篇文章就够了
分为两个部分,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非常核心的方法。主要有以下几个功能

  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);
}

第一步 初始化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. 首先,从缓存里面读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());
// ...省略
// 以下是注册插件,
this.pluginRegistry =
        new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader);

	// 由于automaticallyRegisterPlugins为false,这里其实并没有关联插件
    if (automaticallyRegisterPlugins) {
      registerPlugins();
    }
  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;
  }

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

第三步 将插件与activity绑定

if (host.shouldAttachEngineToActivity()) {

      flutterEngine
          .getActivityControlSurface()
          .attachToActivity(host.getActivity(), host.getLifecycle());
}
  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);
      }
    }

如果开启了自动装配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