Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan ยท April More text challenge”, click to see the details of the activity.
The three trees of a Flutter, the custom drawing of a Flutter, and the routing of a Flutter are all built on the basis that a Flutter is already running. How does a Flutter work?
The next few articles will cover the preparations for Flutter operation.
The Main method at the beginning of the program
Every application has an entry method. The familiar Java project is the main method of public static void. Similarly, the Flutter application is a simple Dart application, so its entry is also the main method.
The default signature of the main method in Flutter is as follows:
void main() {
runApp(MyApp());
}
Copy the code
It is a synchronous method that takes no arguments and returns no values.
Note here that this method is simply the main signature of the Flutter program.
Dart’s main method supports asynchrony and parameter passing. Let’s look at them separately.
Make the main method asynchronous
The Dart application can write asynchronous code synchronously, which is a set of async\/await keywords. The use of these keywords can be seen here:
The code after the transformation is as follows:
void main() async{
await Future.delayed(Duration(seconds: 3));
print('await 3 seconds');
}
Copy the code
In contrast to the normal main method, the above main method adds the async keyword in the declaration and the await keyword inside the method body.
Print statements are not executed immediately, but wait 3 seconds for execution.
Does Flutter support this syntax? Support!!!! But making main async isn’t much use.
Let the main method support arguments
The Dart mian method also supports adding parameters. The rules are as follows:
- The ordinary arguments in the first position must be of type List array, which can be followed by arguments of other types
- Optional named parameters for the required flag are not allowed and are nullable
Let’s look at them separately:
void main(List<String> args) {
print(args);
}
Copy the code
That’s where you declare it, so how do you use it? Add extra parameters to run the program!
IntelliJ IDEA as an example, in Edit Configuration, set the Program arguments parameter.
The above program receives an array of names and sun
So the console prints out name and Sun.
But Flutter does not support this syntax. Let’s see why it’s not supported. Let’s take Android for example.
Android hosts Flutter and runs FlutterActivity, while Android runs Flutter code and FlutterEngine.
FlutterActivity, like Activity, also has onCreate methods, and its onCreate method also carries out initialization operations.
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
/// The code is omitted
delegate = new FlutterActivityAndFragmentDelegate(this);
delegate.onAttach(this);/ / first place
/// The code is omitted
}
Copy the code
FlutterActivityAndFragmentDelegate is agent, processing FlutterActivity and FlutterFragment general logic.
The first binding is to initialize and bind FlutterEngine.
Initialization is as follows:
void setupFlutterEngine() {
// First, check if the host wants to use a cached FlutterEngine.
String cachedEngineId = host.getCachedEngineId();
///--------
// Second, defer to subclasses for a custom FlutterEngine.
flutterEngine = host.provideFlutterEngine(host.getContext());
///--------
flutterEngine =
new FlutterEngine(
host.getContext(),
host.getFlutterShellArgs().toArray(),
/*automaticallyRegisterPlugins=*/ false./*willProvideRestorationData=*/ host.shouldRestoreAndSaveState());
isFlutterEngineFromHost = false;
}
Copy the code
The process of initializing an Engine is to first fetch it from the cache, then try to generate a user-defined Engine if neither is available, and then actually execute the constructor to generate an Engine if neither is available.
The build engine input parameters are as follows:
-
The first parameter context: in the case of FlutterActivity, that context is the Activity itself, and in the case of FlutterFragment, that context is the Fragment’s host Activity.
-
The second virtual machine parameter: dartVmArgs is the return value of getFlutterShellArgs. The return value is an array of string types. ARG_ENABLE_DART_PROFILING is used to set the Dart VM running parameters, such as ARG_ENABLE_DART_PROFILING, which is the port number of the VM.
To be more specific:
So this is not the main parameter, but the virtual machine configuration to run.
- The third parameter is whether the plugin is automatically registered
pubspec.yaml
Whether plug-ins registered under the file are automatically registered tonative
In engineering, it is generally automatic.
Registration is the registration method that calls the GeneratedPluginRegister.
- The fourth parameter indicates whether the engine will delay initialization in response to some data, if set to
true
, indicating that the engine delays initialization until data is available.
So the onCreate of FlutterActivity constructs something initialized: FlutterEngine, FlutterView, DartExecutor, and so on.
The onStart in FlutterActivity is used to display onStart. The onStart in FlutterActivity invokes the proxy logic. Let’s look at a section of the flow.
private void doInitialFlutterViewRun(a) {
/// omit code
String initialRoute = host.getInitialRoute();
if (initialRoute == null) {
initialRoute = maybeGetInitialRouteFromIntent(host.getActivity().getIntent());
if (initialRoute == null) { initialRoute = DEFAULT_INITIAL_ROUTE; }}// omit the code
DartExecutor.DartEntrypoint entrypoint =
new DartExecutor.DartEntrypoint(
appBundlePathOverride, host.getDartEntrypointFunctionName());
flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint); / / first place
}
Copy the code
Before running the Flutter code, two things are done:
-
The route was initialized. Procedure
-
Execute the Dart entry code
Let’s look at the first one, the first one just has the name of the method and no arguments to the method, right! What’s the name of the method?
public String getDartEntrypointFunctionName(a) {
try{ Bundle metaData = getMetaData(); String desiredDartEntrypoint = metaData ! =null ? metaData.getString(DART_ENTRYPOINT_META_DATA_KEY) : null;
returndesiredDartEntrypoint ! =null ? desiredDartEntrypoint : DEFAULT_DART_ENTRYPOINT;
} catch (PackageManager.NameNotFoundException e) {
returnDEFAULT_DART_ENTRYPOINT; }}Copy the code
The value of IO. Flutter.Entrypoint is main by default
This is why the main method of a Flutter can be executed. There are no parameters for the main method of a Flutter, so the main method of a Flutter does not support setting parameters.
summary
The Dart program’s entry method is the main method, which supports asynchronous, input, and other features. However, due to the particularity of Flutter, asynchronism is not often used. Due to engine limitations, the parameters are not supported.
The main method of Flutter is called in the FlutterActivity or FlutterFragment onStart method and is executed by DartExecutor.
What the Flutter main method does
void main() {
runApp(MyApp());
}
Copy the code
This is the default main method, this is the simplest main, and we’ll probably do a lot of other things in our project, but the core is just one word: runApp.
Let’s look at the logic.
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized() / / first place
..scheduleAttachRootWidget(app) / / in the second place
..scheduleWarmUpFrame(); / / the third place
}
Copy the code
The three pieces of code above are three things: initialize WidgetsFlutterBinding, initialize and bind the three trees, and publish the preheat frame.
These are the three lines of code that run the Flutter project.
WidgetsFlutterBinding initialization
class WidgetsFlutterBinding extends BindingBase with GestureBinding.SchedulerBinding.ServicesBinding.PaintingBinding.SemanticsBinding.RendererBinding.WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance!;
}
}
Copy the code
EnsureInitialized Ensuring initialization is a process of ensuring singletons.
When the ensureInitialized method is first executed, it is initialized using the constructor of BindingBase and its subclasses,
Ensure that the Flutter project completes initialization and only completes once.
Each Binding completes the following initialization:
The Binding of | role |
---|---|
BindingBase | Initializes the base class, specifying the framework for initialization. Initialization is done in initInstances, such as singletons, by initServiceExtensions |
GestureBinding | Initialize gesture recognition and gesture tracking frameworks |
SchedulerBinding | The initialization frame invokes the task |
ServicesBinding | Initialize the plug-in channel, system plug-ins. |
PaintingBinding | Initialize the image cache |
SemanticsBinding | Initialize the semantic framework |
RendererBinding | Initialize the rendering mechanism and the root RenderObject |
WidgetsBinding | Initialize the Element mechanism and Debug display mechanism |
This article covers only the macro process, and the next section details various Binding initializations.
Binding root Widget
RunApp takes a Widget parameter, which is our root Widget.
Let’s look at how this Widget is bound to the root.
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
Copy the code
We just called the attach method. The method is as follows:
void attachRootWidget(Widget rootWidget) {
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]', child: rootWidget, ).attachToRenderTree(buildOwner! , renderViewElementasRenderObjectToWidgetElement<RenderBox>?) ;if (isBootstrapFrame) {
SchedulerBinding.instance!.ensureVisualUpdate();
}
}
Copy the code
The summary is: first determine whether the binding has been done.
According to the renderView and rootWidget to generate a RenderObjectToWidgetAdapter, RenderObjectToWidgetAdapter Element. The relationship between the three trees can be seen here.
Now that the root nodes of all three trees have been generated, the attachToRenderTree method is called to manage the three trees. ๐ Flutter will Know will Series – What exactly are Three Trees
Management is BuildOwner to manage Element. The root Element initiates the three-tree binding process, which calls Element’s mount method. Details of this method can be found at ๐ Element lifecycle.
If not, a SchedulerBinding initiates the frame scheduling and drawing process.
Preheating of the frame
The two lines before scheduleWarmUpFrame complete all the preparation work.
The scheduleWarmUpFrame function is to display the Flutter contents as quickly as possible.
We know that the display of the screen is based on the Vsync signal, as shown in the following image:
Each time Vsync is received, a series of calculations are performed and displayed.
The scheduleWarmUpFrame function is not to wait for the next Vsync, but to initiate a drawing directly.
What do I mean by initiate draw? It’s arranging frames.
void scheduleWarmUpFrame() {
/// Omit code
handleBeginFrame(null);
/// Omit code
handleDrawFrame();
/// Omit code
}
Copy the code
Save some preparation and judgment code, handleBeginFrame and handleDrawFrame are the core code. These two methods are at the heart of the whole frame scheduling, which we will discuss in detail later.
summary
The main method of Flutter completes preliminary preparation, generation and binding of root nodes of three trees, and initiating three tasks of warm-up frame. And the previous process adds up to:
conclusion
The entry to the Flutter program is the main method. The place to call main is determined by the Flutter container. The main method then performs a series of Binding initializations and root Binding tasks. With that groundwork, the next article will look at what Binding is.