安卓平臺Flutter啟動過程全解析

銅板街技術發表於2019-04-01

前言

今天主要帶大家一起分析下flutter是如何啟動、初始化和載入dart程式碼的。這裡有幾點需要提前告知:

  1. 由於篇幅的問題,關於flutter介面建立、繪製過程將略過;

  2. 由於相關的c++程式碼比較多,而且較為複雜,建議先下載flutter engine的完整開發環境程式碼,閱讀本文更方便;

  3. 本文只分析啟動過程,參考的專案是基於android studio建立的一個預設flutter專案,以下簡稱demo。

正文

java層啟動過程

熟悉android的朋友都知道,一個APP啟動會先執行Application再執行Activity(AndroidManifest.xml中配置的啟動Activity),結合這個,我們先看看Application裡做了什麼,在分析過程中我們將挑取一些關鍵的native方法作為c++層入口方法作進一步的分析。

// io.flutter.app.FlutterApplication
public class FlutterApplication extends Application {
    @Override
    @CallSuper
    public void onCreate() {
        super.onCreate();
        FlutterMain.startInitialization(this);
    }
    
    //這塊程式碼和FlutterActivityDelegate的生命週期方法結合使用
    private Activity mCurrentActivity = null;
    public Activity getCurrentActivity() {
        return mCurrentActivity;
    }
    public void setCurrentActivity(Activity mCurrentActivity) {
        this.mCurrentActivity = mCurrentActivity;
    }
}

// io.flutter.view.FlutterMain中的方法
public static void startInitialization(Context applicationContext, FlutterMain.Settings settings) {
    if (Looper.myLooper() != Looper.getMainLooper()) {
        throw new IllegalStateException("startInitialization must be called on the main thread");
    } else if (sSettings == null) {
        sSettings = settings;
        long initStartTimestampMillis = SystemClock.uptimeMillis();
        initConfig(applicationContext);
        initAot(applicationContext);
        initResources(applicationContext);
        System.loadLibrary("flutter");
       ...
    }
}

複製程式碼

startInitialization只能執行在主執行緒中,否則會丟擲異常。通過sSettings這個變數可以看出,啟動的過程中,這個方法將只執行一遍。initConfig初始化一些變數的配置資訊(在AndroidManifest.xml中可以通過meta-data方式配置這些變數值), System.loadLibrary("flutter")則完成裝載flutter庫檔案,期間會在c++層完成JNI方法的動態註冊。initResources方法我們往下看。

private static void initResources(Context applicationContext) {
	Context context = applicationContext;
	new ResourceCleaner(context).start();
	...
	sResourceExtractor = new ResourceExtractor(context);
	...
	sResourceExtractor.start();
}
複製程式碼

ResourceCleaner將清理帶有指定標識的快取檔案,ResourceExtractor將完成asset 目錄下flutter相關資源的拷貝,這些資源會在後續flutter engine和DartVM等初始化時使用。 然後我們再來看看啟動activity都做了些什麼

onCreate

//MainActivity.java

public class MainActivity extends FlutterActivity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    GeneratedPluginRegistrant.registerWith(this);
  }
}

//FlutterActivity.java

protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    this.eventDelegate.onCreate(savedInstanceState);
}
複製程式碼

先看FlutterActivity中執行onCreate,可以看到這裡面並沒有當前ContentView的設定,那麼其內容介面是在哪裡設定的呢,我們可以看到第二句this.eventDelegate.onCreate(savedInstanceState);,最終我們發現Activity中顯示的view是在代理類中進行初始化的,下面看下代理類FlutterActivityDelegate的執行,

//FlutterActivityDelegate.java

public void onCreate(Bundle savedInstanceState) {
    ...
    String[] args = getArgsFromIntent(this.activity.getIntent());
    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();
        }
    }
   ...
   this.runBundle(appBundlePath);
   ... 
}
複製程式碼

在這裡我們需要注意FlutterMain.ensureInitializationComplete的執行,

//FlutterMain.java

public static void ensureInitializationComplete(Context applicationContext, String[] args) {
    ...
    sResourceExtractor.waitForCompletion();
    ...
    nativeInit(applicationContext, (String[])shellArgs.toArray(new String[0]), appBundlePath, appStoragePath, engineCachesPath);
    sInitialized = true;
    ...
}

//c++關鍵方法1
private static native void nativeInit(Context var0, String[] var1, String var2, String var3, String var4);
複製程式碼

它將等待解壓任務結束,資源處理完畢,然後拼接引數,完成引數初始化後將執行nativeInit方法對c++層初始化。

然後會建立FlutterView物件,這裡面還包含了很多關鍵物件的建立,這個下文將會分析到。

//FlutterView.java的構造方法
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
    super(context, attrs);
    ...
    if (nativeView == null) {
        this.mNativeView = new FlutterNativeView(activity.getApplicationContext());
    } else {
        this.mNativeView = nativeView;
    }
    this.mNativeView.getFlutterJNI();
    this.mIsSoftwareRenderingEnabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled();
    ...
    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.mAccessibilityManager = (AccessibilityManager)this.getContext().getSystemService("accessibility");
    ...
    this.mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE);
    ...
}
複製程式碼

這個方法中先執行FlutterNativeView物件建立,然後是FlutterJNI物件建立,再通過c++層完成兩者的繫結關係。另外activity和flutterView的繫結關係也在這裡完成,並會在PlatformViewsController中完成註冊方法回撥關係。這個方法還包含了介面繪製監聽,flutter繪製的關鍵呼叫,建立了通訊體系(各類Channel)。在c++層會用到的資源處理物件也是從這裡建立的。

//FlutterNativeView.java構造方法

public FlutterNativeView(Context context, boolean isBackgroundView) {
    this.mPluginRegistry = new FlutterPluginRegistry(this, context);
    this.mFlutterJNI = new FlutterJNI();
    this.mFlutterJNI.setRenderSurface(new FlutterNativeView.RenderSurfaceImpl());
    this.mFlutterJNI.setPlatformMessageHandler(new FlutterNativeView.PlatformMessageHandlerImpl());
    this.mFlutterJNI.addEngineLifecycleListener(new FlutterNativeView.EngineLifecycleListenerImpl());
    this.attach(this, isBackgroundView);
    ....
}
//c++關鍵方法2
private native long nativeAttach(FlutterJNI var1, boolean var2);
複製程式碼

FlutterPluginRegistry是actitiy和flutterView繫結關係操作類,而FlutterJNI建立時,將繫結繪製、跨平臺通訊、生命週期的監聽方法。這裡還會涉及到nativeAttach這個c++方法,等一會將會分析到。

繼續看runBundle的執行

//FlutterView.java

private FlutterNativeView mNativeView;
public void runFromBundle(FlutterRunArguments args) {
  assertAttached();
  preRun();
  mNativeView.runFromBundle(args);
  ...
}

//FlutterNativeView.java

public void runFromBundle(FlutterRunArguments args) {
   ...
   runFromBundleInternal(new String[] {args.bundlePath, args.defaultPath},
            args.entrypoint, args.libraryPath);
   ...
}

/**
* 這裡通過demo,我們需要留意下傳入的資料,方便接下來的分析
* bundlePaths:(flutter_assets目錄地址)
* entrypoint:"main"
* libraryPath:null
*
*/
private void runFromBundleInternal(String[] bundlePaths, String entrypoint,
    String libraryPath) {
    ....
    mFlutterJNI.runBundleAndSnapshotFromLibrary(
        bundlePaths,
        entrypoint,
        libraryPath,
        mContext.getResources().getAssets()
    );
    ....
}
複製程式碼

此時,runFromBundle會先判斷資源的繫結,把一些引數通過runBundleAndSnapshotFromLibrary方法中mFlutterJNI物件呼叫JNI方法來傳遞指定flutter入口供DartVM執行dart層程式碼邏輯。

//FlutterJNI.java

@UiThread
public void runBundleAndSnapshotFromLibrary(@NonNull String[] prioritizedBundlePaths, @Nullable String entrypointFunctionName, @Nullable String pathToEntrypointFunction, @NonNull AssetManager assetManager) {
    this.ensureAttachedToNative();
    this.nativeRunBundleAndSnapshotFromLibrary(this.nativePlatformViewId, prioritizedBundlePaths, entrypointFunctionName, pathToEntrypointFunction, assetManager);
}
    
//最終樣例資料:pathToEntrypointFunction = null,entrypointFunctionName="main"
//prioritizedBundlePaths同上面,nativePlatformViewId = 3719055232
private native void nativeRunBundleAndSnapshotFromLibrary(
    long nativePlatformViewId,
    @NonNull String[] prioritizedBundlePaths,
    @Nullable String entrypointFunctionName,
    @Nullable String pathToEntrypointFunction,
    @NonNull AssetManager manager
);
複製程式碼

nativeRunBundleAndSnapshotFromLibrary 則是native啟動方法的入口,另外這個 nativePlatformViewId 是在FlutterNativeView 建立的時候呼叫了FlutterJNIattachToNative方法,其來源是native層shell_holder物件指標,這個物件指標在native啟動過程中非常關鍵。

再看MainActivity中onCreate執行,GeneratedPluginRegistrant.registerWith(this)將執行到如下程式碼中

//FlutterActivityDelegate.java

private FlutterView flutterView;
@Override
public Registrar registrarFor(String pluginKey) {
    return flutterView.getPluginRegistry().registrarFor(pluginKey);
}

//FlutterPluginRegistry.java

@Override
public Registrar registrarFor(String pluginKey) {
    if (mPluginMap.containsKey(pluginKey)) {
        throw new IllegalStateException("Plugin key " + pluginKey + " is already in use");
    }
    mPluginMap.put(pluginKey, null);
    return new FlutterRegistrar(pluginKey);
}
複製程式碼

registrarFor儲存了外掛的例項,避免重複註冊。

onStart:

以下方法通過生命週期對應的Platform Channel傳送生命週期狀態給Flutter層來告知當前的APP狀態。

this.mFlutterLifecycleChannel.send("AppLifecycleState.inactive");
複製程式碼

onResume:

public void onResume() {
    Application app = (Application)this.activity.getApplicationContext();
    FlutterMain.onResume(app);
    if (app instanceof FlutterApplication) {
        FlutterApplication flutterApp = (FlutterApplication)app;
        flutterApp.setCurrentActivity(this.activity);
    }
}

public static void onResume(Context context) {
    //熱更新有關,這裡也不分析
    if (sResourceUpdater != null && sResourceUpdater.getDownloadMode() == DownloadMode.ON_RESUME) {
        sResourceUpdater.startUpdateDownloadOnce();
    }
}
複製程式碼

到這裡基本完成了java層分析,主要方法呼叫鏈可以參考如下

image

接下來將需要分析的關鍵JNI方法羅列如下:

  • nativeInit
  • nativeAttach
  • nativeRunBundleAndSnapshotFromLibrary

c/c++層啟動過程

nativeInit分析

我們直接找到對應的方法,位於shell/platform/android/flutter_main.cc

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);
  ...
  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);
  };
  ...
  g_flutter_main.reset(new FlutterMain(std::move(settings)));
}
複製程式碼

這裡做了幾件事情:

  • 解析java傳過來的引數
  • 建立Setting,儲存配置
  • 建立FlutterMain,重置其全域性物件

nativeAttach分析

static jlong AttachJNI(JNIEnv* env,
                       jclass clazz,
                       jobject flutterJNI,
                       jboolean is_background_view) {
  fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI);
  auto shell_holder = std::make_unique<AndroidShellHolder>(
      FlutterMain::Get().GetSettings(), java_object, is_background_view);
  if (shell_holder->IsValid()) {
    return reinterpret_cast<jlong>(shell_holder.release());
  } else {
    return 0;
  }
}

//shell/platform/android/android_shell_holder.cc
AndroidShellHolder::AndroidShellHolder(
    blink::Settings settings,
    fml::jni::JavaObjectWeakGlobalRef java_object,
    bool is_background_view)
    : settings_(std::move(settings)), java_object_(java_object) {
  ...
  auto jni_exit_task([key = thread_destruct_key_]() {
    FML_CHECK(pthread_setspecific(key, reinterpret_cast<void*>(1)) == 0);
  });
  thread_host_.ui_thread->GetTaskRunner()->PostTask(jni_exit_task);
  if (!is_background_view) {
    thread_host_.gpu_thread->GetTaskRunner()->PostTask(jni_exit_task);
  }
  ...
  fml::MessageLoop::EnsureInitializedForCurrentThread();
  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();
  if (is_background_view) {
    auto single_task_runner = thread_host_.ui_thread->GetTaskRunner();
    gpu_runner = single_task_runner;
    ui_runner = single_task_runner;
    io_runner = single_task_runner;
  } else {
    gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
    ui_runner = thread_host_.ui_thread->GetTaskRunner();
    io_runner = thread_host_.io_thread->GetTaskRunner();
  }
  blink::TaskRunners task_runners(thread_label,     // label
                                  platform_runner,  // platform
                                  gpu_runner,       // gpu
                                  ui_runner,        // ui
                                  io_runner         // io
  );
  shell_ =
      Shell::Create(task_runners,             // task runners
                    settings_,                // settings
                    on_create_platform_view,  // platform view create callback
                    on_create_rasterizer      // rasterizer create callback
      );
     ...
}

std::unique_ptr<Shell> Shell::Create(
    blink::TaskRunners task_runners,
    blink::Settings settings,
    Shell::CreateCallback<PlatformView> on_create_platform_view,
    Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
   PerformInitializationTasks(settings);
  auto vm = blink::DartVM::ForProcess(settings);
  FML_CHECK(vm) << "Must be able to initialize the VM.";
  return Shell::Create(std::move(task_runners),             //
                       std::move(settings),                 //
                       vm->GetIsolateSnapshot(),            //
                       blink::DartSnapshot::Empty(),        //
                       std::move(on_create_platform_view),  //
                       std::move(on_create_rasterizer)      //
  );
}
複製程式碼

nativeAttach的方法中,呼叫了AndroidShellHolder物件的建立,包含了JNI生命週期同UI和GPU執行緒繫結, 檢視回撥和c++層繪製繫結,啟動一些必要的執行緒。而shell物件的建立中,PerformInitializationTasks包含了一些關鍵庫的初始化,如skia(圖形繪製庫)、ICU(國際化庫)等初始化,shell物件的建立也標誌著dart vm的建立。

關鍵點:AndroidShellHolder物件建立完成後,會將其物件指標值返回給java層儲存,用於後續安卓原生層對Flutter層各操作方法的呼叫。

nativeRunBundleAndSnapshotFromLibrary 分析

在shell/platform/android/io/platform_view_android_jni.cc中,我們很容易找到對應的方法,是採用動態註冊的方式:

  {
          .name = "nativeRunBundleAndSnapshotFromLibrary",
          .signature = "(J[Ljava/lang/String;Ljava/lang/String;"
                       "Ljava/lang/String;Landroid/content/res/AssetManager;)V",
          .fnPtr =
              reinterpret_cast<void*>(&shell::RunBundleAndSnapshotFromLibrary),
 }
複製程式碼
static void RunBundleAndSnapshotFromLibrary(JNIEnv* env,
                                            jobject jcaller,
                                            jlong shell_holder,
                                            jobjectArray jbundlepaths,
                                            jstring jEntrypoint,
                                            jstring jLibraryUrl,
                                            jobject jAssetManager) {
  auto asset_manager = std::make_shared<blink::AssetManager>();
  for (const auto& bundlepath :
       fml::jni::StringArrayToVector(env, jbundlepaths)) {
    ...
    const auto file_ext_index = bundlepath.rfind(".");
    if (bundlepath.substr(file_ext_index) == ".zip") {
        //資源解壓
      asset_manager->PushBack(std::make_unique<blink::ZipAssetStore>(
          bundlepath, "assets/flutter_assets"));

    } else {
      //操作資源地址並儲存到容器中
      asset_manager->PushBack(
          std::make_unique<blink::DirectoryAssetBundle>(fml::OpenDirectory(
              bundlepath.c_str(), false, fml::FilePermission::kRead)));
      ...
    }
  }

  auto isolate_configuration = CreateIsolateConfiguration(*asset_manager);
  ...
  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() > 0) && (libraryUrl.size() > 0)) {
        //設定dart的入口函式,entrypoint為“main”,引用庫地址
      config.SetEntrypointAndLibrary(std::move(entrypoint),
                                     std::move(libraryUrl));
    } else if (entrypoint.size() > 0) {
      config.SetEntrypoint(std::move(entrypoint));
    }
  }

  ANDROID_SHELL_HOLDER->Launch(std::move(config));
}
複製程式碼

從上面的方法我們可以簡單的總結下這個方法做了什麼:

  • 資源的解壓
  • 建立AppSnapshotIsolateConfiguration物件
  • 執行配置項
  • 執行啟動方法

android_shell_holder.cc

void AndroidShellHolder::Launch(RunConfiguration config) {
  //is_valid_ = shell_ != nullptr;正常情況下為true
  if (!IsValid()) {
    return;
  }

  shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
      fml::MakeCopyable([engine = shell_->GetEngine(),  //拿到了引擎的弱引用物件
                         config = std::move(config)     
  ]() mutable {
        ...
        //next
        if (!engine || engine->Run(std::move(config)) ==
                           shell::Engine::RunStatus::Failure) {
        ...
        }
        ...
      }));
}
複製程式碼

Launch方法中拿到engine物件後,呼叫Run的執行

//engine.cc
Engine::RunStatus Engine::Run(RunConfiguration configuration) {
  ...
  auto isolate_launch_status =
      PrepareAndLaunchIsolate(std::move(configuration));
  ....
}

shell::Engine::RunStatus Engine::PrepareAndLaunchIsolate(
    RunConfiguration configuration) {
  TRACE_EVENT0("flutter", "Engine::PrepareAndLaunchIsolate");
  UpdateAssetManager(configuration.GetAssetManager());
  auto isolate_configuration = configuration.TakeIsolateConfiguration();
  std::shared_ptr<blink::DartIsolate> isolate =
      runtime_controller_->GetRootIsolate().lock();

  if (!isolate) {
    return RunStatus::Failure;
  }
  ...
  if (!isolate_configuration->PrepareIsolate(*isolate)) {
    FML_LOG(ERROR) << "Could not prepare to run the isolate.";
    return RunStatus::Failure;
  }
  if (configuration.GetEntrypointLibrary().empty()) {
    if (!isolate->Run(configuration.GetEntrypoint())) {
      FML_LOG(ERROR) << "Could not run the isolate.";
      return RunStatus::Failure;
    }
  } else {
    if (!isolate->RunFromLibrary(configuration.GetEntrypointLibrary(),
                                 configuration.GetEntrypoint())) {
      FML_LOG(ERROR) << "Could not run the isolate.";
      return RunStatus::Failure;
    }
  }

  return RunStatus::Success;
}
複製程式碼

在engine的啟動過程中,準備和啟動isolate,在這個方法中將完成對isolate建立、及狀態返回處理。更新資源管理後,PrepareIsolate方法主要檢查Isolate的狀態,通過屬性phase(列舉)來表示不同的狀態,然後我們再結合java層傳遞的資料,可以知道將執行isolate->Run方法。

//dart_api_impl.cc
FML_WARN_UNUSED_RESULT
bool DartIsolate::Run(const std::string& entrypoint_name) {
 ...   
  auto user_entrypoint_function =
      Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));
  if (!InvokeMainEntrypoint(user_entrypoint_function)) {
    return false;
  }
 ...
}
複製程式碼

Run方法中也比較簡單,繼續看下文。

//dart_isolate.cc
FML_WARN_UNUSED_RESULT
static bool InvokeMainEntrypoint(Dart_Handle user_entrypoint_function) {
  ...
  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}))) {
    FML_LOG(ERROR) << "Could not invoke the main entrypoint.";
    return false;
  }
  return true;
}
複製程式碼

在InvokeMainEntrypoint方法中 會拿到了Dart_Handle物件,並通過DartInvokeField方法執行Dart_Invoke方法。另外Dart_LookupLibrary中建立的物件是一個Library,這個是下個方法執行步驟的判斷依據。

dart_api_impl.cc

DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
                                    Dart_Handle name,
                                    int number_of_arguments,
                                    Dart_Handle* arguments) {                               
  DARTSCOPE(Thread::Current());
  API_TIMELINE_DURATION(T);
  CHECK_CALLBACK_STATE(T);
  String& function_name =
      String::Handle(Z, Api::UnwrapStringHandle(Z, name).raw());
  if (function_name.IsNull()) {
    RETURN_TYPE_ERROR(Z, name, String);
  }
  if (number_of_arguments < 0) {
    return Api::NewError(
        "%s expects argument 'number_of_arguments' to be non-negative.",
        CURRENT_FUNC);
  }
  ...
  if (obj.IsType()) {
    ...
    const Class& cls = Class::Handle(Z, Type::Cast(obj).type_class());
    ...
    //分析節點1
    return Api::NewHandle(
        T, cls.Invoke(function_name, args, arg_names, respect_reflectable,
                      check_is_entrypoint));
  } else if (obj.IsNull() || obj.IsInstance()) {
     ...
    Instance& instance = Instance::Handle(Z);
    ...
    //分析節點2
    return Api::NewHandle(
        T, instance.Invoke(function_name, args, arg_names, respect_reflectable,
                           check_is_entrypoint));
  } else if (obj.IsLibrary()) {
    ...
    const Library& lib = Library::Cast(obj);
    ...
    //分析節點3
    return Api::NewHandle(
        T, lib.Invoke(function_name, args, arg_names, respect_reflectable,
                      check_is_entrypoint));
  } 
  ...
}
複製程式碼

Dart_Invoke方法中,會先進行狀態檢查 ,然後拿到由java層傳遞過來的dart 入口函式對應的方法名(也就"main"),注意在這個方法中,不管是錯誤還是正確都是返回Dart_Handle這個對像。然後再看這個三個分析節點,根據上面的分析,將會執行節點3

//object.cc
RawObject* Library::Invoke(const String& function_name,
                           const Array& args,
                           const Array& arg_names,
                           bool respect_reflectable,
                           bool check_is_entrypoint) const {
    ...
    Function& function = Function::Handle(zone, LookupStaticFunction(function_name));
    ...
    return DartEntry::InvokeFunction(function, args, args_descriptor_array);  
    ...
}
複製程式碼

Invoke方法將會通過方法名拿到記憶體中Function物件,然後通過dart執行該方法。

這裡留意下DartEntry這個類,看原始碼的註釋大意是提取解析dart函式所需的功能的操作物件。是dart函式呼叫的重要物件之一,接著看看InvokeFunction做了什麼。

third_party/dart/runtime/vm/dart_entry.cc

RawObject* DartEntry::InvokeFunction(const Function& function,
                                     const Array& arguments,
                                     const Array& arguments_descriptor,
                                     uword current_sp) {
...
#if defined(TARGET_ARCH_DBC)
  //具體方法解析呼叫
  return Simulator::Current()->Call(code, arguments_descriptor, arguments,
                                    thread);
#elif defined(USING_SIMULATOR)
 //模擬器
  return bit_copy<RawObject*, int64_t>(Simulator::Current()->Call(
      reinterpret_cast<intptr_t>(entrypoint), reinterpret_cast<intptr_t>(&code),
      reinterpret_cast<intptr_t>(&arguments_descriptor),
      reinterpret_cast<intptr_t>(&arguments),
      reinterpret_cast<intptr_t>(thread)));
      ...
}
複製程式碼

InvokeFunction 中將先會對Function物件內容是否編譯過進行判斷(未編譯將編譯重新呼叫),拿到當前執行緒去執行。該方法還會區分生產環境,是否是模擬器等情況對方法進行解析,解析方法執行可以參考Simulator::Current()->Call,在Call的方法內我們可以看到整個方法非常龐大,光方法體就有幾千行程式碼,包含了常量值、位元組碼等的操作,所以這一篇文章就不展開分析。有興趣的朋友可以結合虛擬機器原理,看看這部分是如何執行的。

image

總結

至此我們大致看到了整個啟動過程,在java層主要是對flutter資源相關的引數進行了賦值、初始化,以及回撥方法的註冊,資源的拷貝,c++關鍵方法的呼叫,建立了通訊體系(各類Channel)。而在c++層,我們發現除去關鍵物件的建立,還有各類異常的處理(包含各種情況的考量),引數的解析,資源的解析,方法物件的構建等一系列的呼叫,最後通過dart vm的操作物件對方法進行解析與執行。

作者簡介

蘇哲,銅板街Android開發工程師,2017年12月加入團隊,目前主要負責APP端 Android 日常開發。

安卓平臺Flutter啟動過程全解析

更多精彩內容,請掃碼關注 “銅板街技術” 微信公眾號。

相關文章