This article is based on flutter 1.17.5 analysis
I. Initialization of FlutterApplication
FutterApplication and MainActivity are configured in the Androdmanifester. XML of the Flutter project, so let’s look at what happens in FlutterApplication when the application starts.
FlutterApplication->onCreate
FlutterMain.startInitialization
FlutterLoader.getInstance().startInitialization
public void startInitialization(@NonNull Context applicationContext, @NonNull FlutterLoader.Settings settings) {
if (this.settings == null) {
if(Looper.myLooper() ! = Looper.getMainLooper()) {throw new IllegalStateException("startInitialization must be called on the main thread");
} else {
applicationContext = applicationContext.getApplicationContext();
this.settings = settings;
long initStartTimestampMillis = SystemClock.uptimeMillis();
this.initConfig(applicationContext);
this.initResources(applicationContext);
System.loadLibrary("flutter");
VsyncWaiter.getInstance((WindowManager)applicationContext.getSystemService("window")).init();
longinitTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis; FlutterJNI.nativeRecordStartTimestamp(initTimeMillis); }}}Copy the code
The FlutterApplication does the following during initialization:
- InitConfig Indicates the initial configuration
- InitResources Initializes resource information
- System.loadLibrary(“flutter”); Load the Flutter Native library and register the JNI method
- VsyncWaiter. GetInstance ((WindowManager) applicationContext. GetSystemService (” window “)). The init () by WM initialization VsyncWatier
1.1 Initial Configuration
private void initConfig(@NonNull Context applicationContext) {
Bundle metadata = this.getApplicationInfo(applicationContext).metaData;
if(metadata ! =null) {
this.aotSharedLibraryName = metadata.getString(PUBLIC_AOT_SHARED_LIBRARY_NAME, "libapp.so");
this.flutterAssetsDir = metadata.getString(PUBLIC_FLUTTER_ASSETS_DIR_KEY, "flutter_assets");
this.vmSnapshotData = metadata.getString(PUBLIC_VM_SNAPSHOT_DATA_KEY, "vm_snapshot_data");
this.isolateSnapshotData = metadata.getString(PUBLIC_ISOLATE_SNAPSHOT_DATA_KEY, "isolate_snapshot_data"); }}Copy the code
Initialize variables, including the shared library name of AOT, the Asscets directory of FLUTTER, the snapshot data name of VM, and the snapshot data name of ISOLATE.
1.2 Initializing Resource Information
private void initResources(@NonNull Context applicationContext) {(new ResourceCleaner(applicationContext)).start();
String dataDirPath = PathUtils.getDataDirectory(applicationContext);
String packageName = applicationContext.getPackageName();
PackageManager packageManager = applicationContext.getPackageManager();
AssetManager assetManager = applicationContext.getResources().getAssets();
this.resourceExtractor = new ResourceExtractor(dataDirPath, packageName, packageManager, assetManager);
this.resourceExtractor.addResource(this.fullAssetPathFrom(this.vmSnapshotData)).
addResource(this.fullAssetPathFrom(this.isolateSnapshotData)).addResource(this.fullAssetPathFrom("kernel_blob.bin"));
this.resourceExtractor.start();
}
Copy the code
Get the Android package manager, then get the AssetManager to get the resource manager object, and finally add the resource file of flutter to the resource manager via addResource of ResourceExtractor.
1.3 Load the flutter library
The analysis of the Flutter native library requires a separate download of the engine code. When the SO library is loaded, it calls its internal JNI_OnLoad method. Here we first look at what the Flutter library does at the JNI layer.
// shell/platform/android/library_loader.cc
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// Initialize the Java VM.
fml::jni::InitJavaVM(vm);
JNIEnv* env = fml::jni::AttachCurrentThread(a);bool result = false;
// Register FlutterMain.
result = flutter::FlutterMain::Register(env);
FML_CHECK(result);
// Register PlatformView
result = flutter::PlatformViewAndroid::Register(env);
FML_CHECK(result);
// Register VSyncWaiter.
result = flutter::VsyncWaiterAndroid::Register(env);
FML_CHECK(result);
return JNI_VERSION_1_4;
}
Copy the code
You can see the following operations in JNI_OnLoad:
- InitJavaVM Initializes the JavaVM
- Registered FlutterMain
- Registered PlatformViewAndroid
- Registered VsyncWaiter
1.3.1 Initializing the Java VM
// fml/platform/android/jni_util.cc
static JavaVM* g_jvm = nullptr;
void InitJavaVM(JavaVM* vm) {
FML_DCHECK(g_jvm == nullptr);
g_jvm = vm;
}
Copy the code
We just store JavaVm virtual machine instance objects in the global G_JVM. We know that When Android creates an application process, it creates a JavaVm object for it through Zygote’s fork, which is where the JavaVm object instances come from.
1.3.2 registered FlutterMain
bool FlutterMain::Register(JNIEnv* env) {
static const JNINativeMethod methods[] = {
{
.name = "nativeInit",
.signature = "(Landroid/content/Context; [Ljava/lang/String;Ljava/"
"lang/String; Ljava/lang/String; Ljava/lang/String; J)V",
.fnPtr = reinterpret_cast<void*>(&Init),
},
{
.name = "nativePrefetchDefaultFontManager",
.signature = "()V",
.fnPtr = reinterpret_cast<void*>(&PrefetchDefaultFontManager),
},
};
jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI");
if (clazz == nullptr) {
return false;
}
return env->RegisterNatives(clazz, methods, fml::size(methods)) == 0;
}
Copy the code
By registering here two jni native methods nativeInit and nativePrefetchDefaultFontManager, respectively correspond to the Init method and PrefetchDefaultFontManager FlutterMain.
1.3.3 registered PlatformViewAndroid
// shell/platform/android/platform_view_android_jni_impl.cc
bool PlatformViewAndroid::Register(JNIEnv* env) {... g_flutter_callback_info_class =new fml::jni::ScopedJavaGlobalRef<jclass>(
env, env->FindClass("io/flutter/view/FlutterCallbackInformation"));
if (g_flutter_callback_info_class->is_null()) {
FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation class";
return false;
}
g_flutter_callback_info_constructor = env->GetMethodID(
g_flutter_callback_info_class->obj(), "<init>"."(Ljava/lang/String; Ljava/lang/String; Ljava/lang/String;) V");
if (g_flutter_callback_info_constructor == nullptr) {
FML_LOG(ERROR) << "Could not locate FlutterCallbackInformation constructor";
return false;
}
g_flutter_jni_class = new fml::jni::ScopedJavaGlobalRef<jclass>(
env, env->FindClass("io/flutter/embedding/engine/FlutterJNI"));
if (g_flutter_jni_class->is_null()) {
FML_LOG(ERROR) << "Failed to find FlutterJNI Class.";
return false; }...return RegisterApi(env);
}
bool RegisterApi(JNIEnv* env) {
static const JNINativeMethod flutter_jni_methods[] = {
// Start of methods from FlutterJNI
{
.name = "nativeAttach",
.signature = "(Lio/flutter/embedding/engine/FlutterJNI; Z)J",
.fnPtr = reinterpret_cast<void*>(&AttachJNI),
},
{
.name = "nativeDestroy",
.signature = "(J)V",
.fnPtr = reinterpret_cast<void*>(&DestroyJNI),
},
{
.name = "nativeRunBundleAndSnapshotFromLibrary",
.signature = "(JLjava/lang/String; Ljava/lang/String;"
"Ljava/lang/String; Landroid/content/res/AssetManager;) V",
.fnPtr = reinterpret_cast<void*>(&RunBundleAndSnapshotFromLibrary),
},
{
.name = "nativeDispatchEmptyPlatformMessage",
.signature = "(JLjava/lang/String; I)V",
.fnPtr = reinterpret_cast<void*>(&DispatchEmptyPlatformMessage),
},
{
.name = "nativeDispatchPlatformMessage",
.signature = "(JLjava/lang/String; Ljava/nio/ByteBuffer; II)V",
.fnPtr = reinterpret_cast<void*>(&DispatchPlatformMessage),
},
...
};
if (env->RegisterNatives(g_flutter_jni_class->obj(), flutter_jni_methods,
fml::size(flutter_jni_methods)) ! =0) {
FML_LOG(ERROR) << "Failed to RegisterNatives with FlutterJNI";
return false;
}
g_handle_platform_message_method =
env->GetMethodID(g_flutter_jni_class->obj(), "handlePlatformMessage"."(Ljava/lang/String; [BI)V"); . g_handle_platform_message_response_method = env->GetMethodID(
g_flutter_jni_class->obj(), "handlePlatformMessageResponse"."(I[B)V"); . g_on_first_frame_method = env->GetMethodID(g_flutter_jni_class->obj(), "onFirstFrame"."()V"); . g_on_engine_restart_method = env->GetMethodID(g_flutter_jni_class->obj(), "onPreEngineRestart"."()V"); . g_create_overlay_surface_method = env->GetMethodID(g_flutter_jni_class->obj(), "createOverlaySurface"."()Lio/flutter/embedding/engine/FlutterOverlaySurface;"); .return true;
}
Copy the code
PlateformView registers a bunch of Java object methods for c++ to call Java layer methods, and registers classes’ JNI methods through the RegisterApi for the Java layer to call c++ methods.
1.3.4 registered VsyncWaiter
// shell/platform/android/vsync_waiter_android.cc
bool VsyncWaiterAndroid::Register(JNIEnv* env) {
static const JNINativeMethod methods[] = {{
.name = "nativeOnVsync",
.signature = "(JJJ)V",
.fnPtr = reinterpret_cast<void*>(&OnNativeVsync),
}};
jclass clazz = env->FindClass("io/flutter/embedding/engine/FlutterJNI");
if (clazz == nullptr) {
return false;
}
g_vsync_waiter_class = new fml::jni::ScopedJavaGlobalRef<jclass>(env, clazz);
FML_CHECK(! g_vsync_waiter_class->is_null());
g_async_wait_for_vsync_method_ = env->GetStaticMethodID(
g_vsync_waiter_class->obj(), "asyncWaitForVsync"."(J)V");
FML_CHECK(g_async_wait_for_vsync_method_ ! =nullptr);
return env->RegisterNatives(clazz, methods, fml::size(methods)) == 0;
}
Copy the code
VsyncWaiter registers the nativeOnVsync method in the JNI layer through the Register, and the corresponding native method of this method is OnNativeVsync. Meanwhile, it obtains the asyncWaitForVsync method of The FlutterJNI class in the Java layer.
Initialization of FlutterActivity
MainActivity in androdmanifest.xml inherits from FlutterActivity.
class MainActivity: FlutterActivity(a){}
Copy the code
Its default implementation is empty, so we’ll focus on the internal implementation of FlutterActivity.
public class FlutterActivity extends Activity implements Host.LifecycleOwner {
@VisibleForTesting
protectedFlutterActivityAndFragmentDelegate delegate; .protected void onCreate(@Nullable Bundle savedInstanceState) {
this.switchLaunchThemeForNormalTheme();
super.onCreate(savedInstanceState);
this.lifecycle.handleLifecycleEvent(Event.ON_CREATE);
this.delegate = new FlutterActivityAndFragmentDelegate(this);
this.delegate.onAttach(this);
this.delegate.onActivityCreated(savedInstanceState);
this.configureWindowForTransparency();
this.setContentView(this.createFlutterView());
this.configureStatusBarForFullscreenFlutterExperience();
}
protected void onStart(a) {
super.onStart();
this.lifecycle.handleLifecycleEvent(Event.ON_START);
this.delegate.onStart();
}
protected void onResume(a) {
super.onResume();
this.lifecycle.handleLifecycleEvent(Event.ON_RESUME);
this.delegate.onResume(); }...protected void onStop(a) {
super.onStop();
this.delegate.onStop();
this.lifecycle.handleLifecycleEvent(Event.ON_STOP); }... }Copy the code
From the code structure, FlutterActivity entrust the life cycle of a object, called FlutterActivityAndFragmentDelegate in onCreate method to create it. At the same time, onAttach and onActivityCreated methods are called after creation. The Activity is then set with the setContentView view view, which is created with CreateFlutterView. Let’s look at the implementation.
@NonNull
private View createFlutterView(a) {
return this.delegate.onCreateView((LayoutInflater)null, (ViewGroup)null, (Bundle)null);
}
Copy the code
Internal or onCreateView method through FlutterActivityAndFragmentDelegate object to create, so here we focus on the implementation of this class.
2.1 FlutterActivityAndFragmentDelegate
The name of this class is the client for Activity and Fragment of Flutter on Android. It can be assumed that Flutter delegates many activities and fragments related to this class of objects. Let’s focus on the delegate object’s main functions when FlutterActivity is initialized.
- onAttach()
- onCreateView()
Before we do that, let’s look at its constructor and basic members
final class FlutterActivityAndFragmentDelegate {
@NonNull
private FlutterActivityAndFragmentDelegate.Host host;
@Nullable
private FlutterEngine flutterEngine;
@Nullable
private FlutterSplashView flutterSplashView;
@Nullable
private FlutterView flutterView;
@Nullable
private PlatformPlugin platformPlugin;
private boolean isFlutterEngineFromHost;
FlutterActivityAndFragmentDelegate(@NonNull FlutterActivityAndFragmentDelegate.Host host) {
this.host = host;
}
interface Host extends SplashScreenProvider.FlutterEngineProvider.FlutterEngineConfigurator {}... }Copy the code
FlutterActivity implements FlutterActivityAndFragmentDelegate. The Host interface, the interface object responsible for FlutterActivity and delegate to communicate with each other. There are other members
-
FlutterEngine represents the rendering engine of Flutter
-
The flutterSplashView should be the view object for the splash screen page
-
FlutterView is a flutter view object hosted by FlutterActivity
-
PlatformPlugin is a platform plug-in object
2.1.1 onAttach
//FlutterActivityAndFragmentDelegate.java
void onAttach(@NonNull Context context) {
this.ensureAlive();
if (this.flutterEngine == null) {
this.setupFlutterEngine();
}
this.platformPlugin = this.host.providePlatformPlugin(this.host.getActivity(), this.flutterEngine);
if (this.host.shouldAttachEngineToActivity()) {
Log.v("FlutterActivityAndFragmentDelegate"."Attaching FlutterEngine to the Activity that owns this Fragment.");
this.flutterEngine.getActivityControlSurface().attachToActivity(this.host.getActivity(), this.host.getLifecycle());
}
this.host.configureFlutterEngine(this.flutterEngine);
}
Copy the code
In the delegate’s onAttach, the following happens:
- Create a Flutter engine using setupFlutterEngine.
- Attach the current FlutterActivity to the Flutter engine by attachToActivity
- Configure the flutter engine using the configureFlutterEngine
2.1.1.1 create FlutterEngine
@VisibleForTesting
void setupFlutterEngine(a) {...this.flutterEngine = new FlutterEngine(this.host.getContext(), this.host.getFlutterShellArgs().toArray(), false);
this.isFlutterEngineFromHost = false; . }//FlutterEgine constructor
public FlutterEngine(@NonNull Context context, @NonNull FlutterLoader flutterLoader, @NonNull FlutterJNI flutterJNI, @NonNull PlatformViewsController platformViewsController, @Nullable String[] dartVmArgs, boolean automaticallyRegisterPlugins) {
this.engineLifecycleListeners = new HashSet();
this.engineLifecycleListener = new FlutterEngine.EngineLifecycleListener() {
public void onPreEngineRestart(a) {
Log.v("FlutterEngine"."onPreEngineRestart()");
Iterator var1 = FlutterEngine.this.engineLifecycleListeners.iterator();
while(var1.hasNext()) {
FlutterEngine.EngineLifecycleListener lifecycleListener = (FlutterEngine.EngineLifecycleListener)var1.next();
lifecycleListener.onPreEngineRestart();
}
FlutterEngine.this.platformViewsController.onPreEngineRestart(); }};this.flutterJNI = flutterJNI;
// Initialize the engine
flutterLoader.startInitialization(context.getApplicationContext());
flutterLoader.ensureInitializationComplete(context, dartVmArgs);
flutterJNI.addEngineLifecycleListener(this.engineLifecycleListener);
this.attachToJni();
this.dartExecutor = new DartExecutor(flutterJNI, context.getAssets());
this.dartExecutor.onAttachedToJNI();
// Create a flutter render object
this.renderer = new FlutterRenderer(flutterJNI);
this.accessibilityChannel = new AccessibilityChannel(this.dartExecutor, flutterJNI);
// Create different channels
this.keyEventChannel = new KeyEventChannel(this.dartExecutor);
this.lifecycleChannel = new LifecycleChannel(this.dartExecutor);
this.localizationChannel = new LocalizationChannel(this.dartExecutor);
this.navigationChannel = new NavigationChannel(this.dartExecutor);
this.platformChannel = new PlatformChannel(this.dartExecutor);
this.settingsChannel = new SettingsChannel(this.dartExecutor);
this.systemChannel = new SystemChannel(this.dartExecutor);
this.textInputChannel = new TextInputChannel(this.dartExecutor);
this.platformViewsController = platformViewsController;
this.pluginRegistry = new FlutterEnginePluginRegistry(context.getApplicationContext(), this, flutterLoader);
if (automaticallyRegisterPlugins) {
this.registerPlugins(); // Register the plug-in}}Copy the code
This code is responsible for creating the FlutterEngine object FlutterEngine. Some initialization is done by calling startInitialization in the constructor of the FlutterEngine. It was initialized by this method when the FlutterApplication was created and is called again to ensure that it is initialized. Then finish the rest by ensureInitializationComplete initialization, finally create different Channel object for engine and registration by registerPlugins plug-in.
2.1.1.2 ensureInitializationComplete
public void ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args) {
if (!this.initialized) {
if(Looper.myLooper() ! = Looper.getMainLooper()) {throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
} else if (this.settings == null) {
throw new IllegalStateException("ensureInitializationComplete must be called after startInitialization");
} else {
try {
if (this.resourceExtractor ! =null) {
this.resourceExtractor.waitForCompletion();
}
List<String> shellArgs = new ArrayList();
shellArgs.add("--icu-symbol-prefix=_binary_icudtl_dat");
ApplicationInfo applicationInfo = this.getApplicationInfo(applicationContext);
shellArgs.add("--icu-native-lib-path=" + applicationInfo.nativeLibraryDir + File.separator + "libflutter.so");
if(args ! =null) {
Collections.addAll(shellArgs, args);
}
String kernelPath = null;
String appStoragePath = PathUtils.getDataDirectory(applicationContext) + File.separator + this.flutterAssetsDir;
kernelPath = appStoragePath + File.separator + "kernel_blob.bin";
shellArgs.add("--snapshot-asset-path=" + appStoragePath);
shellArgs.add("--vm-snapshot-data=" + this.vmSnapshotData);
shellArgs.add("--isolate-snapshot-data=" + this.isolateSnapshotData);
shellArgs.add("--cache-dir-path=" + PathUtils.getCacheDirectory(applicationContext));
if (this.settings.getLogTag() ! =null) {
shellArgs.add("--log-tag=" + this.settings.getLogTag());
}
appStoragePath = PathUtils.getFilesDir(applicationContext);
String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);
FlutterJNI.nativeInit(applicationContext, (String[])shellArgs.toArray(new String[0]), kernelPath, appStoragePath, engineCachesPath);
this.initialized = true;
} catch (Exception var8) {
Log.e("FlutterLoader"."Flutter initialization failed.", var8);
throw newRuntimeException(var8); }}}}Copy the code
The engine must be initialized in the Main Thread, followed by preparation of startup parameters for the startup engine and initialization via FlutterJNI’s nativeInit method, which was registered with FlutterMain’s Register method in Section 1.3.2. Let’s move on and look at how it initializes internally.
void FlutterMain::Init(JNIEnv* env, jclass clazz, jobject context, jobjectArray jargs, jstring kernelPath, jstring appStoragePath, jstring engineCachesPath, jlong initTimeMillis) {
std::vector<std::string> args;
args.push_back("flutter");
for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
args.push_back(std::move(arg));
}
auto command_line = fml::CommandLineFromIterators(args.begin(), args.end());
auto settings = SettingsFromCommandLine(command_line);
int64_t init_time_micros = initTimeMillis * 1000;
settings.engine_start_timestamp =
std::chrono::microseconds(Dart_TimelineGetMicros() - init_time_micros);
// Restore the callback cache.
// TODO(chinmaygarde): Route all cache file access through FML and remove this
// setter.
flutter::DartCallbackCache::SetCachePath(
fml::jni::JavaStringToString(env, appStoragePath));
fml::paths::InitializeAndroidCachesPath(
fml::jni::JavaStringToString(env, engineCachesPath));
flutter::DartCallbackCache::LoadCacheFromDisk(a);if(! flutter::DartVM::IsRunningPrecompiledCode() && kernelPath) {
// Check to see if the appropriate kernel files are present and configure
// settings accordingly.
auto application_kernel_path =
fml::jni::JavaStringToString(env, kernelPath);
if (fml::IsFile(application_kernel_path)) {
settings.application_kernel_asset = application_kernel_path;
}
}
settings.task_observer_add = [](intptr_t key, fml::closure callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback));
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};
#if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// There are no ownership concerns here as all mappings are owned by the
// embedder and not the engine.
auto make_mapping_callback = [](const uint8_t* mapping, size_t size) {
return [mapping, size]() {
return std::make_unique<fml::NonOwnedMapping>(mapping, size);
};
};
settings.dart_library_sources_kernel =
make_mapping_callback(kPlatformStrongDill, kPlatformStrongDillSize);
#endif // FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DEBUG
// Not thread safe. Will be removed when FlutterMain is refactored to no
// longer be a singleton.
g_flutter_main.reset(new FlutterMain(std::move(settings)));
g_flutter_main->SetupObservatoryUriCallback(env);
}
Copy the code
This method saves the command argument args and some resource paths in Settings, and then creates a FlutterMain object from it.
2.1.1.3 attachToJni
//FlutterEngine.java
private void attachToJni(a) {
Log.v("FlutterEngine"."Attaching to JNI.");
this.flutterJNI.attachToNative(false); . }//FlutterJni.java
public void attachToNative(boolean isBackgroundView) {
this.ensureRunningOnMainThread();
this.ensureNotAttachedToNative();
this.nativePlatformViewId = this.nativeAttach(this, isBackgroundView);
}
Copy the code
FlutterEngine of Java layer is only a shell. It needs to attach to the engine object of native layer before it can be truly created. Here, the binding with the native engine object is completed through attachToJni method. Before doing this, make sure that the main Thread is running and the object has not been attached. Finally, binding is completed by nativeAttach. This JNI method is registered with PlatformViewAndroid in Section 1.3.3, and its corresponding native method is AttachJNI. Let’s go back to PlatformViewAndroid and look at the concrete implementation of nativeAttach.
//shell/platform/android/platform_view_android_jni_impl.cc
static jlong AttachJNI(JNIEnv* env, jclass clazz, jobject flutterJNI, jboolean is_background_view) {
fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI);
std::shared_ptr<PlatformViewAndroidJNI> jni_facade =
std::make_shared<PlatformViewAndroidJNIImpl>(java_object);
auto shell_holder = std::make_unique<AndroidShellHolder>(
FlutterMain::Get().GetSettings(), jni_facade, is_background_view);
if (shell_holder->IsValid()) {
return reinterpret_cast<jlong>(shell_holder.release());
} else {
return 0; }}Copy the code
In the AttachJNI method it creates an AndroidShellHolder object. Note the three arguments to the AndroidShellHolder constructor:
- Get the previously initialized Settings object from FlutterMain
- PlatformViewAndroidJNIImpl
- Is_background_view defaults to false
2.1.2 create AndroidShellHolder
//shell/platform/android/android_shell_holder.cc
AndroidShellHolder::AndroidShellHolder(
flutter::Settings settings,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
bool is_background_view)
: settings_(std::move(settings)), jni_facade_(jni_facade) {
static size_t shell_count = 1;
auto thread_label = std::to_string(shell_count++);
// Create a thread
FML_CHECK(pthread_key_create(&thread_destruct_key_, ThreadDestructCallback) ==0); ./ / create ThreadHost
thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
ThreadHost::Type::IO};
thread_host_.ui_thread->GetTaskRunner() - >PostTask(jni_exit_task);
if(! is_background_view) { thread_host_.raster_thread->GetTaskRunner() - >PostTask(jni_exit_task);
}
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
// Create a PlatformView callback that creates a PlatformViewAndroid object
Shell::CreateCallback<PlatformView> on_create_platform_view =
[is_background_view, &jni_facade, &weak_platform_view](Shell& shell) {
std::unique_ptr<PlatformViewAndroid> platform_view_android;
if (is_background_view) {
platform_view_android = std::make_unique<PlatformViewAndroid>(
shell, // delegate
shell.GetTaskRunners(), // task runners
jni_facade // JNI interop
);
} else {
platform_view_android = std::make_unique<PlatformViewAndroid>(
shell, // delegate
shell.GetTaskRunners(), // task runners
jni_facade, // JNI interop
shell.GetSettings()
.enable_software_rendering // use software rendering
);
}
weak_platform_view = platform_view_android->GetWeakPtr(a);return platform_view_android;
};
// Create a Rasterizer
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell, shell.GetTaskRunners(),
shell.GetIsGpuDisabledSyncSwitch());
};
// The current thread will be used as the platform thread. Ensure that the
// message loop is initialized.
// Initialize the current thread, and initialize the four runners, gup/ UI/IO /platform, while ensuring that the MessageLoop initialization is complete
fml::MessageLoop::EnsureInitializedForCurrentThread(a); fml::RefPtr<fml::TaskRunner> gpu_runner; fml::RefPtr<fml::TaskRunner> ui_runner; fml::RefPtr<fml::TaskRunner> io_runner; fml::RefPtr<fml::TaskRunner> platform_runner = fml::MessageLoop::GetCurrent().GetTaskRunner(a); . gpu_runner = thread_host_.raster_thread->GetTaskRunner(a); ui_runner = thread_host_.ui_thread->GetTaskRunner(a); io_runner = thread_host_.io_thread->GetTaskRunner(a); .// Create Task runners
flutter::TaskRunners task_runners(thread_label, // label
platform_runner, // platform
gpu_runner, // raster
ui_runner, // ui
io_runner // io
);
// Create a Shell object
shell_ =
Shell::Create(task_runners, // task runners
GetDefaultPlatformData(), // window data
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
platform_view_ = weak_platform_view;
FML_DCHECK(platform_view_); is_valid_ = shell_ ! =nullptr;
if (is_valid_) {
// The priority of raster and UI threads
shell_->GetTaskRunners().GetRasterTaskRunner() - >PostTask([] () {if(: :setpriority(PRIO_PROCESS, gettid(), - 5) != 0) {
if(: :setpriority(PRIO_PROCESS, gettid(), 2 -) != 0) {
FML_LOG(ERROR) << "Failed to set GPU task runner priority"; }}}); shell_->GetTaskRunners().GetUITaskRunner() - >PostTask([] () {if(: :setpriority(PRIO_PROCESS, gettid(), - 1) != 0) {
FML_LOG(ERROR) << "Failed to set UI task runner priority"; }}); }}Copy the code
AndroidShellHolder is constructed to accomplish the following tasks:
-
Create ThreadHost
-
Create a callback to PlatformView, in this case PlatformViewAndroid
-
Initialize the current thread and initialize the four runners, gUP/UI/IO /platform, while ensuring that the MessageLoop initialization is complete
-
Create a task runners
-
Creating a Shell object
2.1.2.1 ThreadHost
ThreadHost::ThreadHost(std::string name_prefix, uint64_t mask) {
if (mask & ThreadHost::Type::Platform) {
platform_thread = std::make_unique<fml::Thread>(name_prefix + ".platform");
}
if (mask & ThreadHost::Type::UI) {
ui_thread = std::make_unique<fml::Thread>(name_prefix + ".ui");
}
if (mask & ThreadHost::Type::GPU) {
raster_thread = std::make_unique<fml::Thread>(name_prefix + ".raster");
}
if (mask & ThreadHost::Type::IO) {
io_thread = std::make_unique<fml::Thread>(name_prefix + ".io");
}
if (mask & ThreadHost::Type::Profiler) {
profiler_thread = std::make_unique<fml::Thread>(name_prefix + ".profiler"); }}Copy the code
The mask for ThreadHost: : Type: : UI | ThreadHost: : Type: : GPU | ThreadHost: : Type: : IO, so will first create the UI, the GPU, IO this three threads.
2.1.2.2 Creating Shell Objects
// shell/common/shell.cc
std::unique_ptr<Shell> Shell::Create(
TaskRunners task_runners,
const PlatformData platform_data,
Settings settings,
Shell::CreateCallback<PlatformView> on_create_platform_view,
Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
// Initialize the task
PerformInitializationTasks(settings);
PersistentCache::SetCacheSkSL(settings.cache_sksl);
TRACE_EVENT0("flutter"."Shell::Create");
/ / create DartVm
auto vm = DartVMRef::Create(settings);
FML_CHECK(vm) << "Must be able to initialize the VM.";
auto vm_data = vm->GetVMData(a);// Continue Shell creation
return Shell::Create(std::move(task_runners), //
std::move(platform_data), //
std::move(settings), //
vm_data->GetIsolateSnapshot(), // isolate snapshot
on_create_platform_view, //
on_create_rasterizer, //
std::move(vm) //
);
}
Copy the code
Create a Dart virtual machine DartVm before creating Shell objects
2.1.2.3 Creating Dart VMS
// runtime/dart_vm_lifecycle.cc
DartVMRef DartVMRef::Create(Settings settings, fml::RefPtr
vm_snapshot, fml::RefPtr
isolate_snapshot)
{
std::scoped_lock lifecycle_lock(gVMMutex); .// If there is already a running VM in the process, grab a strong reference to
// it.
// If the process has already run a VM, it returns a strong reference to it
if (auto vm = gVM.lock()) {
FML_DLOG(WARNING) << "Attempted to create a VM in a process where one was "
"already running. Ignoring arguments for current VM "
"create call and reusing the old VM.";
// There was already a running VM in the process,
return DartVMRef{std::move(vm)};
}
std::scoped_lock dependents_lock(gVMDependentsMutex); .// If there is no VM in the process. Initialize one, hold the weak reference
// and pass a strong reference to the caller.
auto isolate_name_server = std::make_shared<IsolateNameServer>();
// Create a VM
auto vm = DartVM::Create(std::move(settings), //
std::move(vm_snapshot), //
std::move(isolate_snapshot), //
isolate_name_server //); .return DartVMRef{std::move(vm)};
}
Copy the code
Dart VMS are created in this section
2.1.2.4 Continuing Shell Creation
// shell/common/shell.cc
std::unique_ptr<Shell> Shell::Create(
TaskRunners task_runners,
const PlatformData platform_data,
Settings settings,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
const Shell::CreateCallback<PlatformView>& on_create_platform_view,
const Shell::CreateCallback<Rasterizer>& on_create_rasterizer,
DartVMRef vm) {
PerformInitializationTasks(settings);
PersistentCache::SetCacheSkSL(settings.cache_sksl);
TRACE_EVENT0("flutter"."Shell::CreateWithSnapshots");
if(! task_runners.IsValid() | |! on_create_platform_view || ! on_create_rasterizer) {return nullptr;
}
fml::AutoResetWaitableEvent latch;
std::unique_ptr<Shell> shell;
fml::TaskRunner::RunNowOrPostTask(
task_runners.GetPlatformTaskRunner(),
fml::MakeCopyable([&latch, //
vm = std::move(vm), //
&shell, //
task_runners = std::move(task_runners), //
platform_data, //
settings, //
isolate_snapshot = std::move(isolate_snapshot), //
on_create_platform_view, //
on_create_rasterizer //] ()mutable {
shell = CreateShellOnPlatformThread(std::move(vm),
std::move(task_runners), //
platform_data, //
settings, //
std::move(isolate_snapshot), //
on_create_platform_view, //
on_create_rasterizer //
);
latch.Signal(a); })); latch.Wait(a);return shell;
}
Copy the code
Create a Shell through CreateShellOnPlatformThread, continue to see its implementation
// shell/common/shell.cc
std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
DartVMRef vm,
TaskRunners task_runners,
const PlatformData platform_data,
Settings settings,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
const Shell::CreateCallback<PlatformView>& on_create_platform_view,
const Shell::CreateCallback<Rasterizer>& on_create_rasterizer) {...// Create a Shell object
auto shell =
std::unique_ptr<Shell>(new Shell(std::move(vm), task_runners, settings));
// Create the rasterizer on the raster thread.
// Create a rasterizer in the raster thread
std::promise<std::unique_ptr<Rasterizer>> rasterizer_promise;
auto rasterizer_future = rasterizer_promise.get_future(a); std::promise<fml::WeakPtr<SnapshotDelegate>> snapshot_delegate_promise;auto snapshot_delegate_future = snapshot_delegate_promise.get_future(a); fml::TaskRunner::RunNowOrPostTask(
task_runners.GetRasterTaskRunner(), [&rasterizer_promise, //
&snapshot_delegate_promise,
on_create_rasterizer, //
shell = shell.get(a)//] () {TRACE_EVENT0("flutter"."ShellSetupGPUSubsystem");
std::unique_ptr<Rasterizer> rasterizer(on_create_rasterizer(*shell));
snapshot_delegate_promise.set_value(rasterizer->GetSnapshotDelegate());
rasterizer_promise.set_value(std::move(rasterizer));
});
// Create the platform view on the platform thread (this thread).
// Create a platfomeView on the platform thread (current thread)
auto platform_view = on_create_platform_view(*shell.get());
if(! platform_view || ! platform_view->GetWeakPtr()) {
return nullptr;
}
// Ask the platform view for the vsync waiter. This will be used by the engine
// to create the animator.
// Request VsyncWaiter for platform and the engine will use this object to create animations
auto vsync_waiter = platform_view->CreateVSyncWaiter(a);if(! vsync_waiter) {return nullptr;
}
// Create the IO manager on the IO thread. The IO manager must be initialized
// first because it has state that the other subsystems depend on. It must
// first be booted and the necessary references obtained to initialize the
// other subsystems.
std::promise<std::unique_ptr<ShellIOManager>> io_manager_promise;
auto io_manager_future = io_manager_promise.get_future(a); std::promise<fml::WeakPtr<ShellIOManager>> weak_io_manager_promise;auto weak_io_manager_future = weak_io_manager_promise.get_future(a); std::promise<fml::RefPtr<SkiaUnrefQueue>> unref_queue_promise;auto unref_queue_future = unref_queue_promise.get_future(a);auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner(a);// TODO(gw280): The WeakPtr here asserts that we are derefing it on the
// same thread as it was created on. We are currently on the IO thread
// inside this lambda but we need to deref the PlatformView, which was
// constructed on the platform thread.
//
// https://github.com/flutter/flutter/issues/42948
fml::TaskRunner::RunNowOrPostTask(
io_task_runner,
[&io_manager_promise, //
&weak_io_manager_promise, //
&unref_queue_promise, //
platform_view = platform_view->GetWeakPtr(), //
io_task_runner, //
is_backgrounded_sync_switch = shell->GetIsGpuDisabledSyncSwitch(a)//] () {TRACE_EVENT0("flutter"."ShellSetupIOSubsystem");
auto io_manager = std::make_unique<ShellIOManager>(
platform_view.getUnsafe() - >CreateResourceContext(),
is_backgrounded_sync_switch, io_task_runner);
weak_io_manager_promise.set_value(io_manager->GetWeakPtr());
unref_queue_promise.set_value(io_manager->GetSkiaUnrefQueue());
io_manager_promise.set_value(std::move(io_manager));
});
// Send dispatcher_maker to the engine constructor because shell won't have
// platform_view set until Shell::Setup is called later.
auto dispatcher_maker = platform_view->GetDispatcherMaker(a);// Create the engine on the UI thread.
Create the engine on the UI thread
std::promise<std::unique_ptr<Engine>> engine_promise;
auto engine_future = engine_promise.get_future(a); fml::TaskRunner::RunNowOrPostTask(
shell->GetTaskRunners().GetUITaskRunner(),
fml::MakeCopyable([&engine_promise, //
shell = shell.get(), //
&dispatcher_maker, //
&platform_data, //
isolate_snapshot = std::move(isolate_snapshot), //
vsync_waiter = std::move(vsync_waiter), //
&weak_io_manager_future, //
&snapshot_delegate_future, //
&unref_queue_future //] ()mutable {
TRACE_EVENT0("flutter"."ShellSetupUISubsystem");
const auto& task_runners = shell->GetTaskRunners(a);// The animator is owned by the UI thread but it gets its vsync pulses
// from the platform.
auto animator = std::make_unique<Animator>(*shell, task_runners,
std::move(vsync_waiter));
engine_promise.set_value(std::make_unique<Engine>(
*shell, //
dispatcher_maker, //
*shell->GetDartVM(), //
std::move(isolate_snapshot), //
task_runners, //
platform_data, //
shell->GetSettings(), //
std::move(animator), //
weak_io_manager_future.get(), //
unref_queue_future.get(), //
snapshot_delegate_future.get(a)//
));
}));
if(! shell->Setup(std::move(platform_view), //
engine_future.get(), //
rasterizer_future.get(), //
io_manager_future.get()) //
) {
return nullptr;
}
return shell;
}
Copy the code
CreateShellOnPlatformThread method main functions:
-
The Shell object is created
-
Create a Rasterizer in the Raster thread
-
Create a PlatfomeView on a platform thread using the on_create_platform_view callback. In 2.1.2 creating AndroidShellHolder, we know that the PlatformView created is actually PlatformViewAndroid
-
Request VsyncWaiter for platform, and the engine will create an animation with this object
-
Create the engine on the UI thread
-
Create platform_view via Shell
2.1.2.5 Rasterizer Initialization
Rasterizer::Rasterizer(
Delegate& delegate,
TaskRunners task_runners,
std::unique_ptr<flutter::CompositorContext> compositor_context,
std::shared_ptr<fml::SyncSwitch> is_gpu_disabled_sync_switch)
: delegate_(delegate),
task_runners_(std::move(task_runners)),
compositor_context_(std::move(compositor_context)),
user_override_resource_cache_bytes_(false),
weak_factory_(this),
is_gpu_disabled_sync_switch_(is_gpu_disabled_sync_switch) {
FML_DCHECK(compositor_context_);
}
Copy the code
2.1.2.6 PlatformViewAndroid create
// shell/platform/android/platform_view_android.cc
PlatformViewAndroid::PlatformViewAndroid(
PlatformView::Delegate& delegate,
flutter::TaskRunners task_runners,
std::shared_ptr<PlatformViewAndroidJNI> jni_facade,
bool use_software_rendering)
: PlatformView(delegate, std::move(task_runners)),
jni_facade_(jni_facade),
platform_view_android_delegate_(jni_facade) {
std::shared_ptr<AndroidContext> android_context;
if (use_software_rendering) {
android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kSoftware);
} else {
#if SHELL_ENABLE_VULKAN
android_context =
std::make_shared<AndroidContext>(AndroidRenderingAPI::kVulkan);
#else // SHELL_ENABLE_VULKAN
android_context = std::make_shared<AndroidContextGL>(
AndroidRenderingAPI::kOpenGLES,
fml::MakeRefCounted<AndroidEnvironmentGL>());
#endif // SHELL_ENABLE_VULKAN
}
FML_CHECK(android_context && android_context->IsValid()) < <"Could not create an Android context.";
android_surface_ = SurfaceFactory(std::move(android_context), jni_facade);
FML_CHECK(android_surface_ && android_surface_->IsValid()) < <"Could not create an OpenGL, Vulkan or Software surface to setup "
"rendering.";
}
Copy the code
AndroidSurface has three rendering methods: software rendering, VULKAN rendering, and GLES rendering. It is created by SurfaceFactor.
2.1.2.7 create Engine
Engine::Engine(Delegate& delegate,
const PointerDataDispatcherMaker& dispatcher_maker,
DartVM& vm,
fml::RefPtr<const DartSnapshot> isolate_snapshot,
TaskRunners task_runners,
const PlatformData platform_data,
Settings settings,
std::unique_ptr<Animator> animator,
fml::WeakPtr<IOManager> io_manager,
fml::RefPtr<SkiaUnrefQueue> unref_queue,
fml::WeakPtr<SnapshotDelegate> snapshot_delegate)
: delegate_(delegate),
settings_(std::move(settings)),
animator_(std::move(animator)),
activity_running_(true),
have_surface_(false),
image_decoder_(task_runners,
vm.GetConcurrentWorkerTaskRunner(),
io_manager),
task_runners_(std::move(task_runners)),
weak_factory_(this) {
// Runtime controller is initialized here because it takes a reference to this
// object as its delegate. The delegate may be called in the constructor and
// we want to be fully initilazed by that point.
runtime_controller_ = std::make_unique<RuntimeController>(
*this.// runtime delegate
&vm, // VM
std::move(isolate_snapshot), // isolate snapshot
task_runners_, // task runners
std::move(snapshot_delegate),
std::move(io_manager), // io manager
std::move(unref_queue), // Skia unref queue
image_decoder_.GetWeakPtr(), // image decoder
settings_.advisory_script_uri, // advisory script uri
settings_.advisory_script_entrypoint, // advisory script entrypoint
settings_.idle_notification_callback, // idle notification callback
platform_data, // platform data
settings_.isolate_create_callback, // isolate create callback
settings_.isolate_shutdown_callback, // isolate shutdown callback
settings_.persistent_isolate_data // persistent isolate data
);
pointer_data_dispatcher_ = dispatcher_maker(*this);
}
Copy the code
The RuntimeController is created in the Engine constructor, which should be called the RuntimeController by name.
2.1.2.8 Shell: : Setup
bool Shell::Setup(std::unique_ptr
platform_view, std::unique_ptr
engine, std::unique_ptr
rasterizer, std::unique_ptr
io_manager)
{
if (is_setup_) {
return false;
}
if(! platform_view || ! engine || ! rasterizer || ! io_manager) {return false;
}
platform_view_ = std::move(platform_view);
engine_ = std::move(engine);
rasterizer_ = std::move(rasterizer);
io_manager_ = std::move(io_manager);
// The weak ptr must be generated in the platform thread which owns the unique
// ptr.
weak_engine_ = engine_->GetWeakPtr(a); weak_rasterizer_ = rasterizer_->GetWeakPtr(a); weak_platform_view_ = platform_view_->GetWeakPtr(a);// Setup the time-consuming default font manager right after engine created.
fml::TaskRunner::RunNowOrPostTask(task_runners_.GetUITaskRunner(),
[engine = weak_engine_] {
if (engine) {
engine->SetupDefaultFontManager();
}
});
is_setup_ = true;
vm_->GetServiceProtocol() - >AddHandler(this.GetServiceProtocolDescription());
PersistentCache::GetCacheForProcess() - >AddWorkerTaskRunner(
task_runners_.GetIOTaskRunner());
PersistentCache::GetCacheForProcess() - >SetIsDumpingSkp(
settings_.dump_skp_on_shader_compilation);
if (settings_.purge_persistent_cache) {
PersistentCache::GetCacheForProcess() - >Purge(a); }// TODO(gw280): The WeakPtr here asserts that we are derefing it on the
// same thread as it was created on. Shell is constructed on the platform
// thread but we need to call into the Engine on the UI thread, so we need
// to use getUnsafe() here to avoid failing the assertion.
//
// https://github.com/flutter/flutter/issues/42947
display_refresh_rate_ = weak_engine_.getUnsafe() - >GetDisplayRefreshRate(a);return true;
}
Copy the code
2.1.3 onCreateView
In FlutterActivity’s onCreate, the Delegate onAttach method completes the creation of DartVm, three threads (gUP, UI, IO), and the creation of the Engine object. It then creates a view object for the FlutterActivity with CreateFlutterView, which eventually calls the delegate onCreateView to do so.
View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
Log.v("FlutterActivityAndFragmentDelegate"."Creating FlutterView.");
this.ensureAlive();
Create a FLUTTER view object
if (this.host.getRenderMode() == RenderMode.surface) {
FlutterSurfaceView flutterSurfaceView = new FlutterSurfaceView(this.host.getActivity(), this.host.getTransparencyMode() == TransparencyMode.transparent);
this.host.onFlutterSurfaceViewCreated(flutterSurfaceView);
this.flutterView = new FlutterView(this.host.getActivity(), flutterSurfaceView);
} else {
FlutterTextureView flutterTextureView = new FlutterTextureView(this.host.getActivity());
this.host.onFlutterTextureViewCreated(flutterTextureView);
this.flutterView = new FlutterView(this.host.getActivity(), flutterTextureView);
}
this.flutterView.addOnFirstFrameRenderedListener(this.flutterUiDisplayListener);
this.flutterSplashView = new FlutterSplashView(this.host.getContext());
if (VERSION.SDK_INT >= 17) {
this.flutterSplashView.setId(View.generateViewId());
} else {
this.flutterSplashView.setId(486947586);
}
this.flutterSplashView.displayFlutterViewWithSplash(this.flutterView, this.host.provideSplashScreen());
Log.v("FlutterActivityAndFragmentDelegate"."Attaching FlutterEngine to FlutterView.");
Attach the Flutter view to the FlutterEngine
this.flutterView.attachToFlutterEngine(this.flutterEngine);
return this.flutterSplashView;
}
Copy the code
OnCreateView is responsible for creating view objects for FlutterActivity. First, it decides which view to create based on the RenderMode. If rendermode. surface is used to create the FlutterView, which is actually the SurfaceView implementation inside. Otherwise build via FlutterTextureView and internally implement via TextureView.
Rendering mode is determined by default in FlutterActivity using the following criteria:
public RenderMode getRenderMode(a) {
return this.getBackgroundMode() == BackgroundMode.opaque ? RenderMode.surface : RenderMode.texture;
}
Copy the code
If the background is opaque, the RenderMode is rendermode.surface. Then create a FlutterSplashView(actually FrameLayout) and return the splash View to the Host Activity, since the splash page is the first View of the page. The subsequent content is rendered to the flutterView by the FlutterEngine. This is attached to the engine by attachToFlutterEngine. Let’s first look at the constructor of the FlutterView
// FlutterView.java
public FlutterView(@NonNull Context context, @NonNull FlutterView.RenderMode renderMode) {
super(context, (AttributeSet)null);
this.flutterUiDisplayListeners = new HashSet();
this.flutterEngineAttachmentListeners = new HashSet();
this.viewportMetrics = new ViewportMetrics();
this.onAccessibilityChangeListener = new NamelessClass_2();
this.flutterUiDisplayListener = new NamelessClass_1();
if (renderMode == FlutterView.RenderMode.surface) {
this.flutterSurfaceView = new FlutterSurfaceView(context);
this.renderSurface = this.flutterSurfaceView;
} else {
this.flutterTextureView = new FlutterTextureView(context);
this.renderSurface = this.flutterTextureView;
}
this.init();
}
Copy the code
The FlutterView constructor initializes the RenderSurface with the FlutterSurfaceView and FlutterTextView because they both implement the RenderSurface interface internally.
//RenderSurface.java
public interface RenderSurface {
@Nullable
FlutterRenderer getAttachedRenderer(a);
void attachToRenderer(@NonNull FlutterRenderer var1);
void detachFromRenderer(a);
}
Copy the code
2.1.3.1 attachToFlutterEngine
Next look at the implementation inside attachToFlutterEngine
//FlutterView.class
public void attachToFlutterEngine(@NonNull FlutterEngine flutterEngine) {...this.flutterEngine = flutterEngine;
// Get the FlutterRenderer object from the engine
FlutterRenderer flutterRenderer = this.flutterEngine.getRenderer();
this.isFlutterUiDisplayed = flutterRenderer.isDisplayingFlutterUi();
// Bind the view to FlutterRenderer
this.renderSurface.attachToRenderer(flutterRenderer);
flutterRenderer.addIsDisplayingFlutterUiListener(this.flutterUiDisplayListener);
this.textInputPlugin = new TextInputPlugin(this.this.flutterEngine.getDartExecutor(), this.flutterEngine.getPlatformViewsController());
this.androidKeyProcessor = new AndroidKeyProcessor(this.flutterEngine.getKeyEventChannel(), this.textInputPlugin);
this.androidTouchProcessor = new AndroidTouchProcessor(this.flutterEngine.getRenderer());
this.accessibilityBridge = new AccessibilityBridge(this, flutterEngine.getAccessibilityChannel(), (AccessibilityManager)this.getContext().getSystemService("accessibility"), this.getContext().getContentResolver(), this.flutterEngine.getPlatformViewsController());
this.accessibilityBridge.setOnAccessibilityChangeListener(this.onAccessibilityChangeListener);
this.resetWillNotDraw(this.accessibilityBridge.isAccessibilityEnabled(), this.accessibilityBridge.isTouchExplorationEnabled());
this.flutterEngine.getPlatformViewsController().attachAccessibilityBridge(this.accessibilityBridge);
this.textInputPlugin.getInputMethodManager().restartInput(this);
this.sendUserSettingsToFlutter();
this.sendLocalesToFlutter(this.getResources().getConfiguration());
this.sendViewportMetricsToFlutter();
// Bind the view to the engine's PlatformViewsController
flutterEngine.getPlatformViewsController().attachToView(this); . }Copy the code
In attachToFlutterEngine, a FlutterRenderer is first obtained by The flutterEngine, which should be used to render the view, and then the renderSurface of the FlutterView is attached to it. Finally, the engine will get a PlatFormViewController and attach it to the current view.
2.1.3.2 attachToRenderer
// FlutterSurfaceView.java
public void attachToRenderer(@NonNull FlutterRenderer flutterRenderer) {...this.flutterRenderer = flutterRenderer;
this.isAttachedToFlutterRenderer = true;
this.flutterRenderer.addIsDisplayingFlutterUiListener(this.flutterUiDisplayListener);
if (this.isSurfaceAvailableForRendering) {
Log.v("FlutterSurfaceView"."Surface is available for rendering. Connecting FlutterRenderer to Android surface.");
this.connectSurfaceToRenderer(); }}private void connectSurfaceToRenderer(a) {
if (this.flutterRenderer ! =null && this.getHolder() ! =null) {
this.flutterRenderer.startRenderingToSurface(this.getHolder().getSurface());
} else {
throw new IllegalStateException("connectSurfaceToRenderer() should only be called when flutterRenderer and getHolder() are non-null."); }}//FlutterRenderer.java
public void startRenderingToSurface(@NonNull Surface surface) {
if (this.surface ! =null) {
this.stopRenderingToSurface();
}
this.surface = surface;
this.flutterJNI.onSurfaceCreated(surface);
}
@UiThread
public void onSurfaceCreated(@NonNull Surface surface) {
this.ensureRunningOnMainThread();
this.ensureAttachedToNative();
this.nativeSurfaceCreated(this.nativePlatformViewId, surface);
}
Copy the code
Here we can look at the implementation of attachToRenderer in FlutterSurfaceView. FlutterTextView is left for readers to analyze by themselves. The method first adds a flutter UI listener. The FlutterRenderer is then connected via connectSurfaceToRenderer(). FlutterRender calls startRenderingToSurface and passes it the drawing Surface object of the current FlutterSurfaceView, Surface, Finally through flutterJNI. The onSurfaceCreated calls to jni nativeSurfaceCreated, the jni methods registered in 1.3.3 registration section PlatformViewAndroid RegisterApi Its corresponding native method is SurfaceCreated.
2.1.3.3 SurfaceCreated
//shell/platform/android/platform_view_android_jni_impl.cc
static void SurfaceCreated(JNIEnv* env, jobject jcaller, jlong shell_holder, jobject jsurface) {
// Note: This frame ensures that any local references used by
// ANativeWindow_fromSurface are released immediately. This is needed as a
// workaround for https://code.google.com/p/android/issues/detail?id=68174
fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env);
auto window = fml::MakeRefCounted<AndroidNativeWindow>(
ANativeWindow_fromSurface(env, jsurface));
ANDROID_SHELL_HOLDER->GetPlatformView() - >NotifyCreated(std::move(window));
}
//shell/platform/android/platform_view_android.cc
void PlatformViewAndroid::NotifyCreated( fml::RefPtr
native_window)
{
if (android_surface_) {
InstallFirstFrameCallback(a); fml::AutoResetWaitableEvent latch; fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetRasterTaskRunner(),
[&latch, surface = android_surface_.get(),
native_window = std::move(native_window)]() {
surface->SetNativeWindow(native_window);
latch.Signal(a); }); latch.Wait(a); } PlatformView::NotifyCreated(a); }Copy the code
You can see that ANativeWindow_fromSurface creates an ANativeWindow from the surface. This ANativeWindow must be familiar to anyone who has read Android source code. It represents the local window, which is the bridge between GPU rendering and screen. And then ANDROID_SHELL_HOLDER gets the PlatformView, so here’s the PlatformViewAndroid object, which tells you to create the window.
Three, the engine start process
In the onCreate FlutterActivity, through the delegate class FlutterActivityAndFragmentDelegate completed preparations to Flutter engine initialization, and views. The next step is to start the engine, which is done in onStart.
// FlutterActivityAndFragmentDelegate.java
void onStart(a) {
Log.v("FlutterActivityAndFragmentDelegate"."onStart()");
this.ensureAlive();
this.doInitialFlutterViewRun();
}
private void doInitialFlutterViewRun(a) {
if (this.host.getCachedEngineId() == null) {
if (!this.flutterEngine.getDartExecutor().isExecutingDart()) {
if (this.host.getInitialRoute() ! =null) {
this.flutterEngine.getNavigationChannel().setInitialRoute(this.host.getInitialRoute());
}
DartEntrypoint entrypoint = new DartEntrypoint(this.host.getAppBundlePath(), this.host.getDartEntrypointFunctionName());
this.flutterEngine.getDartExecutor().executeDartEntrypoint(entrypoint); }}}public String getDartEntrypointFunctionName(a) {
try {
ActivityInfo activityInfo = this.getPackageManager().getActivityInfo(this.getComponentName(), 128); Bundle metadata = activityInfo.metaData; String desiredDartEntrypoint = metadata ! =null ? metadata.getString("io.flutter.Entrypoint") : null;
returndesiredDartEntrypoint ! =null ? desiredDartEntrypoint : "main";
} catch (NameNotFoundException var4) {
return "main"; }}Copy the code
Call doInitialFlutterViewRun in the onStart method to start rendering FlutterView. Inside the method, execute via executeDartEntrypoint, which passes it a DartEntryPoint, Dart access point as the name implies. This object by getAppBundlePath and getDartEntrypointFunctionName to create two methods to get to the variables. GetAppBundlePath access to the resource path Dart files, getDartEntrypointFunctionName to is Lord of the Dart files operation method, the default name of the method is the main, the main method of the Dart.
3.1 executeDartEntrypoint
//DartExecutor.java
public void executeDartEntrypoint(@NonNull DartExecutor.DartEntrypoint dartEntrypoint) {
if (this.isApplicationRunning) {
Log.w("DartExecutor"."Attempted to run a DartExecutor that is already running.");
} else {
Log.v("DartExecutor"."Executing Dart entrypoint: " + dartEntrypoint);
this.flutterJNI.runBundleAndSnapshotFromLibrary(dartEntrypoint.pathToBundle, dartEntrypoint.dartEntrypointFunctionName, (String)null.this.assetManager);
this.isApplicationRunning = true; }}//FlutterJNI.java
@UiThread
public void runBundleAndSnapshotFromLibrary(
@NonNull String bundlePath,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager assetManager) { ensureRunningOnMainThread(); ensureAttachedToNative(); nativeRunBundleAndSnapshotFromLibrary( nativePlatformViewId, bundlePath, entrypointFunctionName, pathToEntrypointFunction, assetManager); }Copy the code
Internal call FlutterJNI runBundleAndSnapshotFromLibrary method, this method internal call nativeRunBundleAndSnapshotFromLibrary jni method, This JNI method is also registered in the RegisterApi method of 1.3.3 Registering PlatformViewAndroid.
3.2 nativeRunBundleAndSnapshotFromLibrary
//shell/platform/android/platform_view_android_jni_impl.cc
static void RunBundleAndSnapshotFromLibrary(JNIEnv* env, jobject jcaller, jlong shell_holder, jstring jBundlePath, jstring jEntrypoint, jstring jLibraryUrl, jobject jAssetManager) {
auto asset_manager = std::make_shared<flutter::AssetManager>();
asset_manager->PushBack(std::make_unique<flutter::APKAssetProvider>(
env, // jni environment
jAssetManager, // asset manager
fml::jni::JavaStringToString(env, jBundlePath)) // apk asset dir
);
std::unique_ptr<IsolateConfiguration> isolate_configuration;
if (flutter::DartVM::IsRunningPrecompiledCode()) {
isolate_configuration = IsolateConfiguration::CreateForAppSnapshot(a); }else {
std::unique_ptr<fml::Mapping> kernel_blob =
fml::FileMapping::CreateReadOnly(
ANDROID_SHELL_HOLDER->GetSettings().application_kernel_asset);
if(! kernel_blob) {FML_DLOG(ERROR) << "Unable to load the kernel blob asset.";
return;
}
isolate_configuration =
IsolateConfiguration::CreateForKernel(std::move(kernel_blob));
}
// Initialize RunConfiguration
RunConfiguration config(std::move(isolate_configuration), std::move(asset_manager));
{
auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint);
auto libraryUrl = fml::jni::JavaStringToString(env, jLibraryUrl);
if ((entrypoint.size(a) >0) && (libraryUrl.size(a) >0)) {
config.SetEntrypointAndLibrary(std::move(entrypoint),
std::move(libraryUrl));
} else if (entrypoint.size(a) >0) {
config.SetEntrypoint(std::move(entrypoint));
}
}
ANDROID_SHELL_HOLDER->Launch(std::move(config));
}
Copy the code
This method creates a RunConfiguration object for engine startup and is further processed by the Launch of ANDROID_SHELL_HOLDER.
//shell/platform/android/android_shell_holder.cc
void AndroidShellHolder::Launch(RunConfiguration config) {
if (!IsValid()) {
return;
}
shell_->RunEngine(std::move(config));
}
Copy the code
Here you can see that the engine is started by Shell
3.3 RunEngine
// shell/common/shell.cc
void Shell::RunEngine(
RunConfiguration run_configuration,
const std::function<void(Engine::RunStatus)>& result_callback) {... fml::TaskRunner::RunNowOrPostTask(
task_runners_.GetUITaskRunner(),
fml::MakeCopyable(
[run_configuration = std::move(run_configuration),
weak_engine = weak_engine_, result]() mutable {
if(! weak_engine) {FML_LOG(ERROR)
<< "Could not launch engine with configuration - no engine.";
result(Engine::RunStatus::Failure);
return;
}
// Start the engine
auto run_result = weak_engine->Run(std::move(run_configuration));
if (run_result == flutter::Engine::RunStatus::Failure) {
FML_LOG(ERROR) << "Could not launch engine with configuration.";
}
result(run_result);
}));
}
Copy the code
Further processing is done within RunEngine through Engine’s Run method
// shell/common/engine.cc
Engine::RunStatus Engine::Run(RunConfiguration configuration) {
// Obtain the configured EntryPoint
last_entry_point_ = configuration.GetEntrypoint(a); last_entry_point_library_ = configuration.GetEntrypointLibrary(a);// Prepare and load the ISOLATE
auto isolate_launch_status =
PrepareAndLaunchIsolate(std::move(configuration)); . }Copy the code
3.4 PrepareAndLaunchIsolate
//shell/common/engine.cc
Engine::RunStatus Engine::PrepareAndLaunchIsolate( RunConfiguration configuration) {
auto isolate_configuration = configuration.TakeIsolateConfiguration(a); std::shared_ptr<DartIsolate> isolate = runtime_controller_->GetRootIsolate().lock(a); ./ / ready to isolate
if(! isolate_configuration->PrepareIsolate(*isolate)) {
FML_LOG(ERROR) << "Could not prepare to run the isolate.";
returnRunStatus::Failure; }...// Start with DartIsolate's run method
if(! isolate->Run(configuration.GetEntrypoint(),
settings_.dart_entrypoint_args)) {
FML_LOG(ERROR) << "Could not run the isolate.";
returnRunStatus::Failure; }...return RunStatus::Success;
}
Copy the code
// runtime/dart_isolate.cc
[[nodiscard]] bool DartIsolate::Run(const std::string& entrypoint_name,
const std::vector<std::string>& args,
const fml::closure& on_run) {
TRACE_EVENT0("flutter"."DartIsolate::Run");
if(phase_ ! = Phase::Ready) {return false;
}
tonic::DartState::Scope scope(this);
auto user_entrypoint_function =
Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));
auto entrypoint_args = tonic::ToDart(args);
// Call main
if (!InvokeMainEntrypoint(user_entrypoint_function, entrypoint_args)) {
return false;
}
phase_ = Phase::Running;
if (on_run) {
on_run(a); }return true;
}
Copy the code
Here entrypoint_name is the main method name “main”. This method gets the entry function corresponding to the name of the main method, that is, the main method. The main method is then called through InvokeMainEntrypoint.
3.5 Calling the main method
// runtime/dart_isolate.cc
[[nodiscard]] static bool InvokeMainEntrypoint( Dart_Handle user_entrypoint_function, Dart_Handle args) {... Dart_Handle start_main_isolate_function = tonic::DartInvokeField(Dart_LookupLibrary(tonic::ToDart("dart:isolate")),
"_getStartMainIsolateFunction"{}); .if (tonic::LogIfError(tonic::DartInvokeField(
Dart_LookupLibrary(tonic::ToDart("dart:ui")), "_runMainZoned",
{start_main_isolate_function, user_entrypoint_function, args}))) {
FML_LOG(ERROR) << "Could not invoke the main entrypoint.";
return false;
}
return true;
}
Copy the code
The main method is finally started using the _runMainZoned() method of the Dart VM.
//lib/ui/hooks.dart
@pragma('vm:entry-point')
// ignore: unused_element
void _runMainZoned(Function startMainIsolateFunction,
Function userMainFunction,
List<String> args) {
startMainIsolateFunction((){
runZonedGuarded<void> (() {if (userMainFunction is _BinaryFunction) {
// This seems to be undocumented but supported by the command line VM.
// Let's do the same in case old entry-points are ported to Flutter.
(userMainFunction as dynamic)(args, ' ');
} else if (userMainFunction is _UnaryFunction) {
(userMainFunction as dynamic)(args);
} else{ userMainFunction(); }},Object error, StackTrace stackTrace) {
_reportUnhandledException(error.toString(), stackTrace.toString());
});
}, null);
}
Copy the code
Here userMainFunction is equal to the main() method in the main.dart file.