Flutter在Android上的热更新方案

这篇具有很好参考价值的文章主要介绍了Flutter在Android上的热更新方案。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. 加载流程分析

flutter sdk版本:v3.0.3

dart版本:v2.17.5

1.1 FlutterApplication启动流程

按照安卓原生启动流程,当继承FlutterApplication且在AndroidManifest.xml将application:name设置后,除Content Provider初始化,Application流程将首先执行,此时FlutterApplication代码如下:

Flutter在Android上的热更新方案

FlutterApplication的onCreate只执行了FlutterLoader类下的startInitialzation,并把自己作为Context传入。

FlutterLoader对于startInitialzation有多个重载方法,此时将调用其中一个重载方法,并且生成了一个Settings传入。

Flutter在Android上的热更新方案

这时能看见startInitialization(@NonNull Context applicationContext, @NonNull Settings settings)才是实际执行的方法,方法前半段限制了该方法只能运行在主线程,如运行在非主线程则直接抛出异常。

新建了一个initTask的Callable类型的回调,并提交给了线程池执行。此时可以看见flutterJni.loadLibrary(),这里就是在加载libflutter.so,flutterJni该成员变量是FlutterInjector.instance()生成单例时,通过工厂+建造者生成的一个实例。

initResultFuture将作为线程任务执行的结果进行返回。

这说明了FlutterApplication执行过程中,最重要的就是在子线程中System.loadLibrary("flutter"),并且在线程执行完成后生成了initResultFuture,提供给后续操作。

Flutter在Android上的热更新方案

Flutter在Android上的热更新方案

1.2 FlutterActivity启动流程

如果已继承FlutterActivity作为LaunchActivity,且没有设置自定义的Application,将不会执行1.1的流程。

FlutterActivity时序图如下所示:

(下图出自得物技术专栏,链接:Flutter启动流程分析之插件化升级探索_configureflutterengine_得物技术的博客-CSDN博客)

Flutter在Android上的热更新方案

通过FlutterAcitivity的onCreate可以得知,大部分操作全部放在FlutterActivityAndFragmentDelegate中进行(代理模式)。

Flutter在Android上的热更新方案

deletegate.onAttach将自己注入到方法。此时由于成员变量flutterEngine必定为空,则执行setupFlutterEngine()。

Flutter在Android上的热更新方案

Flutter在Android上的热更新方案

进入setupFlutterEngine方法,此时可以通过getCachedEngineId,从FlutterEngineCache.getInstance()的map中,取出已经生成的flutterEngine实例,getCachedEngineId()方法具体实现在FlutterActivity。这里说明在FlutterActivity启动前预生成,可以有效减少onCreate()的时间。

当Cache和FlutterActivity子类中,都没有提供FlutterEngine时,将自己生成一个FlutterEngine,其参数将通过FlutterActivity子类提供。此时可以发现,通过FlutterActivity自动生成的Engine,automaticallyRegisterPlugins默认是为false的,这说明了此时需要手动注册插件,在预生成的Engine中可以设置为true。

Flutter在Android上的热更新方案

Flutter在Android上的热更新方案

通过FlutterEngine的构造方法,可以看见此时和FlutterApplication相似,生成了FlutterInjector的单例、flutterJni实例,并且初始化DartExecutor,生成了名为"flutter/isolate的channel,最后flutterJni与该executor的messager进行绑定。

此时由于flutterJni还未与Engine绑定,必定会执行FlutterLoader的startInitialzation方法,具体逻辑1.1中已经介绍。

之后将调用ensureInitialzationComplete的方法。

Flutter在Android上的热更新方案

该方法执行必定要在主线程中执行,且Settings不能为空。

此时可以发现,initResultFuture.get将挂起线程,并等待startInitialzation方法中执行的子线程结果后,才会继续向下执行。

这时定义了一个shellArgs,用于存放执行dart vm时携带的参数,并且可以将外部的args传入。

此时可以通过vm的参数,发现JIT和AOT将加载不同的产物:

共同加载:libflutter.so

JIT:vm_snapshot_data、isolate_snapshot_data、kernel_blob.bin

AOT:libapp.so、libvmservice_snapshot.so(Profile)

在AOT的代码段,注释:

// Most devices can load the AOT shared library based on the library name
// with no directory path. Provide a fully qualified path to the library
// as a workaround for devices where that fails.

这里说明了当第一段

"--" + AOT_SHARED_LIBRARY_NAME + "=" + flutterApplicationInfo.aotSharedLibraryName 路径加载不到库时,将自动加载第二段绝对路径

"--"
+ AOT_SHARED_LIBRARY_NAME
+ "="
+ flutterApplicationInfo.nativeLibraryDir
+ File.separator
+ flutterApplicationInfo.aotSharedLibraryName

此时可以推断,"--" + AOT_SHARED_LIBRARY_NAME + PATH,当加载成功库,将不再执行同参数的后续加载。

最后将用flutterJni.init将参数传递给nativeInit的jni映射方法中,在jni层启动.

进入Jni层,可以通过flutter_main.cc文件下的Jni方法映射,找到实际引用的函数引用Init。

在Init方法中,将java层传输过来的args,增加了名叫flutter的参数

然后通过SettingsFromCommandLine生成了Settings类

Flutter在Android上的热更新方案

Flutter在Android上的热更新方案

Settings类中大部分都是作为设置的逻辑,在SettingsFromCommandLine方法中可以找到,aot_shared_library_name为数组,说明可以同时存在多个路径。

并且全部存到Settings类的application_library_path中。

通过注解:

// Path to a library containing the application's compiled Dart code.
// This is a vector so that the embedder can provide fallback paths in
// case the primary path to the library can not be loaded.

可以得知,该属性将作为默认加载路径无法加载后的备选路径。

Flutter在Android上的热更新方案

通过dart vm的生命周期,dart_vm.cc文件中,Create方法会调用DartVMData的Create方法。

dart_vm_data.cc文件中的Create方法,将调用IsoSnapshotFromSettings函数,该函数中会调用SearchMapping函数。

SearchMapping函数中表明了会根据Path查找NativeLibrary,并且查找native_library_symbol_name符号名,如果查找到,则不会继续查找。此时可以证明只要Engine源码中,不改变该符号名的名称,则AOT产物替换加载方案为可行。

Flutter在Android上的热更新方案

Flutter在Android上的热更新方案

Flutter在Android上的热更新方案

2. 热更新Android方案

根据加载流程分析来看,如果不修改jni代码,仅仅在java层进行改动,主要是在FlutterApplication与FlutterActivity上进行hook,将对于libapp.so的加载地址进行修改。

2.1 对于FlutterApplication的Hook

由1.1分析可以得出,FlutterApplication执行过程中,已经对于FlutterLoader进行了初始化,此时FlutterLoader的flutterInfoApplication属性已经填充了值,此时可以执行之后,使用Reflect替换掉flutterInfoApplication里的内容,将FlutterActivity加载库的地址指向新的文件地址即可。

方案1:

描述:继承FlutterApplication,修改flutterInfoApplication的aotSharedLibraryName属性

侵入性:中

结果:无效

原因:

1.原flutterApplicationInfo的nativeLibraryDir指向的是/data/app,只读不可写权限且属于私有文件目录,无法移动下载的补丁文件到nativeLibraryDir的路径下。

2.如将aotSharedLibraryName的绝对路径,通过../强制转成补丁文件目录,会触发jni路径检查错误。

错误信息如下:

library "/storage/emulated/0/Android/data/packagename/files/Download/-712256458372815974.so" ("/storage/emulated/0/Android/data/packagename/files/Download/-712256458372815974.so") needed or dlopened by "/data/app/~~F_tPgp6PDbt9Ug0opdFRsg==/packagename-W65G60j2V9GbWfwGQd_upw==/lib/arm64/libflutter.so" is not accessible for the namespace: [name="classloader-namespace", ld_library_paths="", default_library_paths="/data/app/~~F_tPgp6PDbt9Ug0opdFRsg==/packagename-W65G60j2V9GbWfwGQd_upw==/lib/arm64:/data/app/~~F_tPgp6PDbt9Ug0opdFRsg==/packagename-W65G60j2V9GbWfwGQd_upw==/base.apk!/lib/arm64-v8a", permitted_paths="/data:/mnt/expand:/data/data/packagename"]

从Log中可以看见libflutter.so文件对于加载的路径,除了default_library_paths的,还有/data:/mnt/expand:/data/data/packagename 这2个路径。

Flutter在Android上的热更新方案

Flutter在Android上的热更新方案

方案2:

描述:继承FlutterApplication,修改flutterInfoApplication的aotSharedLibraryName、nativeLibraryDir属性

侵入性:中

结果:无效

原因:原flutterApplicationInfo的nativeLibraryDir指向的是/data/app,只读不可写权限且属于私有文件目录,如将libflutter.so转移到公共目录,成本过高。且公共目录会出现jni路径检查错误。具体原因可见方案1。

2.2 对于FlutterActivity的Hook

由1.2分析可以得出,即使onCreate执行后,也不能完全保证flutterApplicationInfo的属性被填充,必须等到子线程完成后才会注入数据,所以不可以通过2.1的操作,对于flutterApplicationInfo通过反射注入自定义路径。

通过1.2分析startInitialzationComplete方法,可以得出,FlutterActivity的args参数,可以被传入到方法中,与方法内的参数进行合并,并传入到jni层。并且此时注入到args的路径,必定顺序在源代码定义lib加载路径之前,由此可以得出自定义路径可以替代原路径进行加载。

方案1:

描述:继承FlutterActivity,重写getFlutterShellArgs方法,使得vm args中加入一条指向新文件的路径

侵入性:低

结果:有效

原因:通过1.2分析表明,jni层在根据路径查找so文件时,如果查找到文件,且符号名正确,将不会继续查找,所以修改getFlutterShellArgs传入自定义的文件路径,可以完成热更新的操作。

注意:此时当下载到新文件时,必须将新文件从公共路径(转移/写入)到/data/user/0/packageName路径以下,由上面link的日志可以看出,/data/data路径是可允许的加载路径,而/data/data是/data/user/0的软连接,且data/user/0/packageName路径下是拥有可读可写权限的。

Flutter在Android上的热更新方案

3.补丁下发方案设计

3.1补丁检查

方案1:

在登录账号时触发检测,通过登录时的接口来判定当时是否存在最新的补丁。

优点:结构简单

缺点:如app不关闭且不重新登录账号,则无法触发更新获取,时效性差。服务器无法保证即时通知到app进行更新。

流程图如下:

Flutter在Android上的热更新方案

方案2:

在登录账号时建立长链接,通过服务器推送来判定是否存在补丁。

优点:即时性高,服务器可以确保补丁下发过程中状态是否正常。

缺点:结构复杂,依赖于长链接的是否稳定。

流程图如下:

Flutter在Android上的热更新方案

3.2 版本管理

用户将上传自身的包版本、补丁包版本和包名。

补丁下发将存在2种模式:

1.全量下发

上传补丁后,将选择要发布的版本号,然后将上传补丁的分发至选择的版本号,如该版本用户本地补丁版本低于分发的补丁号,则将使用下发的补丁。

2.指定用户下发

上传补丁后,将针对指定用户下发补丁。如该版本用户本地补丁版本低于分发的补丁号,则将使用下发的补丁。文章来源地址https://www.toymoban.com/news/detail-470395.html

到了这里,关于Flutter在Android上的热更新方案的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • UnityC#脚本的热更新原理

    想要了解Unity的热更原理,必须要先了解Unity脚本的编译和跨平台机制。通常游戏的跨平台主要指安卓和IOS端。Unity的官方脚本语言是C#,但也有不少项目会采用C# + Lua语言的方式进行开发。它们主要有三种跨平台的形式:JIT、AOT、脚本语言。 Unity的C#代码在代码被打包时会被编

    2024年02月08日
    浏览(38)
  • Android 10.0 Settings 加载流程

    一、系统设置首页 代码路径:packages/app/Settings/ 1 主界面加载: Settings的主界面是Settings.java,但是从Settings.java来看,除了大量的静态类继承SettingsActivity,就无其他有效信息了。但看其xml定义可以发现targetActivity属性,实质应是SettingsHomepageActivity.java。 先看其xml配置: SettingsH

    2024年02月05日
    浏览(49)
  • Flutter Android 打包保姆式全流程 2023 版

    大家好,我是 17。 Flutter 打包的文章一共有两篇 Flutter Android 打包保姆式全流程 2023 版 Flutter IOS 新建打包发布全流程 2023 版 本篇介绍 Android 的打包全流程。 为什么要写这篇文章呢?对于一没有 android 开发经验,从未有过打包经历的新人来说,要想成功打包,是很困难的。因

    2023年04月08日
    浏览(80)
  • Unity 热更新方案和流程

    在开发商业游戏时,热更新是一个很重要的模块,这里讲的热更新不是指仅仅修复Bug,而是进行游戏功能的更新。简单来讲,就是启动游戏后,跑个条,下载资源和代码,然后再进入游戏。本篇博客所写的内容并不是最优的解,只是完成了热更新这个事情而已,具体使用还需

    2024年02月09日
    浏览(42)
  • Android Studio更新新版本后无法创建flutter项目

    最新更新了AndroidStudio版本,发现无法创建flutter项目。 dart和flutter插件确认都已安装,该有的环境配置都已配置。 最后与同事的插件作比较发现是Android APK Support这个插件没勾选。 勾选后,点击右下角的apply,重启AndroidStudio。 然后去点击File--New--New Flutter Project。

    2024年02月09日
    浏览(45)
  • Flutter网络请求框架Dio源码分析以及封装(一)--请求流程分析

    利用flutter开发app也已经有些时间了,这个过程中最多接触到的就是网络请求相关的代码。自己目前项目中使用的是现在市面上最流行的网络请求库-dio,相对于flutter自带的HttpClient来说,dio使用起来更简单,功能更强大,支持全局配置、Restful API、FormData、拦截器、 请求取消、

    2024年02月09日
    浏览(39)
  • uniapp 制作 wgt 包(用于 app 的热更新)

    修改 manifest.json 的配置, 应用版本名称 和 应用版本号 必须高于上一版的值。 打开 uni-admin 项目的升级中心 上传后会自动生成下载链接 发布新版后,用户打开app,后台会自动下载 wgt 包完成热更新 当用户再次打开app,便会看到更新后的app效果

    2024年01月21日
    浏览(37)
  • 【前端工程化面试题目】webpack 的热更新原理

    可以在顺便学习一下 vite 的热更新原理,请参考这篇文章。 首先有几个知识点需要明确 热更新是针对开发过程中的开发服务器的,也就是 webpack-dev-server webpack 的热更新不需要额外的插件,但是需要在配置文件中 devServer 属性中配置 hot: true,需要安装 webpack-dev-server 这个

    2024年02月19日
    浏览(59)
  • UE5热更新:Pak包的Cook、打包、加载全流程及踩坑经验分享

    探索UE5中Pak包加载的全流程,包括Cook、打包、加载等步骤,并分享在实践中遇到的一些坑和解决方法。跟随本教程,了解UE5与UE4的不同之处,以及如何成功实现热更新功能。

    2024年02月03日
    浏览(59)
  • gin的热启动方案-air

    使用gin搭建服务的时候,在开发环境修改代码,要关闭服务,再重新打开代码才能生效。 频繁修改再打开是一个很低效的事情。所以需要一个工具,在修改完代码之后可以热启动。 可以使用 air来完成。 air 不会自动配置路径,需要我们添加 然后添加一个air的配置文件 执行命

    2024年02月14日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包