Flutter提供了獨立的執行環境Flutter.so作為Dart程式碼的執行環境和控制邏輯,完整的UI框架彙總邏輯和dartVM都是封裝在flutter.so庫中,在Android中開發完成之後,系統會打包到Asset目錄下,安裝成功之後,會抽取Asset資料夾下的Dart相關檔案到Android安裝包的環境中,進行載入執行,最終把FlutterUI相關的介面彙總在Android端提供的SurfaceView中進行渲染,開始分析Flutter相關檔案在android的載入流程
Android初始化
Flutter Engine編譯完成之後會生成一個FlutterJar
包,提供給Android和Flutter程式碼互動的過程
Flutter程式碼在Android端程式碼啟動時,是如何載入相關的Dart程式碼,進行執行,最終在手機端是如何顯示出來的
!!! info "Android初始化過程"
* 1.Android Application啟動
* 2.FlutterMain類初始化Flutter相關的檔案,請檢視flutter的Apk包中的檔案結構
* 3.抽取Apk包中的Flutter相關的資源
* 4.System.loadLibrary(“flutter”)載入`libflutter.so` 引擎檔案
* 5.JNI第一次載入so庫時,會自動呼叫JNI_OnLoad方法,關聯Flutter java平臺程式碼到JNI中
* 6.在FlutterMain.cc中呼叫`Init()`初始化在java端抽取的flutter程式碼,把相關的檔案路徑傳遞到JNI中,進行初始化,JNI層可以讀取的檔案路徑
複製程式碼
Application中進行初始化:
!!! info "STEP"
* 1.Application中呼叫FlutterMain.startInitialization(this);
* 2.初始化配置檔案的路徑和檔名配置,方便讀取不同位置的flutter檔案
* 3.抽取Aot優化過的程式碼
* 4.載入資原始檔到資源查詢路徑中
* 5.載入so庫:System.loadLibrary("flutter");
* 6.FlutterActivity:對View、事件一系列動作的初始化
複製程式碼
android應用啟動Application初始化Dart相關的程式碼 Android專案下打包完成之後,抽取Flutter.jar下的libFlutter.so檔案在lib資料夾下,資原始檔和虛擬機器和相關的配置檔案到lib下,這一步檔案還是今天載入的檔案。安裝App成功之後,App第一次啟動會判斷時間戳來判斷當前的檔案是否需要再次抽取到Android的目錄資料夾下。
初始化Flutter檔案
Application啟動Flutter.jar進行初始化
public class FlutterApplication extends Application {
private Activity mCurrentActivity = null;
public FlutterApplication() {
}
@CallSuper
public void onCreate() {
super.onCreate();
FlutterMain.startInitialization(this);
}
public Activity getCurrentActivity() {
return this.mCurrentActivity;
}
public void setCurrentActivity(Activity mCurrentActivity) {
this.mCurrentActivity = mCurrentActivity;
}
}
複製程式碼
抽取Flutter相關程式碼,載入Flutter.so庫
flutter在apk中的檔案需要載入才能夠執行dart程式碼
1.初始化配置檔案 2.抽象相關的程式碼Apk包Asset目錄中的資料抽取到apk包下的目錄中 3.System.loadLibrary("flutter");初始化so庫
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 {
this.settings = settings;
long initStartTimestampMillis = SystemClock.uptimeMillis();
this.initConfig(applicationContext);
this.initResources(applicationContext);
System.loadLibrary("flutter");
VsyncWaiter.getInstance((WindowManager)applicationContext.getSystemService("window")).init();
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
FlutterJNI.nativeRecordStartTimestamp(initTimeMillis);
}
}
}
複製程式碼
初始化配置檔案,在apk包安裝到手機之後,在apk的目錄下可以找,初始化查詢的路徑
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");
}
}
複製程式碼
apk是一個檔案壓縮包,flutter程式碼發在apk包中的Assert中,需要出去出來發在apk的安裝目錄中,通過ExtractTask
類解析抽取,AssetManager
管理器可以抽取出相關的Flutter程式碼,會根據目錄下的時間戳檔案來判斷檔案是否已經被抽取過res_timestamp
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();
}
複製程式碼
到目前為止只是android在啟動時進行靜態的載入資料,相關的檔案已經新增到Android目錄資料夾下。
初始化UI介面、Plug,事件監聽回撥方法
1.在主執行緒中初始化Flutter檔案的安裝路徑
2.FlutterLoader
主要負責抽取Flutter的相關檔案到從apk包中的asset資料夾下抽取到安裝目錄檔案下
FlutterEngine是一個so庫,只有載入到Java的執行路徑中才能初始化Dart虛擬機器,提供Dart執行的環境,在接下來的文章中將一些分析FlutterEngine初始化過程,在Android中Application啟動完成,已經初始化程式之後,就可以開啟Activity,在清單檔案中配置的啟動Activity類繼承了FlutterActivity
1.初始化Androidwindow屬性,提供一個全屏狀態個Flutter來使用
2.等待Flutter引擎初始化完成
3.createFlutterView 提供給開發者自己定義Flutter SurfaceView的機制
4.如果createFlutterView
使用者沒有定製,那麼使用系統預設的SurfaceView
5.FlutterView
提供了面向使用者操作的類,FlutterNativeView
提供了Android程式碼和Flutter的互操作機制
6.呼叫setContentView
新增SurfaceView
7.新增第一幀到系統,可以避免Flutter初始化的時候出現白屏現象
經過上面的初始化過程,已經初始化完成UI介面,但是還有Flutter.so檔案的載入工作還沒有完成
public void onCreate(Bundle savedInstanceState) {
if (VERSION.SDK_INT >= 21) {
Window window = this.activity.getWindow();
window.addFlags(-2147483648);
window.setStatusBarColor(1073741824);
window.getDecorView().setSystemUiVisibility(1280);
}
String[] args = getArgsFromIntent(this.activity.getIntent());
載入flutter.so庫,通過FlutterJNI.nativeInit方法
FlutterMain.ensureInitializationComplete(this.activity.getApplicationContext(), args);
this.flutterView = this.viewFactory.createFlutterView(this.activity);
if (this.flutterView == null) {
FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView();
this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView);
this.flutterView.setLayoutParams(matchParent);
this.activity.setContentView(this.flutterView);
this.launchView = this.createLaunchView();
if (this.launchView != null) {
this.addLaunchView();
}
}
if (!this.loadIntent(this.activity.getIntent())) {
String appBundlePath = FlutterMain.findAppBundlePath();
if (appBundlePath != null) {
this.runBundle(appBundlePath);
}
}
}
複製程式碼
FlutterView初始化時需要載入Flutter相關的資源
1.初始化FlutterNativeView
監聽Flutter.so
庫的事件監聽,載入,解除安裝so庫的事件
FlutterPluginRegistry
:註冊系統層級的外掛管理物件
DartExecutor
:真正的管理FlutterAndroid側的外掛繫結及解繫結,事件級別的的處理
FlutterJNI
:監聽Flutter側回撥Android端的程式碼邏輯,so庫層級的事件處理
FlutterUiDisplayListener
:Flutter初始化完成之後會回到到Android端的UI改變監聽
2.
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs);
this.nextTextureId = new AtomicLong(0L);
this.mIsSoftwareRenderingEnabled = false;
this.didRenderFirstFrame = false;
this.onAccessibilityChangeListener = new OnAccessibilityChangeListener() {
public void onAccessibilityChanged(boolean isAccessibilityEnabled, boolean isTouchExplorationEnabled) {
FlutterView.this.resetWillNotDraw(isAccessibilityEnabled, isTouchExplorationEnabled);
}
};
Activity activity = getActivity(this.getContext());
if (activity == null) {
throw new IllegalArgumentException("Bad context");
} else {
if (nativeView == null) {
this.mNativeView = new FlutterNativeView(activity.getApplicationContext());
} else {
this.mNativeView = nativeView;
}
this.dartExecutor = this.mNativeView.getDartExecutor();
this.flutterRenderer = new FlutterRenderer(this.mNativeView.getFlutterJNI());
this.mIsSoftwareRenderingEnabled = this.mNativeView.getFlutterJNI().nativeGetIsSoftwareRenderingEnabled();
this.mMetrics = new FlutterView.ViewportMetrics();
this.mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;
this.setFocusable(true);
this.setFocusableInTouchMode(true);
this.mNativeView.attachViewAndActivity(this, activity);
this.mSurfaceCallback = new Callback() {
public void surfaceCreated(SurfaceHolder holder) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceChanged(width, height);
}
public void surfaceDestroyed(SurfaceHolder holder) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceDestroyed();
}
};
this.getHolder().addCallback(this.mSurfaceCallback);
this.mActivityLifecycleListeners = new ArrayList();
this.mFirstFrameListeners = new ArrayList();
註冊系統級別的外掛監聽
this.navigationChannel = new NavigationChannel(this.dartExecutor);
this.keyEventChannel = new KeyEventChannel(this.dartExecutor);
this.lifecycleChannel = new LifecycleChannel(this.dartExecutor);
this.localizationChannel = new LocalizationChannel(this.dartExecutor);
this.platformChannel = new PlatformChannel(this.dartExecutor);
this.systemChannel = new SystemChannel(this.dartExecutor);
this.settingsChannel = new SettingsChannel(this.dartExecutor);
final PlatformPlugin platformPlugin = new PlatformPlugin(activity, this.platformChannel);
this.addActivityLifecycleListener(new ActivityLifecycleListener() {
public void onPostResume() {
platformPlugin.updateSystemUiOverlays();
}
});
this.mImm = (InputMethodManager)this.getContext().getSystemService("input_method");
PlatformViewsController platformViewsController = this.mNativeView.getPluginRegistry().getPlatformViewsController();
this.mTextInputPlugin = new TextInputPlugin(this, this.dartExecutor, platformViewsController);
this.androidKeyProcessor = new AndroidKeyProcessor(this.keyEventChannel, this.mTextInputPlugin);
this.androidTouchProcessor = new AndroidTouchProcessor(this.flutterRenderer);
this.mNativeView.getPluginRegistry().getPlatformViewsController().attachTextInputPlugin(this.mTextInputPlugin);
this.sendLocalesToDart(this.getResources().getConfiguration());
this.sendUserPlatformSettingsToDart();
}
}
複製程式碼
SurfaceView事件和FlutterEngine事件
在Android端進行SurfaceView進行初始化時,SurfaceView的回到函式中,通過FlutterJNI
類中的本地方法和JNI層中的方法進行繫結,FlutterEngine在進行Flutter的渲染時,就可以傳遞相關的Surface給Android平臺進行渲染顯示
this.mSurfaceCallback = new Callback() {
public void surfaceCreated(SurfaceHolder holder) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
}
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceChanged(width, height);
}
public void surfaceDestroyed(SurfaceHolder holder) {
FlutterView.this.assertAttached();
FlutterView.this.mNativeView.getFlutterJNI().onSurfaceDestroyed();
}
};
複製程式碼
FlutterMain
呼叫Flutter for android 庫初始化Flutter相關的檔案
1.FlutterMain.startInitialization(this);
2.System.loadLibrary('flutter')
載入so
在engine中呼叫engine/src/flutter/shell/platform/android/library_loader.cc,JNI中的程式碼初始化:
!!! info "主要完成三件事"
* 1.初始化FlutterMain作為JNI層回撥的類
* 2.初始化平臺事件的處理類
* 3.初始化UI繪製Sync訊號的傳遞
複製程式碼
System.loadLibrary 呼叫過程
在Flutter.jar初始化時,呼叫System.loadLibrary 查詢解壓出來的Flutter.so檔案,呼叫dlopen開啟開啟so庫,載入C++相關的資源,載入完成後呼叫JNI_OnLoad,具體呼叫過程參考下圖,當呼叫JNI_OnLoad
完成後so庫以及提供了可以執行的函式,接下來就是初始化Flutter相關的程式碼和業務邏輯
JNI_OnLoad
JNI中對應相關的類進行初始化
-
第一次載入so庫時,呼叫當前的方法,完成一下三件事:
-
註冊Java層程式碼到JNI層,方便後續的回撥Java層程式碼
-
engine/src/flutter/shell/platform/android/flutter_main.cc
-
/src/flutter/shell/platform/android/io/flutter/view/FlutterView.java
初始化相關的平臺View的事件處理邏輯
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// Initialize the Java VM.
fml::jni::InitJavaVM(vm);
JNIEnv* env = fml::jni::AttachCurrentThread();
bool result = false;
// Register FlutterMain.
result = shell::FlutterMain::Register(env);
FML_CHECK(result);
// Register PlatformView
// 處理平臺的UI/事件相關的內容,生命週期的管理,後臺執行
result = shell::PlatformViewAndroid::Register(env);
FML_CHECK(result);
// Register VSyncWaiter.
// 真正處理相關的UI繪製事件
result = shell::VsyncWaiterAndroid::Register(env);
FML_CHECK(result);
return JNI_VERSION_1_4;
}
複製程式碼
FlutterMain::Register(env)
在library_loader庫中進行組成,編譯engine回撥java成程式碼
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;)V",
.fnPtr = reinterpret_cast<void*>(&Init),
},
{
.name = "nativeRecordStartTimestamp",
.signature = "(J)V",
.fnPtr = reinterpret_cast<void*>(&RecordStartTimestamp),
},
};
jclass clazz = env->FindClass("io/flutter/view/FlutterMain");
if (clazz == nullptr) {
return false;
}
return env->RegisterNatives(clazz, methods, arraysize(methods)) == 0;
}
複製程式碼
Shell
result = shell::PlatformViewAndroid::Register(env);對應java層事件的處理 /engine/src/flutter/shell/platform/android/platform_view_android.h
class PlatformViewAndroid final : public PlatformView {
<!-- 沒有對Register進行初始化 -->
public:
static bool Register(JNIEnv* env);
複製程式碼
VsyncWaiterAndroid
VsyncWaiterAndroid::Register(env);處理相關的frame rate問題,同步平臺層的事件和dart UI的事件更新,JNI回撥Java層的程式碼io/flutter/view/VsyncWaiter
,Android平臺層對幀率的控制使用的類:android.view.Choreographer
// 對應flutter for android 庫中的:io.flutter.view.VsyncWaiter
//
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/view/VsyncWaiter");
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, arraysize(methods)) == 0;
}
複製程式碼
flutter_main::nativeInit
apk中檔案抽取完成之後處理初始化JNI中的程式碼,載入flutter相關的檔案程式碼,io/flutter/view/FlutterMain.class ,nativeInit(applicationContext, (String[])shellArgs.toArray(new String[0]), appBundlePath, appStoragePath, engineCachesPath);
,初始化完成之後十里河
nativeInit() 在Java層可以使用相關的命令傳遞相關的引數 Java層中apk檔案抽取完成後初始化引數資訊, 主要是傳遞相關的初始化引數到JNI層,方便載入相關的檔案 初始化Flutter路徑資訊
void FlutterMain::Init(JNIEnv* env,
jclass clazz,
jobject context,
jobjectArray jargs,
jstring bundlePath,
jstring appStoragePath,
jstring engineCachesPath) {
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);
// 初始化資源目錄結構
settings.assets_path = fml::jni::JavaStringToString(env, bundlePath);
// Restore the callback cache.
// TODO(chinmaygarde): Route all cache file access through FML and remove this
// setter.
// 設定快取目錄
blink::DartCallbackCache::SetCachePath(
fml::jni::JavaStringToString(env, appStoragePath));
// 初始化Android快取目錄
fml::paths::InitializeAndroidCachesPath(
fml::jni::JavaStringToString(env, engineCachesPath));
// 從磁碟載入快取資料
blink::DartCallbackCache::LoadCacheFromDisk();
// 如何執行時配置檔案路徑
if (!blink::DartVM::IsRunningPrecompiledCode()) {
// Check to see if the appropriate kernel files are present and configure
// settings accordingly.
auto application_kernel_path =
fml::paths::JoinPaths({settings.assets_path, "kernel_blob.bin"});
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)));
}
複製程式碼
執行到這個地方,Apk中的檔案已經抽取完成,回撥方法,回撥事件已經完成,路徑的初始化資訊已經初始化
總結
通過上面的分析,已經初始化完成Flutter和Android的呼叫邏輯,通過上面的分析,總結一下Flutter初始化的大概邏輯
1.App啟動,在Application中呼叫FlutterMain
中的startInitialization
方法,完成路徑的配置,flutter程式碼的抽取,載入Flutter.so庫
2.開始初始化Activity中的UI介面和繫結Android和Flutter通訊,回撥等一系列的操作FlutterActivityDelegate
中完成
3.在``中呼叫nativeInit
方法初始化Flutter.so
庫進行DartVM的初始化操作
4.Flutter.so
庫載入時呼叫JNI_OnLoad
方法中初始化FlutterMain
作為Android端的呼叫入口
5.初始化PlatformViewAndroid
,Android端和FlutterView通訊互動的入口
6.Flutter.so初始化完成Engine相關的資源之後,在FlutterLoader.class
中呼叫nativeInit
方法初始化
7.通過FlutterJNI
中的nativeAttach
方法初始化Flutter.so
開始繫結Android端和Flutter相關的事件回撥
8.在Android端進行SurfaceView進行初始化時,SurfaceView的回到函式Callback
中,通過FlutterJNI
類中的本地方法和JNI層中的方法進行繫結,FlutterEngine在進行Flutter的渲染時,就可以傳遞相關的Surface給Android平臺進行渲染顯示
至此,Android端啟動、載入Flutter資源、初始化FlutterEngine,繫結SurfaceView和FlutterEngine引擎的物件,註冊系統級別的外掛功能,已經初始化完成,下一篇中我們將,分析FlutterEngine引擎是怎麼初始化的,畢竟要在手機上看到Flutter引擎程式碼的顯示