深入解析 Flutter 初始化流程-程序员宅基地

技术标签: shell  移动开发  

在调研 Flutter 动态化方案的时候,需要了解 Flutter 加载 dart 产物的流程,于是梳理了一遍 FLutter 的初始化流程

flutter的源码下载地址在 github 上可以找到,具体地址: github-flutter/engine

FLutterMain的初始化

先从 Android 的入口开始看

在 FlutterAppliation 的 onCreate 中调用了

FlutterMain.startInitialization(this);

跟进去我们会看到调用了 startInitialization 方法,最后会顺序调用这几个方法

initConfig(applicationContext);
initAot(applicationContext);
initResources(applicationContext);

我们查看 initResources 方法如图

这里我们可以看到实际加载了assets里面的flutter资源。并且会把资源 copy 到本地的�路径。这里不做深究。 FlutterMan 的初始化基本包括了

  • 初始化配置
  • 初始化 AOT 编译
  • 初始化资源

3 个部分

继续看 � Flutter 的 View 的初始化:

FLutterView的初始化

以 FlutterActivity 为例,在 onCreate 中会调用到 FlutterActivityDelegate 的对应方法,最终调用 FlutterView 的 runFromBundle 方法

public void runFromBundle(FlutterRunArguments args) {
    this.assertAttached();
    this.preRun();
    this.mNativeView.runFromBundle(args);
    this.postRun();
}

跟踪这段代码,会调用 FlutterNativeView 的 nativeRunBundleAndSnapshotFromLibrary 方法。

这里会继续进行 � jni 层的调用,�查看 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),
},

查看 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));
    } else {
      asset_manager->PushBack(
          std::make_unique<blink::DirectoryAssetBundle>(fml::OpenDirectory(
              bundlepath.c_str(), false, fml::FilePermission::kRead)));
      const auto last_slash_index = bundlepath.rfind("/", bundlepath.size());
      if (last_slash_index != std::string::npos) {
        auto apk_asset_dir = bundlepath.substr(
            last_slash_index + 1, bundlepath.size() - last_slash_index);

        asset_manager->PushBack(std::make_unique<blink::APKAssetProvider>(
            env,                       // jni environment
            jAssetManager,             // asset manager
            std::move(apk_asset_dir))  // apk asset dir
        );
      }
    }
  }      
  auto isolate_configuration = CreateIsolateConfiguration(*asset_manager);    
  RunConfiguration config(std::move(isolate_configuration),
                          std::move(asset_manager));  
    ANDROID_SHELL_HOLDER->Launch(std::move(config));

首先会对资源路径进行处理 会�分为 zip 包或者文件夹进行分别处理。最终会调用常量 ANDROID_SHELL_HOLDER的 Launch 函数.

最终走到 engine 的 Run 函数。

这里有 2 个函数比较重要,先是 IsolateConfiguration::PrepareIsolate , 然后是 RunFromLibrary 或者 Run 函数

跟到 PrepareAndLaunchIsolate 函数,查看源码

bool IsolateConfiguration::PrepareIsolate(blink::DartIsolate& isolate) {
  if (isolate.GetPhase() != blink::DartIsolate::Phase::LibrariesSetup) {
    FML_DLOG(ERROR)
        << "Isolate was in incorrect phase to be prepared for running.";
    return false;
  }

  return DoPrepareIsolate(isolate);
}

而有 DoPrepareIsolate 函数的类 Configuration 类有3个

  • AppSnapshotIsolateConfiguration
  • KernelIsolateConfiguration
  • KernelListIsolateConfiguration

他们分别会调用 DartIsolate 的

  • PrepareForRunningFromPrecompiledCode
  • PrepareForRunningFromKernel

这2个方法的一个,可以�看到这里的 prepare 操作分成了 预先加载的代码 和 从内核获取 2种

至于 RunFromLibrary 函数和 Run 函数

我们能看到�他们最终都会调用 dart:isolate 和 _startMainIsolate 的逻辑:

Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate"));
if (tonic::LogIfError(Dart_Invoke(
          isolate_lib, tonic::ToDart("_startMainIsolate"),
          sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) {
    return false;
  }

这里说明我们正在执行调用 Dart 的入口方法。而 Run 和 RunFromLibrary 的区别,则是如果我们传入了 entrypoint 参数去进行 Flutter 的 bundle 初始化的时候,则会去加载我们制定的 library。

小结

到这里, Flutter 的初始化流程就就简单的分析了一遍。大致可以总结成三个部分

  1. 初始化 FlutterMain
  2. 初始化 FlutterView,开始加载 bundle
  3. 初始化Flutter Bundle,这里获取了 Flutter 的入口方法、Flutter 的 library, 以及对 Flutter 入口方法的调用。

初始化的逻辑比较复杂,对后续一些初始化相关的性能优化应该也会有不小的启发。 FlutterMain 中对资源的处理和写入本地的逻辑也给 Android 端研究 Flutter 动态化提供了基础。

有需要Android进阶全面系统视频资料的可以加入Android进阶交流群;701740775。免费获取 加群请备注csdn领取进阶资料

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34015336/article/details/89569655

智能推荐

OpenJudge 东方14ACM小组 / 20170123 06:Challenge 3_openjudge怎么加入小组-程序员宅基地

文章浏览阅读507次。总时间限制: 10000ms 单个测试点时间限制: 1000ms 内存限制: 262144kB描述给一个长为N的数列,有M次操作,每次操作是以下两种之一:(1)修改数列中的一个数(2)求数列中某连续一段的和输入第一行两个正整数N和M。第二行N个整数表示这个数列。接下来M行,每行开头是一个字符,若该字符为'M',则表示一个修改操作,接下来两个整数x和y,表示把x位置的值修改为y;若该_openjudge怎么加入小组

java.lang.ClassNotFoundException: org.springframework.integration.config.HandlerMethodArgumentResolv_caused by: java.lang.classnotfoundexception: org.s-程序员宅基地

文章浏览阅读2.8k次。记录一个问题,微服务单体集成消息总线依赖, <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-bus-amqp</artifactId> </dependency>再次启动项目会出..._caused by: java.lang.classnotfoundexception: org.springframework.integration

双系统安装Ubuntu(16.04)(含重装教程)_天选3安装ubuntu16.04双系统教程-程序员宅基地

文章浏览阅读2.6k次,点赞13次,收藏49次。双系统安装Ubuntu(16.04)正文制作启动盘给Ubuntu分硬盘空间安装Ubuntu系统版权声明:本文为博主原创文章,转载请附上原文出处链接和本声明。笔者之前装过Ubuntu的双系统,现在是重装,后面有详细介绍由于网上很多教程都不太靠谱,所以特意重新装了一遍写了此教程,留备以后查看。本教程尽量做到详细、傻瓜式。正文制作启动盘准备一个4G以上的空U盘去Ubuntu官网下载系统镜..._天选3安装ubuntu16.04双系统教程

玩一下shiro.demo支持Jetty,支持Mybatis最新版本_jetty shiro-程序员宅基地

文章浏览阅读476次。https://www.sojson.com/shiro里面有个关于Shiro的Demo,只支持tomcat分析代码和依赖,修改了几下,支持jetty启动登录使用同时支持mybatis3.5.2,shiro1.4.1,并把使所有日志归集到slf4j日志框架,运行时log4j2Log4j2使用2.8.2的版本,适配tomcat7,版本过高就不能用tomcat7的maven插件启动了w..._jetty shiro

BaiChuan-QWen_ffn hidden size为啥是4倍-程序员宅基地

文章浏览阅读259次。总结一下百川和千问的训练技术报告对要点_ffn hidden size为啥是4倍

Qt-ubuntu20.04下+gcc-64编译qt5.15.2的mysql驱动_linux qt5.15 mysql驱动-程序员宅基地

文章浏览阅读1.3k次。导读qt5.15.2不管在哪个操作系统下都是不自带mysql驱动的,想在ubuntu的qt下使用mysql,必须手动编译,才能使用,不过相对于windows来说,linux 比较方便。编译下载mysql(已安装请忽略)下载链接:mysql下载,下载如下版本安装mysql输入如下命令, 为后面下载mysql配置一些版本,记得勾选8.0版本的,不然后面安装可能出现意外sudo dpkg -i mysql-apt-config_0.8.22-1_all.deb下面开始安装mysq_linux qt5.15 mysql驱动

随便推点

html中给元素添加背景图片或者gif动图-程序员宅基地

文章浏览阅读1.2w次,点赞5次,收藏29次。添加背景图片有四种常用的方式,分别是:repeat 完全平铺repeat-x 横向平铺repeat-y 纵向平铺no-repeat 不平铺使用示例:<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> ..._html背景动图怎么添加

Linux系统下C语言编程--使用gcc/Makefile编译执行C程序_linux makefile 编译c-程序员宅基地

文章浏览阅读942次。基础知识源程序编译使用 gcc 编译程序int main(int argc, char** argv){ printf("Hello Linux\n");}要编译这个程序,执行以下命令gcc -o hello hello.cgcc 编译器会为我们生成一个可执行文件 hello。gcc 参数选项-o 表示我们要求编译器为我们输出可执行文件。-c 表示我们只要求编译起输出目标代码,而不必输出可执行文件。-g 表示我们要求编译器在编译的时候提供对程序的调试信息。MakeFile_linux makefile 编译c

IOS学习-UISlider(滑块控件)_ios uislider-程序员宅基地

文章浏览阅读543次。学习用学习用UISlider空间的常用属性minimumValue属性:设置滑块的最小值maximumValue属性:设置滑块的最大值UIImage属性:为滑块设置表示放大和缩小的图像素材使用slider控件的基本方法创建滑块是一个标准的UIControl,我们可以通过来创建它,例如:UISlider* mySlider = [[UISlider alloc] initW..._ios uislider

SQL Server Management Studio (SSMS)单独安装,仅安装连接工具-程序员宅基地

文章浏览阅读6.1w次,点赞13次,收藏43次。简单来说,SSMS是用于远程连接数据库与执行管理任务的一个工具。当安装SQL SERVER时,会默认安装。但也可以单独安装在不是数据库服务器的主机上。SQL Server Management Studio(SSMS) 是用于管理任何 SQL 基础结构的集成环境。使用 SSMS,可以访问、配置、管理和开发SQL Server、Azure SQL 数据库和 SQL 数据仓库的所有组件。SSMS 在一个综合实用工具中汇集了大量图形工具和丰富的脚本编辑器,为各种技能水平的开发者和数据库管理员提供对...._ssms

发那科机器人网段_FANUC 机器人应用项目-程序员宅基地

文章浏览阅读324次。发那科机器人应用项目大揭秘FANUC iR PickTool是对传送带上的工件使用相机或光电传感器等设备进行检测,机器人配合传送带的运动对工件进行动态拾取的定制化软件。该软件提供了多台机器人共享一条传送带上工件数据信息的应用,支持一台机器人同时对应多条传送带进行跟踪的应用。在iR PickTool软件应用中,机器人可以通过队列管理或视觉跟踪实现对传送带上运动的工件追踪功能。利用iR PickToo..._发那科r-30ib 寄存器地址映射

Scala与Java8的Future转化_scala/compat/java8/futureconverters$futureops$-程序员宅基地

文章浏览阅读1.2k次。在学习Akka时,自然会接触到scala语言,对于用惯了java的程序员来说,使用JAVA的Future的特性更为熟练。所以有了本篇文章。本节主要讨论feature在这两种语言中的转换与使用。 &lt;dependency&gt; &lt;groupId&gt;org.scala-lang.modules&lt;/groupId&gt; ..._scala/compat/java8/futureconverters$futureops$

推荐文章

热门文章

相关标签