preface
The common startup mode currently used is:
Static page -> GIF -> App home pageCopy the code
The reasons for this design are as follows:
1. Product requirements, such as advertising
2. Brand display
3. The startup time is longer when the application is large, and the transition effect of a picture is better than that of a white screen
And so on… .
Because the engine creation and initialization of Flutter take some time, it also provides a transition scheme (default is white screen), located in:
Under the androidmanifest.xml < meta - data android: name = "IO. Flutter. Embedding. Android. SplashScreenDrawable" android:resource="@drawable/launch_background" />Copy the code
The drawable/launch_background.xml file under RES
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@color/red" /> <! -- You can insert your own image assets here --> <! -- <item> <bitmap android:gravity="center" android:src="@mipmap/launch_image" /> </item> --> </layer-list>Copy the code
Here we can set up the page shown before engine initialization. Let’s take a look at the process.
Start the page loading process
We only analyze the loading process of the startup page here, engine startup and other processes will be skipped.Copy the code
MainActivity
The related initialization of the whole flutter engine starts in the onCreate method:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { switchLaunchThemeForNormalTheme(); super.onCreate(savedInstanceState); lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_CREATE); delegate = new FlutterActivityAndFragmentDelegate(this); // create a delegate.onAttach(this); / / / framework for plug-in, restore state delegate onActivityCreated (savedInstanceState); / / / set the background transparent window, hide the status bar configureWindowForTransparency (); SetContentView (createFlutterView()); configureStatusBarForFullscreenFlutterExperience(); }Copy the code
SetContentView is pretty familiar, so let’s go straight to createFlutterView () :
@NonNull
private View createFlutterView() {
return delegate.onCreateView(
null /* inflater */, null /* container */, null /* savedInstanceState */);
}
Copy the code
FlutterActivityAndFragmentDelegate
The initialization, startup, and other operations of a flutter are delegated to it.Copy the code
So let’s move on to onCreateView, and I’m going to comment it out in the code
@NonNull View onCreateView( LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { Log.v(TAG, "Creating FlutterView."); ensureAlive(); If (host.getRenderMode() == rendermode.surface) {// apply a flutter to the surface, FlutterSurfaceView FlutterSurfaceView = new FlutterSurfaceView(host.getActivity(), host.getTransparencyMode() == TransparencyMode.transparent); // Allow our host to customize FlutterSurfaceView, if desired. host.onFlutterSurfaceViewCreated(flutterSurfaceView); Create the FlutterView that owns the FlutterSurfaceView. Create the FlutterView that owns the FlutterSurfaceView new FlutterView(host.getActivity(), flutterSurfaceView); } else { FlutterTextureView flutterTextureView = new FlutterTextureView(host.getActivity()); // Allow our host to customize FlutterSurfaceView, if desired. host.onFlutterTextureViewCreated(flutterTextureView); // Create the FlutterView that owns the FlutterTextureView. flutterView = new FlutterView(host.getActivity(), flutterTextureView); } // Add listener to be notified when Flutter renders its first frame. flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener); View-fluttersplashview FlutterSplashView = new FlutterSplashView(host.getContext()); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { flutterSplashView.setId(View.generateViewId()); } else { // TODO(mattcarroll): Find a better solution to this ID. This is a random, static ID. // It might conflict with other Views, and it means that only a single FlutterSplashView // can exist in a View hierarchy at one time. flutterSplashView.setId(486947586); } / / / show the splash screen page flutterSplashView displayFlutterViewWithSplash (flutterView, host provideSplashScreen ()); Log.v(TAG, "Attaching FlutterEngine to FlutterView."); / / / created surface binding to the engine flutterView. AttachToFlutterEngine (flutterEngine); return flutterSplashView; }Copy the code
Here we can see that a FlutterSurfaceView is created which inherits from the surfaceView(our Flutter page is also rendered on this surface). Then we use it to initialize a FlutterView,
FlutterView inherits from FrameLayoutCopy the code
Then we’ll create a FlutterSplashView displayFlutterViewWithSplash FrameLayout (inheritance) and call () method.
public void displayFlutterViewWithSplash( @NonNull FlutterView flutterView, @Nullable SplashScreen splashScreen) { // If we were displaying a previous FlutterView, remove it. if (this.flutterView ! = null) { this.flutterView.removeOnFirstFrameRenderedListener(flutterUiDisplayListener); removeView(this.flutterView); } // If we were displaying a previous splash screen View, remove it. if (splashScreenView ! = null) { removeView(splashScreenView); } // Display the new FlutterView. this.flutterView = flutterView; // add flutterView addView(flutterView); this.splashScreen = splashScreen; // Display the new splash screen, if needed. if (splashScreen ! = null) { if (isSplashScreenNeededNow()) { Log.v(TAG, "Showing splash screen UI."); // This is the typical case. A FlutterEngine is attached to the FlutterView and we're // waiting for the first frame to render. Show a splash UI until that happens. splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState); /// add splashScreenView addView(this.splashScreenView); flutterView.addOnFirstFrameRenderedListener(flutterUiDisplayListener); } else if (isSplashScreenTransitionNeededNow()) { Log.v( TAG, "Showing an immediate splash transition to Flutter due to previously interrupted transition."); splashScreenView = splashScreen.createSplashView(getContext(), splashScreenState); addView(splashScreenView); transitionToFlutter(); } else if (! flutterView.isAttachedToFlutterEngine()) { Log.v( TAG, "FlutterView is not yet attached to a FlutterEngine. Showing nothing until a FlutterEngine is attached."); flutterView.addFlutterEngineAttachmentListener(flutterEngineAttachmentListener); }}}Copy the code
This method saves the flutterView (forget that), and then we save an implementation class for the interface, splashScreen, which is provided by the FlutterActivity implementation (MainActivity) :
///host is an interface implemented by FlutterActivity. flutterSplashView.displayFlutterViewWithSplash(flutterView, host.provideSplashScreen());Copy the code
public SplashScreen provideSplashScreen() { Drawable manifestSplashDrawable = getSplashScreenFromManifest(); if (manifestSplashDrawable ! Return New DrawableSplashScreen(manifestSplashDrawable); } else { return null; }}Copy the code
Through getSplashScreenFromManifest initialize a drawable, we look at the inside it:
@Nullable @SuppressWarnings("deprecation") private Drawable getSplashScreenFromManifest() { try { ActivityInfo activityInfo = getPackageManager().getActivityInfo(getComponentName(), PackageManager.GET_META_DATA); Bundle metadata = activityInfo.metaData; ///SPLASH_SCREEN_META_DATA_KEY = metadata! = null ? metadata.getInt(SPLASH_SCREEN_META_DATA_KEY) : 0; return splashScreenId ! = 0? Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP ? getResources().getDrawable(splashScreenId, getTheme()) : getResources().getDrawable(splashScreenId) : null; } catch (PackageManager.NameNotFoundException e) { // This is never expected to happen. return null; }}Copy the code
static final String SPLASH_SCREEN_META_DATA_KEY =
"io.flutter.embedding.android.SplashScreenDrawable";
Copy the code
As you can see, with the above method we initialize a drawable with the launch resource set in androidmanifest.xml, which in turn initializes the DrawableSplashScreen and returns it.
We went back to displayFlutterViewWithSplash continue to look down,
/ / / a view through our previous drawable splashScreenView = splashScreen. CreateSplashView (getContext (), splashScreenState); /// add splashScreenView addView(this.splashScreenView);Copy the code
At this point, the whole flash page process is over.
Looking back, you can see that there is a long process between the display of the flutter page and the start of the engine and the display of the flutter page, and the flutter page is not removed until the flutter page is displayed.
So how do you optimize? Here is my plan.
Start page optimization solution
Since the flutter is in two different surfaces from the native one, we can consider displaying the start page using the native surface. In order to avoid slowing down the start of the flutter, we can do this in the child thread. ww
Case we show a svga animation plug-in USES: 'com. Making. Yyued: SVGAPlayer - Android: 2.5.12'Copy the code
implementation
To get started, we’ll call it initSplashPage() :
public void initSplashPage(){ Executors.newSingleThreadExecutor() .execute(new Runnable() { @RequiresApi(api = Build.version_codes.lollipop) @override public void run() {/// We need a loop Looper. Prepare (); WindowManager wm = (WindowManager)getSystemService(WINDOW_SERVICE); SVGAImageView imageView = new SVGAImageView(getApplicationContext()); imageView.setBackgroundColor(getResources().getColor(R.color.white)); Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case 1111: / / / the start page displayed in the imageView svga animation. SetImageDrawable ((SVGADrawable) MSG. Obj); imageView.startAnimation(); break; }}}; SVGAParser Parser = new SVGAParser(getApplicationContext()); parser.decodeFromAssets("angel.svga", New SVGAParser. ParseCompletion () {@ Override public void the onComplete (SVGAVideoEntity SVGAVideoEntity) {/ / / the plugin switch inside the main thread, SVGADrawable drawable = new SVGADrawable(svgaVideoEntity); Message msg = Message.obtain(); msg.what = 1111; msg.obj = drawable; handler.sendMessage(msg); } @Override public void onError() { } }); WindowManager.LayoutParams params = new WindowManager.LayoutParams(); params.width = wm.getDefaultDisplay().getWidth(); params.height = wm.getDefaultDisplay().getHeight(); Wm. addView(imageView, params); // Remove the flash screen after 6 seconds, New Handler().postdelayed (new Runnable() {@override public void run() {wm.removeView(imageView); }}, 6000); Looper.loop(); }}); }Copy the code
The above is the whole implementation process. Since the parsing callback of SVGA plug-in is in the main thread, a thread switch needs to be carried out through the handler of the child thread, and finally the screen display is carried out through WM.
In addition, I removed the splash page after an additional 6 seconds. Actually, the flutter end should notify the splash page to be removed via channel. I’m writing casually here.
use
We then use it in MainActivity’s onCreate:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { initSplashPage(); /// Flutter related work super.oncreate (savedInstanceState); }Copy the code
The effect is as follows:
In order to see the effect, some additional colors are set: red is the splash screen set in AndroidMainfest. XML and blue is the splash screen shown in the GIF of the Flutter pageCopy the code
At the end of this article, if you have a better plan, you can exchange and discuss, thank you for reading.
Demo
The Demo did some experiments, so the code is messy, heh heh
series
Flutter mimics netease Cloud music App
Flutter version of imitation. Parallax effect of Zhihu list
Flutter — Realize progressive card switching for netease Cloud Music
Flutter imitates flush list of self-selected stocks