Изучение динамического решения Flutter

Flutter

1. Предпосылки

С развитием мобильных платформ масштабы мобильных пользователей становятся все больше и больше, соответственно растет и спрос на продукты. Чтобы решить многие быстро итерационные бизнес-линии и потребности и повысить эффективность нашей разработки, коллеги в отрасли попытались изучить множество кросс-платформенных решений.Сегодня наиболее распространенными решениями являются примерно следующие. Такие как:

  1. Реагировать нативно;
  2. Векс;
  3. гибридное приложение;
  4. Трепетание;
  5. апплеты;

Вышеупомянутые решения имеют более или менее узкие места или дефекты в сценариях использования, поэтому я не буду их здесь обсуждать. Основная информация для сравнения приведена ниже для справки:

Название схемы React Native Weex Hybrid App Flutter Апплеты
Реализация платформы JS JS нет моста нет моста нет моста
двигатель JSCore JS V8 родной рендеринг Flutter engine -
основной язык React Vue Java/Obeject-C Dart WXML
Размер APK (релиз) 4-6М или около того около 10м - 8-10М или около того -
размер файла пакета Одинарная по умолчанию, крупнее Небольшой, многостраничный многофайловый ненужный ненужный ненужный
Трудно начать (родной угол) легкий в общем в общем легкий легкий
степень обрамления тяжелее легче Тяжелый тяжелее легче
Функции Подходит для разработки общего приложения Подходит для одной страницы Подходит для разработки общего приложения Подходит для разработки общего приложения Подходит для разработки общего приложения
Сообщество Богатый (Фейсбук) Генерал (Али) в общем Богатый (Гугл) Общие (WeChat)
Кроссплатформенная поддержка Андроид, iOS Андроид, iOS Андроид, iOS Android, iOS, веб, фуксия и т. д. Андроид, iOS

В качестве кросс-платформенного решения, которое быстро развивалось за последние два года, Flutter вошел в сферу нашего исследования, и мы в основном будем измерять его по следующим аспектам:

  1. Трудность доступа.
  2. стоимость обучения.
  3. представление.
  4. объем пакета.
  5. Динамическая способность.

2. Исследуйте динамические решения

Что касается предыдущих аспектов, я считаю, что у всех уже есть некоторое понимание при контакте с Flutter, поэтому я не буду подробно обсуждать это здесь. В качестве кросс-платформенного решения динамизация является одной из наиболее важных функций.Проверив информацию, перелистывая документы и обсуждения в технических группах, было обнаружено, что в настоящее время во Flutter существуют следующие три схемы реализации:

  1. Аналогичен фреймворку React Native.
  2. Заменяет скомпилированные продукты Flutter.
  3. Структура динамического компонента страницы.

三种实现方案

Далее мы кратко представим конкретные принципы реализации этих схем.

1. Схема динамического компонента

В настоящее время большинство технических команд на рынке используют идею динамических компонентов страницы для достижения динамики, например Xianyu, Vipshop, Toutiao и т. д. Основной принцип этого решения заключается в том, что перед упаковкой приложения, например вставкой/покупкой DynamicWidget в код во время компиляции, затем динамически доставляются данные Json, сопоставляются данные в JSON с помощью согласованной семантики и динамически заменяются виджеты. content Для достижения динамизации (в дополнение к пользовательскому интерфейсу, если вам нужно добиться динамизации логического кода, вы можете написать бизнес-логику с помощью более динамичного языка сценариев, такого как Lua).

Обобщенные характеристики следующие:

  1. Там было много на рынке с аналогичной структурой зрелости, таких как рысь TanGram, Taobao Dinamicx и так далее. Он сделал относительно хороший баланс между эффективными и динамическими затратами на развитие. Он может удовлетворить динамические потребности общих ситуаций в определенной степени, смогут решать практические проблемы.
  2. Поддержка Android / динамически заканчивается iOS.
  3. Относительно легко сделать пользовательский интерфейс динамичным, но с бизнес-логикой сложнее.
  4. Семантические синтаксические анализаторы относительно дороги в разработке и сложны в обслуживании.

1.1 О синтаксическом дереве

Такие фреймворки, как Tangram и DinamicX, имеют одну общую черту: все они используют Xml или Html в качестве DSL. Но Flutter — это синтаксис React Style, и собственный синтаксис Flutter уже может очень хорошо выражать страницы. Таким образом, эта схема не требует специального синтаксиса. Вы можете использовать исходный код Flutter в качестве DSL. Это значительно упрощает процесс разработки и тестирования и не требует дополнительной инструментальной поддержки.

Анализ Flutter анализирует исходный код, чтобы получить процесс ASTNode:

Как видно из рисунка выше, плагин или команда инициирует запрос к серверу анализа.Запрос содержит путь к анализируемому файлу и тип анализа.Analyze_server получает commilationUnit (ASTNode) с помощью package: Analyzer, а затем анализирует ASTNode компьютером и возвращает список результатов анализа.

Согласно принципу Flutter, мы также можем использовать package:analyzer для преобразования исходного файла в commentUnit (ASTNode), ASTNode — это абстрактное синтаксическое дерево (абстрактное синтаксическое дерево или сокращенно AST) — это древовидное представление абстрактной синтаксической структуры. исходного кода, используя абстрактное синтаксическое дерево, можно очень хорошо анализировать исходный код Dart.

Недостатки программы:

Необходимо сформулировать правила для формата исходного кода, например, не поддерживается запись if else напрямую, а вместо оператора if else необходимо использовать компонент логического виджета. Если правила не сформулированы, процесс парсинга от узла AST к узлу виджета будет сложным. Следовательно, можно ввести lua для реализации динамизации логического кода.

1.2 Общее планирование архитектуры решения json + lua:

Flutter 动态组件框架设计图.jpg

Решение с открытым исходным кодом:

GitHub.com/Big Hot Sky-Wolf/Is…

GitHub.com/equal печатает 2000…

luakit_plugin:GitHub.com/Уильям Аск 1…

Использованная литература:

Жаркий день.Dev/tools/Сломай его...

Особенно Aliyun.com/articles/67…

2. RN-подобное решение (пакет JS)

Ссылаясь на идеи дизайна React Native, можно сделать вывод, что использование JavasSriptCore для замены DartVM, использование JavaScript (сокращенно JS) для преобразования XML DSL в атомарные компоненты виджета Flutter, а затем предоставление возможности рендеринга Flutter. Это технически осуществимо, но стоимость также огромна, что будет огромным проектом.

В частности, первое дерево (то есть: WidgetTree) из трех деревьев (то есть: WidgetTree, Element, RenderObject) в логике рендеринга Flutter помещается в JS для генерации. Инкапсуляция уровня управления Flutter полностью реализована с помощью JS, и приложения Flutter можно разрабатывать с использованием JS в методе разработки, подобном Dart. Используя облегченную версию Flutter Runtime для JavaScript, описание пользовательского интерфейса генерируется и передается в механизм пользовательского интерфейса слоя Dart, а затем механизм пользовательского интерфейса создает реальный элемент управления Flutter из описания пользовательского интерфейса.

Решение с открытым исходным кодом команды Mobile QQ Kandian:"Высокопроизводительный динамический фреймворк Flutter на основе JS"

Дефект схемы: независимо от того, насколько быстро создается JSWidget, всегда существует межъязыковое выполнение, которое всегда будет влиять на производительность. Кроме того, поскольку система iOS имеет встроенную поддержку JS, она полностью динамическая на iOS, но на стороне Android необходимо ввести дополнительную библиотеку JS. В настоящее время решение MXFlutter реализует только динамику версии для iOS, и его сложнее реализовать.

3. Заменить составленную схему продукта

Для достижения продуктов динамической компиляции на платформе Android он будет ограничен JIT-кодом, а на платформе iOS — интерпретируемым кодом. Команда Google Flutter раньше пыталась предоставить официальное решение, но сдалась и откатила код. Что говорят, так это то, что нет большой уверенности в производительности такого решения с ограничениями платформы на платформе iOS может оправдать ожидания (проще говоря, работать в системе iOS будет невыносимо. , потому что iOS не похож на андроид, это может напрямую загружать динамическую библиотеку, поэтому необходимо загрузить статическую библиотеку) Поэтому, если этот метод замены скомпилированного продукта будет принят, в настоящее время его можно использовать только на стороне Android.

Прежде всего, мы должны знать, что такое скомпилированный продукт Flutter.Так же, как мы знаем, что скомпилированный продукт Android — это файл dex, цель динамизации может быть достигнута путем кражи процесса загрузки файла dex. Итак, давайте взглянем на скомпилированные продукты Flutter, здесь следует отметить, что текущая скорость обновления Flutter слишком высока, а скомпилированные продукты разных версий не согласованы.

3.1 Инструкции по компиляции Flutter

(1) Скомпилируйте движок apk и aar по умолчанию

// 编译纯Flutter apk,默认是release版本
flutter build apk

// 编译纯debug版apk
flutter build apk --debug

// 编译 aar, 默认是release 版本
flutter build aar

// 编译aar, 默认是debug版本
flutter build aar --debug

Что касается скомпилированных инструкций, вы можете просмотреть их через команду flutter build -h, как показано на следующем снимке экрана:

(2) Скомпилируйте apk и aar, чтобы указать локальный движок

  • О том, как скомпилировать движок, вы можете прочитать в этой статье [Ubuntu 16.04 Compiling Flutter Engine](/home/lichaojian/Documentation/Ubuntu 16.04 Compiling Flutter Engine.md)

  • Как сослаться на локальный движок

// 指定引用本地的引擎去编译apk,适用于纯Flutter应用
flutter build apk --target-platform android-arm64 --local-engine=android_release_arm64 --local-engine-src-path=/home/lichaojian/engine/src

// 指定引用本地的引擎去编译aar,适用于Flutter & Native 的混编项目
flutter build aar --target-platform android-arm64 --local-engine=android_release_arm64 --local-engine-src-path=/home/lichaojian/engine/src

(3) Просмотрите исходный код инструкций по компиляции Flutter.

На самом деле, компилируем ли мы apk или aar, мы используем команду флаттера, поэтому проверьте, каков на самом деле исходный код этой команды флаттера. Затем мы можем проверить /your_flutter_sdk_path/bin/flutter и открыть файл флаттера, Основное предложение в нем выглядит следующим образом:

FLUTTER_TOOLS_DIR="$FLUTTER_ROOT/packages/flutter_tools"
SNAPSHOT_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.snapshot"
STAMP_PATH="$FLUTTER_ROOT/bin/cache/flutter_tools.stamp"
SCRIPT_PATH="$FLUTTER_TOOLS_DIR/bin/flutter_tools.dart"
DART_SDK_PATH="$FLUTTER_ROOT/bin/cache/dart-sdk"

DART="$DART_SDK_PATH/bin/dart"
PUB="$DART_SDK_PATH/bin/pub"

# FLUTTER_TOOL_ARGS isn't quoted below, because it is meant to be considered as
# separate space-separated args.
"$DART" --packages="$FLUTTER_TOOLS_DIR/.packages" $FLUTTER_TOOL_ARGS "$SNAPSHOT_PATH" "$@"
  • $DART: запустить виртуальную машину дротика
  • $SNAPSHOT_PATH: укажите исполняемый файл моментального снимка, путь /your_flutter_sdk_path/bin/cache/flutter_tools.snapshot
  • $@: параметр, который вы передали (например: build apk)
// 从上面可以看出,平时我们运行的
flutter build apk

// 实际上是
/your_flutter_sdk_path/bin/cache/dart-sdk/bin/dart /your_flutter_sdk_path/bin/cache/flutter_tools.snapshot build apk

Выполните приведенную выше команду, как показано на скриншоте ниже, apk успешно скомпилирован, и для aar используется тот же принцип:

  • Затем взгляните на исходный файл flutter_tools.snapshot, расположенный в /your_flutter_sdk_path/flutter/package/flutter_tools/bin/flutter_tools.dart.

Как видно из скриншота выше, на самом деле вызывается метод executable.main, далее давайте посмотрим на executable.dart

Видно, что бегун запускает здесь ряд классов Command, и, конечно же, мы знакомы с командой flutter build, поэтому мы видим, что команда flutter build соответствует BuildCommand.

Как видно из приведенного выше рисунка, на самом деле BuildCommand состоит из множества подкоманд, таких как aar, apk, aot и т. д., все они являются подкомандами BuildCommand.

Если вы хотите узнать больше о процессе упаковки и компиляции Flutter, рекомендуется просмотреть [Изучение Flutter — Подробное объяснение процесса упаковки и компиляции]

3.2 Отличия скомпилированных продуктов под разные версии Flutter

(v1.5.4-исправления и v1.9.1)

  • V1.5.4-hofixed

(1) режим отладки

image-20191026064033993

(2) режим выпуска

image-20191026064004887

  • v1.9.1

(1) режим отладки

(2) режим выпуска

Как видно из вышеприведенных скриншотов, в режиме отладки продукты v1.5.4-hofixes и v1.9.1 почти не изменились, мы не будем здесь обсуждать отладочную версию, на которую можно не обращать внимания, но мы можем найти разницу между ними следующим образом:

Продукт в режиме выпуска исправлений v1.5.4

  • isolate_snapshot_instr
  • isolate_snapshot_data
  • vm_snapshot_data
  • assets/vm_snapshot_instr

Продукты в режиме выпуска v1.9.1

  • libapp.so

Анализы Продукт режима выпуска V1.9.1 разделен на несколько:

  • /lib/libapp.so — это в основном исполняемый файл, созданный при компиляции Dart.
  • /lib/libflutter.so в основном хранит исполняемые файлы Flutter Engine.
  • /assets/flutter_assets в основном хранит некоторые файлы ресурсов флаттера, такие как шрифты, изображения и т. д.

Видно, что после версии v1.9.1 продукты компиляции кода Flutter стали более едиными, что полезно для нашего динамического исследования.Мы знаем, что libapp.so по своей природе поддерживает динамическое связывание. Это означает, что мы можем заменить файл libapp.so для достижения цели динамического. Сначала Lisen заменила продукт, укоренив телефон напрямую, и обнаружила, что он поддерживается, а затем у нас было продолжение.

Поскольку он поддерживает замену libapp.so для достижения динамических обновлений, как мы реализуем его через код?

3.3 Как Flutter динамически заменяет скомпилированные продукты?

Из вышеизложенного мы можем узнать, что представляет собой скомпилированный продукт Flutter, поэтому мы можем добиться динамического эффекта, динамически указав путь для загрузки скомпилированного продукта в коде, так как же нам изменить код? Есть два способа:

(1), Изменяя способ трепетания двигателя.

преимущество:

  • Облегчает знакомство с кодом Engine.
  • Настраиваемый механизм расширения.

недостаток:

  • Код двигателя более инвазивен.
  • Вам необходимо поддерживать свой собственный движок и предоставлять его извне.
  • Движок нуждается в обновлении, регулярно синхронизируйте официальный код.

(2) Через крюк.

Преимущества: Менее инвазивный код Engine.

Недостатки: SDK нужно поддерживать, при обновлении версии движка необходимо следить, не нужно ли заменить точку хука.

3.3.1 Процесс замены so-файла

  • (1) Как Android загружает файл so.

    С помощью продуктов компиляции, представленных выше, видно, что в релизной версии под Android флаттер будет скомпилирован в файл libapp.so, так как же Android сам загружает файл so? В основном делятся на следующие два типа:

// 默认加载路径加载,对应~/app/libs
System.loadLibrary("libname")
    
// 通过绝对路径进行加载
System.load("/your_so_path/libupdate.so")

Основное различие между этими двумя методами заключается в том, что loadLibrary загружает файл so в каталог libs в приложении и загружает его, загружая его абсолютный путь.

По поводу принципа загрузки файлов .so в Android можно глянуть на gityuanАнализ процесса загрузки динамической библиотеки LoadLibrary, также см

Глубокое понимание System.loadLibraryЭта статья.

Проще говоря, все это делается путем вызова функций из заголовочного файла dlfcn.h следующим образом:

void *dlopen(const char *filename, int flag);  //打开动态链接库
char *dlerror(void);   //获取错误信息
void *dlsym(void *handle, const char *symbol);  //获取方法指针
int dlclose(void *handle); //关闭动态链接库  

Поняв, как Android загружает so-файлы, давайте посмотрим, как Flutter загружает so-файлы.

  • (2) Как Flutter загружает такие файлы.
  1. Инициализируйте Flutter, и, глядя на исходный код, мы знаем, что есть два метода, которые необходимо вызвать.

    FlutterMain.startInitialization(@NonNull Context applicationContext)
    FlutterMain.ensureInitializationComplete(@NonNull Context applicationContext, @Nullable String[] args)
    

Здесь многое упущено.... Прямо к делу....

native_library_posix.cc

NativeLibrary::NativeLibrary(const char* path) {
  ::dlerror();

  FML_LOG(ERROR)<< "lichaojian-path = " << path;
  
  handle_ = ::dlopen(path, RTLD_NOW);
  if (handle_ == nullptr) {
    FML_DLOG(ERROR) << "Could not open library '" << path << "' due to error '"
                    << ::dlerror() << "'.";
  }
}

fml::RefPtr<NativeLibrary> NativeLibrary::Create(const char* path) {
  auto library = fml::AdoptRef(new NativeLibrary(path));
  FML_LOG(ERROR)<< "lichaojian-Create = " << path;
  return library->GetHandle() != nullptr ? library : nullptr;
}

Как видно из приведенных выше инструкций, dlopen фактически вызывается для загрузки библиотеки so. Итак, зная этот принцип, мы знаем, как с ним бороться.Логи, которые я добавил к этим двум функциям, выводятся следующим образом:

Зная принцип, способ, которым Flutter динамически загружает файлы, в основном делится на две части:

(1) родной слой

通过更改native层代码,让native层判断某个预定好的路径是否存在更新的文件,存在的话,则进行加载更新的文件,不存在的话,则加载原来的libapp文件。

(2) слой Java

Только что в методе FlutterMain#ensureInitializationComplete мы видим, что среди параметров, связанных с libapp, очень важны две строки кода.

private static final String DEFAULT_AOT_SHARED_LIBRARY_NAME = "libapp.so";
private static String sAotSharedLibraryName = DEFAULT_AOT_SHARED_LIBRARY_NAME;

shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + sAotSharedLibraryName);

                // 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.
                shellArgs.add("--" + AOT_SHARED_LIBRARY_NAME + "=" + applicationInfo.nativeLibraryDir + File.separator + sAotSharedLibraryName);

Из приведенного выше кода видно, что aot_share_library_name и его путь загружаются в параметр shellArgs, поэтому мы можем динамически загружать их, изменяя этот путь и имя на уровне java. Почему даже имя должно быть изменено?Если имя не изменено, когда имя libapp.so найдено, оно будет напрямую сопоставлено с файлом libapp.so в каталоге lib, поэтому динамическая загрузка so не удастся.

Динамическая загрузка достигается заменой so-файла, путь к которому изменил свое имя.

3.3.2 Замена ресурсов

О флаттер-ресурсах

По сравнению с управлением ресурсами в системе Android, управление ресурсами во флаттере слишком простое. Ресурсы во флаттере не компилируются и не выставляются в виде исходных файлов. Чтобы получить ресурсы, нужно прочитать файлы. содержимое ресурса в памяти, а ресурс идентифицируется по имени каталога + имени файла следующим образом:

О трепетаре Assetmanager

Внутри движка флаттера также есть AssetManager, а исходный путьflutter/assets/asset_manager.hВ AssetManager не так много кода, он просто поддерживается внутри.AssetResolverОчередь из , основной метод имеет два

//往队列里面添加一个AssetResolver
void AssetManager::PushBack(std::unique_ptr<AssetResolver> resolver) {
  if (resolver == nullptr || !resolver->IsValid()) {
    return;
  }

  resolvers_.push_back(std::move(resolver));
}

//检索资源
std::unique_ptr<fml::Mapping> AssetManager::GetAsMapping(
    const std::string& asset_name) const {
  if (asset_name.size() == 0) {
    return nullptr;
  }
  TRACE_EVENT1("flutter", "AssetManager::GetAsMapping", "name",
               asset_name.c_str());
  for (const auto& resolver : resolvers_) {
    auto mapping = resolver->GetAsMapping(asset_name);
    if (mapping != nullptr) {
      return mapping;
    }
  }
  FML_DLOG(WARNING) << "Could not find asset: " << asset_name;
  return nullptr;
}

Из кода видно, что реальный ресурс на самом деле генерируетсяAssetResolverкоторый предоставил.

О флаттере AssetResolver

AssetResolver — это класс интерфейса, поставщики ресурсов флаттера должны реализовать этот интерфейс в качестве источника.flutter/assets/asset_resolver.hНиже определения примерно такие

namespace flutter {

class AssetResolver {
 public:
  // 无关重要的被我省略了。。。
  virtual std::unique_ptr<fml::Mapping> GetAsMapping(
      const std::string& asset_name) const = 0;

 private:
  FML_DISALLOW_COPY_AND_ASSIGN(AssetResolver);
};

}  // namespace flutter

Наиболее важным из которых являетсяGetAsMappingметод, который возвращает файлMapping,MappingЭто тоже интерфейсный класс, его определение тоже очень простое, исходный код находится вflutter/fml/mapping.hНиже, здесь прямо дается

class Mapping {
 public:
  // 无关重要的被我省略了。。。
  virtual size_t GetSize() const = 0;
  virtual const uint8_t* GetMapping() const = 0;
};

вGetSizeВозвращает размер файла,GetMappingВозвращается адрес ресурса в памяти.Структура управления всем ресурсом примерно такая, как показано на следующем рисунке:

О флаттере APKAssetProvider

APKAssetProvider реализует интерфейс AssetResolver и предоставляет флаттеру возможности получения ресурсов под платформу Android.Суть его в том, чтобы передать AssetManager слоя Java черезAAssetManager_fromJavaИнтерфейс преобразуется в уровень C++, а затем черезAAssetManager_open AAsset_getBuffer AAsset_closeПодождите, пока интерфейс NDK прочитает ресурсы активов, Исходный путь находится вflutter/shell/platform/android/apk_asset_provider.hНиже кода не так много, вот непосредственно вызывающий процесс

Несколько схем динамического развертывания ресурсов флаттера

  • Платформа Android

Благодаря предыдущему анализу кода мы ясно видим, что на платформе Android ресурсы флаттера на самом деле состоят изAssetManagerПри условии, что мы можем учиться на принципе горячего ремонта (на самом деле, это намного проще, чем горячий ремонт, потому что здесь нам не нужно делать полный синтез, достаточно сделать полуполный синтез, и нет необходимости заменять системаAssetManagerпросто настройтесьaddAssetPathпросто хорошо). Конечно, с помощью этого решения необходимо устранить ограничение Android 9 на частные API.

  • Кроссплатформенная универсальная способ

Недостатки вышеприведенной схемы очевидны.Во-первых, она может удовлетворить только платформу Android.Во-вторых, она должна решить системные ограничения на приватные API.На самом деле, прежде чем делать первую схему, автор уже реализовал слой C++. процесс и принцип кросс-платформенного общего метода также очень просты.Благодаря предыдущему анализу нам нужно только реализовать один из наших собственныхAssetResolverа такжеMapping, затем поставьтеAssetResolverнабитый до дрожиAssetManagerПросто в очереди.

  • Используйте встроенное решение поддержки, предоставляемое flutter

Этот метод был обнаружен только прошлой ночью, когда я писал эту статью, поэтому он еще не проверен, но теоретически выполним и должен быть самым простым и эффективным решением на данный момент.

существуетRunConfigurationВнутри мы можем найти следующий код (исходный путьflutter/shell/common/run_configuration.h)

RunConfiguration RunConfiguration::InferFromSettings(
    const Settings& settings,
    fml::RefPtr<fml::TaskRunner> io_worker) {
  // 下面无关重要的代码已经被我删除了。。。
  if (fml::UniqueFD::traits_type::IsValid(settings.assets_dir)) {
    asset_manager->PushBack(std::make_unique<DirectoryAssetBundle>(
        fml::Duplicate(settings.assets_dir)));
  }
  asset_manager->PushBack(
      std::make_unique<DirectoryAssetBundle>(fml::OpenDirectory(
          settings.assets_path.c_str(), false, fml::FilePermission::kRead)));
}

На самом деле InferFromSettings здесь дляfuchsiaБ/у (флаттер кроссплатформенный, сходство можно увидеть везде в проекте движкаfuchsia android ios windows linux darwinи т.д. структура каталогов), мы не можем вызвать эту функцию напрямую, ноDirectoryAssetBundleНо он может быть общедоступным (на самом деле, платформа ios не упаковывает его, как платформа Android.APKAssetProviderВыходите, ios тоже используется напрямуюDirectoryAssetBundleиз)DirectoryAssetBundleПо сути тожеAssetResolverРеализация , исходный путь находится вflutter/assets/directory_asset_bundle.hНиже я не буду его здесь разбирать, а если вам интересно, то можете сразу перейти к нему.

3.3.3 Процесс реализации

Общий процесс плана

Принцип, реализованный здесь, примерно аналогичен решениям для горячего обновления Android, таким как Tinker: путем сравнения различий файлов между старой и новой версиями создается пакет исправлений. Затем поместите пакет патчей на сервер и раздайте его пользователям старой версии APK. После загрузки разархивируйте его локально и объедините пакет исправлений, чтобы добиться полной замены.

Генерация и объединение дифференциальных пакетов

Я сделал несколько обходных путей в этой области.Когда я впервые искал в Интернете, я рекомендовал bsdiff и bspatch, но на официальном сайте есть только код C. В это время я был довольно сбит с толку, поэтому я напрямую пересадил код в NDK. на платформе Android при портировании и компиляции динамических библиотек я наступал на некоторые ямки, но в основном встречаются ямки новичков, вызванные незнанием CMake и C++.

Некоторые справочные ссылки о bsdiff:

bsdiff.pdf

алгоритм bsdiff

Дифференциальное обновление Google фактически использует bsdiff

Tinker основан на коде Java, упакованном bsdiff v4.2.

Что касается генерации и слияния дифференциальных пакетов, то используются готовые фреймворки, так что это не очень сложно.

Суммировать

В этой статье в основном исследуются и объясняются три текущие основные схемы динамической реализации во Flutter, схема динамического компонента и схема Js, такая как RN, которые по существу реализуются с помощью AST, анализирующего семантическое дерево. Что касается динамизации скомпилированного продукта, то при анализе исходного кода можно обнаружить, что его можно реализовать на платформе Android в настоящее время, но для платформы iOS хорошего решения нет. Динамическую схему компиляции продуктов Android в настоящее время относительно легко реализовать, и это не слишком сложно. В процессе исследования я более-менее наступил на некоторые ямы.Если в статье есть какие-то недочеты, надеюсь, вы меня поправите~

Спасибо за чтение~

автор


xiaosongzeem