A recent performance optimization found that in mixed stack development, the time to start a Flutter page for the first time was almost twice as long as the time to start a Flutter page for the second time, which made it uncomfortable. Based on the analysis, I found that the first time a Flutter page is started, some initialization work is done. In this way, I reviewed the Flutter initialization process.
Flutter initialization consists of four parts: FlutterMain initialization, FlutterNativeView initialization, FlutterView initialization and Flutter Bundle initialization. Let’s take a look at the sequence diagram of Flutter initialization to get an overall idea of the general process of Flutter initialization:
FlutterMain initialization
This part of the initialization is started by a call to the application. onCreate method. This is done when the Application is created. This will not affect the first startup of the Flutter page, so this is just a brief analysis. From FlutterMain. StartInitialization method in the code can easily see that initializes the main points in four parts. The first three parts are similar, namely, initializing configuration information, initializing AOT compilation, and initializing resources. The last part is loading the Native environment of the Flutter. For those who are interested in fluttermain. Java, the logic is clear.
public static void startInitialization(Context applicationContext, Settings settings) {
// other codes ...
initConfig(applicationContext);
initAot(applicationContext);
initResources(applicationContext);
System.loadLibrary("flutter");
// other codes ...
}
Copy the code
FlutterNativeView initialization
The call stack of the FlutterNativeView constructor is shown as follows:
From the call stack in the figure above, we know what the initialization of FlutterNativeView mainly does. From the perspective of source code, we have a deeper understanding: the constructor of FlutterNativeView finally mainly calls an nativeAttach method. At this point, we need to analyze the engine layer code. We can find the corresponding JNI method call in the JNI file. (Platform_view_android_jni.cc)
static const JNINativeMethod native_view_methods[] = {
{
.name = "nativeAttach",
.signature = "(Lio/flutter/view/FlutterNativeView;) J",
.fnPtr = reinterpret_cast<void*>(&shell::Attach),
},
// other codes ...
};
Copy the code
It’s easy to see from the code that the FlutternativeView. attach method finally calls the shell:: attach method, and the shell::Attach method does two things: 1. Create PlatformViewAndroid. 2. Call PlatformViewAndroid: : Attach.
static jlong Attach(JNIEnv* env, jclass clazz, jobject flutterView) {
auto view = new PlatformViewAndroid();
// other codes ...
view->Attach();
// other codes ...
}
Copy the code
So what does the PlatformViewAndroid constructor and Attach method do?
PlatformViewAndroid::PlatformViewAndroid()
: PlatformView(std::make_unique<NullRasterizer>()),
android_surface_(InitializePlatformSurface()) {}
void PlatformViewAndroid::Attach() {
CreateEngine();
// Eagerly setup the IO thread context. We have already setup the surface.
SetupResourceContextOnIOThread();
UpdateThreadPriorities();
}
Copy the code
Among them: 1. PlatformViewAndroid main is to call the constructor InitializePlatformSurface method, this method is to initialize the Surface, the Surface has Vulkan, OpenGL and Software three types of distinction. 2. PlatformViewAndroid: : Attach method here call three main methods: CreateEngine, SetupResourceContextOnIOThread and UpdateThreadPriorities. Create an Engine object. Create an Engine object. 2.2 SetupResourceContextOnIOThread is in the context of the IO thread to prepare resources logic. 2.3 UpdateThreadPriorities Is set to thread priorities. This setting sets the GPU thread priorities to -2 and the UI thread priorities to -1.
FlutterView initialization
FlutterView initialization is pure Android layer, so it’s relatively simple. Analysis of the constructor of Flutterview.java reveals that the initialization of the entire FlutterView does two things besides ensuring the successful creation of the FlutterNativeView and some necessary view Settings: 1. Register a SurfaceHolder listener where the surfaceCreated callback is used as the first frame callback of a Flutter. 2. Initialize a series of bridge methods needed by the Flutter system. For example, localization, Navigation, Keyevent, System, Settings, platform, and Textinput. The FlutterView initialization process is mainly shown in the figure below:
Initialize the Flutter Bundle
Flutter Bundle initialization is by calling FlutterActivityDelegate runFlutterBundle started, first with a graph to illustrate the call stack of runFlutterBundle method:
FlutterActivity’s onCreate method calls Its runFlutterBundle method after executing FlutterActivityDelegate’s onCreate method. The runFlutterBundle code looks like this:
public void runFlutterBundle(){
// other codes ...
String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
if (appBundlePath ! = null) {
flutterView.runFromBundle(appBundlePath, null, "main", reuseIsolate);
}
}
Copy the code
It is obvious that the runFlutterBundle didn’t do too many things, and direct call FlutterView. RunFromBundle method. Then around for the last call to PlatformViewAndroid: : RunBundleAndSnapshot method.
void PlatformViewAndroid::RunBundleAndSnapshot(JNIEnv* env, std::string bundle_path,
std::string snapshot_override,
std::string entrypoint,
bool reuse_runtime_controller,
jobject assetManager) {
// other codes ...
blink::Threads::UI()->PostTask(
[engine = engine_->GetWeakPtr(),
asset_provider = std::move(asset_provider),
bundle_path = std::move(bundle_path), entrypoint = std::move(entrypoint),
reuse_runtime_controller = reuse_runtime_controller] {
if (engine)
engine->RunBundleWithAssets(
std::move(asset_provider), std::move(bundle_path),
std::move(entrypoint), reuse_runtime_controller);
});
}
Copy the code
PlatformViewAndroid: : RunBundleAndSnapshot UI thread invokes the Engine: : RunBundleWithAssets, eventually call Engine: : DoRunBundle. Finally will only call RunFromPrecompiledSnapshot, RunFromKernel DoRunBundle method and RunFromScriptSnapshot one of the three methods. All three methods end up calling the SendStartMessage method.
-
bool DartController:: SendStartMessage(Dart_Handle root_library,
-
const std:: string& entrypoint ) {
-
// other codes ...
-
-
// Get the closure of main().
-
Dart_Handle main_closure = Dart_GetClosure(
-
root_library , Dart_NewStringFromCString (entrypoint. c_str()));
-
-
// other codes ...
-
-
// Grab the 'dart:isolate' library.
-
Dart_Handle isolate_lib = Dart_LookupLibrary( ToDart("dart:isolate" ));
-
DART_CHECK_VALID (isolate_lib);
-
-
// Send the start message containing the entry point by calling
-
// _startMainIsolate in dart:isolate.
-
const intptr_t kNumIsolateArgs = 2;
-
Dart_Handle isolate_args[ kNumIsolateArgs];
-
isolate_args [0] = main_closure ;
-
isolate_args [1] = Dart_Null();
-
Dart_Handle result = Dart_Invoke( isolate_lib, ToDart("_startMainIsolate" ),
-
kNumIsolateArgs , isolate_args);
-
return LogIfError (result);
-
}
Copy the code
The SendStartMessage method does three things: 1. Get the closure of the Flutter entry method (for example, the main method). 2. Obtain FlutterLibrary. 3. Send a message to invoke the entry method of Flutter.
This paper mainly analyzes the part of Flutter initialization logic in the onCreate method of FlutterActivity. Obviously, it can be found that the main time is the initialization of FlutterNativeView, FlutterView and Flutter Bundle. The problems raised in the introduction can be easily solved by preloading the initialization of these three parts. Tests found that after this change, the first Flutter page startup time was about the same as the subsequent ones. For FlutterMain startInitialization initialization logic, SendStartMessage messages sent to ultimately call no further analysis method of entries in the Flutter logic, subsequent to an article in the analysis further share the content.
Pay attention to two-dimensional code, forward-looking technology is in control