文章目录

  • 环境
  • 启动页配置
  • 启动流程
  • 源码分析
    • FlutterActivity
      • switchLaunchThemeForNormalTheme
    • FlutterActivityAndFragmentDelegate
      • delegate.onAttach(this);
        • 第一步 初始化FlutterEngine
        • 第二步 初始化PlatformPlugin系统平台插件
        • 第三步 将插件与activity绑定
        • 第四步 注册第三方插件
    • 启动页核心setContentView(createFlutterView());
      • 第一步,根据渲染模式加载不同FlutterView
      • 第二步,FlutterView增加第一帧显示回调
      • 第三步,初始化FlutterSplashView
      • 第四步,FlutterView与FlutterEngine绑定在一起
      • 第五步,返回一个flutterSplashView
    • flutterSplashView.displayFlutterViewWithSplash
      • 第一步,添加FlutterView
      • 第二步,判断splashScreen是否为空
      • 第三步,实现splashScreenView
      • 第四步,flutterView监听第一帧绘制完成回调

环境

  • 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 应用中添加闪屏页和启动页

启动流程

分为两个部分,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