Flutter应用启动流程分析(二)
看看Flutter页面是怎么展示出来的
MainActivity
在AndroidManifest中的声明
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<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>
只是声明了一个meta-data,指定是否展示加载画面,也就是应用打开时的等待画面。接着看MainActivity的代码实现
class MainActivity: FlutterActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
GeneratedPluginRegistrant.registerWith(this)
}
}
乖乖,继承了FlutterActivity,复写了onCreate方法就添加了一行代码,看名称貌似是注册了什么,进去瞅瞅,嘟嘟嘟…
/**
* Generated file. Do not edit.
*/
public final class GeneratedPluginRegistrant {
public static void registerWith(PluginRegistry registry) {
if (alreadyRegisteredWith(registry)) {
return;
}
}
private static boolean alreadyRegisteredWith(PluginRegistry registry) {
final String key = GeneratedPluginRegistrant.class.getCanonicalName();
if (registry.hasPlugin(key)) {
return true;
}
registry.registrarFor(key);
return false;
}
}
Flutter应用会默认自动生成GeneratedPluginRegistrant类,负责管理在pubspec文件中依赖插件注册于更新。 调用了PluginRegistry的registrarFor方法进行插件的注册。plugin可以提供与当前原生平台交互的能力。返回去看看FlutterActivity
FlutterActivity
代码不是很多,都贴出来看看吧
/**
* Base class for activities that use Flutter.
*/
public class FlutterActivity extends Activity implements FlutterView.Provider, PluginRegistry, ViewFactory {
private static final String TAG = "FlutterActivity";
private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);
// These aliases ensure that the methods we forward to the delegate adhere
// to relevant interfaces versus just existing in FlutterActivityDelegate.
private final FlutterActivityEvents eventDelegate = delegate;
private final FlutterView.Provider viewProvider = delegate;
private final PluginRegistry pluginRegistry = delegate;
/**
* Returns the Flutter view used by this activity; will be null before
* {@link #onCreate(Bundle)} is called.
*/
@Override
public FlutterView getFlutterView() {
return viewProvider.getFlutterView();
}
/**
* Hook for subclasses to customize the creation of the
* {@code FlutterView}.
*
* <p>The default implementation returns {@code null}, which will cause the
* activity to use a newly instantiated full-screen view.</p>
*/
@Override
public FlutterView createFlutterView(Context context) {
return null;
}
/**
* Hook for subclasses to customize the creation of the
* {@code FlutterNativeView}.
*
* <p>The default implementation returns {@code null}, which will cause the
* activity to use a newly instantiated native view object.</p>
*/
@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}
@Override
public boolean retainFlutterNativeView() {
return false;
}
@Override
public final boolean hasPlugin(String key) {
return pluginRegistry.hasPlugin(key);
}
@Override
public final <T> T valuePublishedByPlugin(String pluginKey) {
return pluginRegistry.valuePublishedByPlugin(pluginKey);
}
@Override
public final Registrar registrarFor(String pluginKey) {
return pluginRegistry.registrarFor(pluginKey);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
eventDelegate.onCreate(savedInstanceState);
}
@Override
protected void onStart() {
super.onStart();
eventDelegate.onStart();
}
@Override
protected void onResume() {
super.onResume();
eventDelegate.onResume();
}
@Override
protected void onDestroy() {
eventDelegate.onDestroy();
super.onDestroy();
}
@Override
public void onBackPressed() {
if (!eventDelegate.onBackPressed()) {
super.onBackPressed();
}
}
@Override
protected void onStop() {
eventDelegate.onStop();
super.onStop();
}
@Override
protected void onPause() {
super.onPause();
eventDelegate.onPause();
}
@Override
protected void onPostResume() {
super.onPostResume();
eventDelegate.onPostResume();
}
// @Override - added in API level 23
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
eventDelegate.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (!eventDelegate.onActivityResult(requestCode, resultCode, data)) {
super.onActivityResult(requestCode, resultCode, data);
}
}
@Override
protected void onNewIntent(Intent intent) {
eventDelegate.onNewIntent(intent);
}
@Override
public void onUserLeaveHint() {
eventDelegate.onUserLeaveHint();
}
@Override
public void onTrimMemory(int level) {
eventDelegate.onTrimMemory(level);
}
@Override
public void onLowMemory() {
eventDelegate.onLowMemory();
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
eventDelegate.onConfigurationChanged(newConfig);
}
}
看代码并没有实现什么具体的功能, 与FlutterActivityDelegate之间就一个代理模式的实现。继承了Activity并实现了FlutterView.Provider、PluginRegistry、ViewFactory三个接口,内部有一个FlutterActivityDelegate对象,也是最终干活的苦力,其他的三个成员变量也都被设置为了delegate对象。这样实现我们也可以不用继承FlutterActivity写自己的Activity依样画葫芦的把FlutterActivityDelegate代理类挪过来就可以了,哈哈哈…下面我们看看FlutterActivity实现的这几个接口都是什么鬼:
FlutterView.Provider
/**
* Interface for those objects that maintain and expose a reference to a
* {@code FlutterView} (such as a full-screen Flutter activity).
*
* <p>
* This indirection is provided to support applications that use an activity
* other than {@link io.flutter.app.FlutterActivity} (e.g. Android v4 support
* library's {@code FragmentActivity}). It allows Flutter plugins to deal in
* this interface and not require that the activity be a subclass of
* {@code FlutterActivity}.
* </p>
*/
public interface Provider {
/**
* Returns a reference to the Flutter view maintained by this object. This may
* be {@code null}.
*/
FlutterView getFlutterView();
}
FlutterView的内部定义的接口,返回一个FlutterView,FlutterView是负责展示Dart UI的容器。
PluginRegistry
注册用于原生平台交互的插件。
public interface PluginRegistry {
Registrar registrarFor(String pluginKey);
boolean hasPlugin(String pluginKey);
<T> T valuePublishedByPlugin(String pluginKey);
interface Registrar {
Activity activity();
Context context();
Context activeContext();
BinaryMessenger messenger();
TextureRegistry textures();
PlatformViewRegistry platformViewRegistry();
FlutterView view();
String lookupKeyForAsset(String asset);
String lookupKeyForAsset(String asset, String packageName);
Registrar publish(Object value);
Registrar addRequestPermissionsResultListener(RequestPermissionsResultListener listener);
Registrar addActivityResultListener(ActivityResultListener listener);
Registrar addNewIntentListener(NewIntentListener listener);
Registrar addUserLeaveHintListener(UserLeaveHintListener listener);
Registrar addViewDestroyListener(ViewDestroyListener listener);
}
interface RequestPermissionsResultListener {
boolean onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults);
}
interface ActivityResultListener {
boolean onActivityResult(int requestCode, int resultCode, Intent data);
}
interface NewIntentListener {
boolean onNewIntent(Intent intent);
}
interface UserLeaveHintListener {
void onUserLeaveHint();
}
interface ViewDestroyListener {
boolean onViewDestroy(FlutterNativeView view);
}
interface PluginRegistrantCallback {
void registerWith(PluginRegistry registry);
}
}
内部有7个接口,现在只关注FlutterActivity所要实现的三个方法:
registrarFor:注册插件并返回这个插件的Registrar;
hasPlugin:指定的插件名称是否已经存在;
valuePublishedByPlugin:从已注册的插件集合中获取指定的插件。
ViewFactory
/**
* Specifies the mechanism by which Flutter views are created during the
* operation of a {@code FlutterActivityDelegate}.
*
* <p>A delegate's view factory will be consulted during
* {@link #onCreate(Bundle)}. If it returns {@code null}, then the delegate
* will fall back to instantiating a new full-screen {@code FlutterView}.</p>
*
* <p>A delegate's native view factory will be consulted during
* {@link #onCreate(Bundle)}. If it returns {@code null}, then the delegate
* will fall back to instantiating a new {@code FlutterNativeView}. This is
* useful for applications to override to reuse the FlutterNativeView held
* e.g. by a pre-existing background service.</p>
*/
public interface ViewFactory {
FlutterView createFlutterView(Context context);
FlutterNativeView createFlutterNativeView();
/**
* Hook for subclasses to indicate that the {@code FlutterNativeView}
* returned by {@link #createFlutterNativeView()} should not be destroyed
* when this activity is destroyed.
*/
boolean retainFlutterNativeView();
}
FlutterActivityDelegate类中定义的接口,用来创建FlutterView和FlutterNativeView的,最后一个方法是用于控制是否在页面销毁时同步销毁FlutterNativeView。这三个方法在FlutterActivity的实现中均是返回的默认值。
private final FlutterActivityEvents eventDelegate = delegate;
private final FlutterView.Provider viewProvider = delegate;
private final PluginRegistry pluginRegistry = delegate;
三个成员变量实际上都指向了delegate,最终FlutterActivity中所有的调用都会调用到FlutterActivityDelegate中。
FlutterActivityDelegate
呀!现在找到真正干事情的老实人了,我们来简单看一下他的属性和构造方法:
public final class FlutterActivityDelegate
implements FlutterActivityEvents,
FlutterView.Provider,
PluginRegistry {
private final Activity activity;
private final ViewFactory viewFactory;
private FlutterView flutterView;
private View launchView;
public FlutterActivityDelegate(Activity activity, ViewFactory viewFactory) {
this.activity = Preconditions.checkNotNull(activity);
this.viewFactory = Preconditions.checkNotNull(viewFactory);
}
}
viewFactory:这个也就是FlutterActivity,创建FlutterActivityDelegate时传入的。
flutterView:当前Activity中所要显示的View。
launchView:加载Flutter时等待界面,这个View会根据在AndroidManifest中的配置来控制是否展示。
与FlutterActivity不同的是它实现了FlutterActivityEvents:
public interface FlutterActivityEvents
extends ComponentCallbacks2,
ActivityResultListener,
RequestPermissionsResultListener {
/**
* @see android.app.Activity#onCreate(android.os.Bundle)
*/
void onCreate(Bundle savedInstanceState);
/**
* @see android.app.Activity#onNewIntent(Intent)
*/
void onNewIntent(Intent intent);
/**
* @see android.app.Activity#onPause()
*/
void onPause();
/**
* @see android.app.Activity#onStart()
*/
void onStart();
/**
* @see android.app.Activity#onResume()
*/
void onResume();
/**
* @see android.app.Activity#onPostResume()
*/
void onPostResume();
/**
* @see android.app.Activity#onDestroy()
*/
void onDestroy();
/**
* @see android.app.Activity#onStop()
*/
void onStop();
/**
* Invoked when the activity has detected the user's press of the back key.
*
* @return {@code true} if the listener handled the event; {@code false}
* to let the activity continue with its default back button handling.
* @see android.app.Activity#onBackPressed()
*/
boolean onBackPressed();
/**
* @see android.app.Activity#onUserLeaveHint()
*/
void onUserLeaveHint();
}
FlutterActivityEvents定义了与Activity生命周期和一些特定交互相关的方法,便于实现代理模式。
回到FlutterActivityDelegate中,在FlutterActivity创建是首先会走onCreate方法,因FlutterActivityDelegate是FlutterActivity的代理实现,我们定位到这里
@Override
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(activity.getIntent());
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);
flutterView = viewFactory.createFlutterView(activity);
if (flutterView == null) {
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
flutterView = new FlutterView(activity, null, nativeView);
flutterView.setLayoutParams(matchParent);
activity.setContentView(flutterView);
launchView = createLaunchView();
if (launchView != null) {
addLaunchView();
}
}
if (loadIntent(activity.getIntent())) {
return;
}
String appBundlePath = FlutterMain.findAppBundlePath();
if (appBundlePath != null) {
runBundle(appBundlePath);
}
}
onCreate方法做了以下事情:
1、根据sdk版本设置状态条的样式;
2、确认Flutter engine初始化,没有,初始化Flutter engine;
3、创建FlutterView;
4、添加FlutterView到页面中(展示Dart UI);
5、创建launchView;
6、launchView不为null,添加到页面中,在FlutterView渲染完成第一帧时移除;
7、加载Dart页面
step 2 初始化Flutter Engine:FlutterMain ensureInitializationComplete方法最终调用了FlutterLoader的ensureInitializationComplete方法
public void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
if (initialized) {
return;
}
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
}
if (settings == null) {
throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
}
try {
if (resourceExtractor != null) {
resourceExtractor.waitForCompletion();
}
List<String> shellArgs = new ArrayList<>();
shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
ApplicationInfo applicationInfo = getApplicationInfo(applicationContext);
shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + DEFAULT_LIBRARY);
if (args != null) {
Collections.addAll(shellArgs, args);
}
String kernelPath = null;
if (BuildConfig.DEBUG || BuildConfig.JIT_RELEASE) {
String snapshotAssetPath = PathUtils.getDataDirectory(applicationContext) + File.separator + flutterAssetsDir;
kernelPath = snapshotAssetPath + File.separator + DEFAULT_KERNEL_BLOB;
shellArgs.add("--" + SNAPSHOT_ASSET_PATH_KEY + "=" + snapshotAssetPath);
shellArgs.add("--" + VM_SNAPSHOT_DATA_KEY + "=" + vmSnapshotData);
shellArgs.add("--" + ISOLATE_SNAPSHOT_DATA_KEY + "=" + isolateSnapshotData);
} else {
shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + aotSharedLibraryName);
// Most devices can load the AOT shared library based on the library name
// with no directory path. Provide a fully qualified path to the library
// as a workaround for devices where that fails.
shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + applicationInfo.nativeLibraryDir + File.separator + aotSharedLibraryName);
}
shellArgs.add("--cache-dir-path=" + PathUtils.getCacheDirectory(applicationContext));
if (settings.getLogTag() != null) {
shellArgs.add("--log-tag=" + settings.getLogTag());
}
String appStoragePath = PathUtils.getFilesDir(applicationContext);
String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
FlutterJNI.nativeInit(applicationContext, shellArgs.toArray(new String[0]),
kernelPath, appStoragePath, engineCachesPath);
initialized = true;
} catch (Exception e) {
Log.e(TAG, "Flutter initialization failed.", e);
throw new RuntimeException(e);
}
}
确保Flutter已经初始化完成,前面介绍打开应用时在Debug模式下Flutter会异步进行资源解压缩操作,resourceExtractor.waitForCompletion()保证前面的异步任务执行完毕才能往下执行。当任务完成后,配置一些参数调用FlutterJNI.nativeInit(applicationContext, shellArgs.toArray(new String[0]),
kernelPath, appStoragePath, engineCachesPath)Native方法来完成C++层的Flutter Engine的初始化。上面代码看到在DEBUG、JIT_RELEASE和RELEASE配置参数不同,我们先只关注RELEASE模式,配置了AOT编译模式下需要的参数:
1、也就是flutter应用编译生成的so库名称(这个是可在Manifest中配置的,默认名称为libapp.so);
2、还有so库所在的绝对路径,便于底层engine加载。
再就是向底层engine传入了三个应用路径:
kernelPath :在RELEASE下这个路径 为null;
appStoragePath:/data/user/0/package/files
engineCachesPath:/data/user/0/package/code_cache
step 3、4 创建FlutterView
flutterView = viewFactory.createFlutterView(activity);
if (flutterView == null) {
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
flutterView = new FlutterView(activity, null, nativeView);
flutterView.setLayoutParams(matchParent);
activity.setContentView(flutterView);
launchView = createLaunchView();
if (launchView != null) {
addLaunchView();
}
}
FlutterView为Dart UI的跟布局,会优先从ViewFactory的createFlutterView方法中获取,这里的viewFactory就是FlutterActivity,方法没有实现默认返回null,所以一定会进入下面的判断,在下面代码中也是会调用viewFactory.createFlutterNativeView优先获取FlutterNativeView,同样也是返回了null,接着new了个FlutterView并设置为全屏显示添加到Activity中。
看看FlutterView的创建:卧槽进去了一下比较复杂些,先略过后面在单个分析,我们现在只要知道它继承自SurfaceView并实现了BinaryMessenger接口(原生平台与Flutter通讯的消息)和TextureRegistry接口(待了解)。好吧我们回来接着往下看。
step 5 创建launchView
默认会创建一个launchView用于在加载Flutter页面时展示的等待画面,会获取当前Activity配置的WindowBackground Drawable资源,为null则不创建。
private View createLaunchView() {
if (!showSplashScreenUntilFirstFrame()) {
return null;
}
final Drawable launchScreenDrawable = getLaunchScreenDrawableFromActivityTheme();
if (launchScreenDrawable == null) {
return null;
}
final View view = new View(activity);
view.setLayoutParams(matchParent);
view.setBackground(launchScreenDrawable);
return view;
}
private Drawable getLaunchScreenDrawableFromActivityTheme() {
TypedValue typedValue = new TypedValue();
if (!activity.getTheme().resolveAttribute(
android.R.attr.windowBackground,
typedValue,
true)) {
return null;
}
if (typedValue.resourceId == 0) {
return null;
}
try {
return activity.getResources().getDrawable(typedValue.resourceId);
} catch (NotFoundException e) {
Log.e(TAG, "Referenced launch screen windowBackground resource does not exist");
return null;
}
}
step 6 添加launchView
在以上执行创建launchView成功后会将launchView添加至Activity中,同样也是设置为全屏,这时launchView会覆盖在FlutterView的上层,这样在加载Flutter时看到的是launchView。
private void addLaunchView() {
if (launchView == null) {
return;
}
activity.addContentView(launchView, matchParent);
flutterView.addFirstFrameListener(new FlutterView.FirstFrameListener() {
@Override
public void onFirstFrame() {
FlutterActivityDelegate.this.launchView.animate()
.alpha(0f)
// Use Android's default animation duration.
.setListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// Views added to an Activity's addContentView is always added to its
// root FrameLayout.
((ViewGroup) FlutterActivityDelegate.this.launchView.getParent())
.removeView(FlutterActivityDelegate.this.launchView);
FlutterActivityDelegate.this.launchView = null;
}
});
FlutterActivityDelegate.this.flutterView.removeFirstFrameListener(this);
}
});
// Resets the activity theme from the one containing the launch screen in the window
// background to a blank one since the launch screen is now in a view in front of the
// FlutterView.
//
// We can make this configurable if users want it.
activity.setTheme(android.R.style.Theme_Black_NoTitleBar);
}
添加launchView时同时注册了Flutter渲染第一帧的回调,在收到回调后移除launchView让FlutterView得以显示。
step 7 加载Dart页面
private boolean loadIntent(Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_RUN.equals(action)) {
String route = intent.getStringExtra("route");
String appBundlePath = intent.getDataString();
if (appBundlePath == null) {
// Fall back to the installation path if no bundle path was specified.
appBundlePath = FlutterMain.findAppBundlePath();
}
if (route != null) {
flutterView.setInitialRoute(route);
}
runBundle(appBundlePath);
return true;
}
return false;
}
这里可以指定Flutter Bundle的文件路径和route参数,这个route参数最终在加载Dart UI入口中带入window.defaultRouteName获取,
例:void main() => runApp(_widgetForRoute(window.defaultRouteName));
Flutter Bundle文件路径现在有点模糊后续再看(因在上边的分析中我们知道在DEBUG模式中是存在资源文件解压到应用文件中的可以理解通,但在RELEASE中没有资源解压缩的操作,只有个libapp.so文件,而这个路径也在启动Flutter engine中传入了,所以就有点模糊了,以后再看…)。
以上可知两个参数的传入只有在Intent action等于Intent.ACTION_RUN的情况下可用,否则不执行任何操作,直接返回false。但最终都会执行到runBundle(String appBundlePath)方法
private void runBundle(String appBundlePath) {
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
FlutterRunArguments args = new FlutterRunArguments();
args.bundlePath = appBundlePath;
args.entrypoint = "main";
flutterView.runFromBundle(args);
}
}
首先会判断当前的FlutterView应用是否已经在运行,如果已经运行就不会重新执行。没有运行会构建运行参数,entrypoint为dart应用的入口函数名,调用FlutterView的runFromBundle来运行。
再看看FlutterActivityDelegate的其他方法,都是Activity的生命周期方法调用,最终都通知到了FlutterView。
通过这篇分析了解到FlutterActivity只是一个空壳,我们可以自定义自己的实现,真正干活的是FlutterActivityDelegate。加载运行flutter应用的是FlutterView,而FlutterView通过FlutterActivityDelegate响应了Activity的生命周期。最终的核心在FlutterView。