FlutterEngin啟動流程&android

AidenCang發表於2019-11-29

Flutter提供了獨立的執行環境Flutter.so作為Dart程式碼的執行環境和控制邏輯,完整的UI框架彙總邏輯和dartVM都是封裝在flutter.so庫中,在Android中開發完成之後,系統會打包到Asset目錄下,安裝成功之後,會抽取Asset資料夾下的Dart相關檔案到Android安裝包的環境中,進行載入執行,最終把FlutterUI相關的介面彙總在Android端提供的SurfaceView中進行渲染,開始分析Flutter相關檔案在android的載入流程

Android初始化

Flutter Engine編譯完成之後會生成一個FlutterJar包,提供給Android和Flutter程式碼互動的過程

FlutterEngin啟動流程&android

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層可以讀取的檔案路徑
複製程式碼

FlutterEngin啟動流程&android

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的目錄資料夾下。

FlutterEngin啟動流程&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目錄資料夾下。

FlutterEngin啟動流程&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相關的程式碼和業務邏輯

FlutterEngin啟動流程&android

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引擎程式碼的顯示

相關文章