2.23. Flutter: виды сборки

Теперь когда мы узнали всё необходимое про технологию, которую мы используем как инструмент для разработки и написали свое первое приложение, возникает вопрос: «А как это приложение доставить для наших пользователей»?

До этого времени мы с вами использовали Flutter в так называемом режиме разработки или debug-режиме, но на самом деле это не единственный режим для запуска нашего приложения.

В этом параграфе мы узнаем, как компилируется Dart-код, познакомимся с тремя режимами сборки Flutter-приложения и разберёмся в различиях между ними.

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

Компиляция Dart-кода

Прежде чем приступить к разбору различных типов запуска Flutter-приложения, давайте сперва поговорим про то, как может компилироваться написанный нами Dart-код

Dart может компилироваться в двух вариантах:

  • Ahead of time (AOT).
  • Just in time (JIT).

AOT-компиляция — это когда наш код собирается до запуска нашего приложения. При AOT-компиляции необходимо пройтись по каждой части программы и скомпилировать ее.

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

👉 Принципиальное отличие между JIT и AOT в том, когда и как исходный код компилируется в промежуточное представление, необходимое dartVM.

При разработке приложений мы, как правило, пользуемся JIT-компиляцией кода Dart. Как вы могли догадаться, именно она позволяет нам использовать функционал Hot restart & reload.

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

Но это не даётся нам бесплатно: за удобную и быструю разработку мы платим производительностью нашего приложения. Перекомпиляция в момент работы приложения занимает определённое время и напрямую влияет на производительность программы. При таком раскладе конечный пользователь будет получать просадки кадров и, скорее всего, ему это не очень понравится.

Если же мы компилируем наш Dart-код в AOT-режиме, мы теряем возможность использовать Hot restart & reload, но не тратим ресурсы устройства на компиляцию в процессе исполнения. Пользователи получают более плавную и ровную производительность приложения, быстрый запуск.

Режимы сборки Flutter-приложения

Flutter позволяет использовать компиляцию Dart-кода в JIT- и AOT-режиме для разных вариантов сборки и запуска наших приложений. Всего их три:

  • Debug mode.
  • Profile mode.
  • Release mode.

Расскажем о каждом их них подробнее.

Debug mode

Это стандартный режим запуска Flutter-приложения.

Именно в нём мы запускаем приложение командой flutter run, если явно не указываем другой тип сборки. Он спроектирован специально для разработки, и не предполагает быстрой производительности или минимально возможного веса приложения.

В нём есть возможность пользоваться инструментами разработчика для отладки работы вашего кода. Например, ставить точки остановки, смотреть стек вызовов в конкретном участке кода, содержимое переменных.

В этом режиме dart код собирается JIT-компилятором.

Profile mode

Это режим, похожий на Debug mode — за исключением того, что конечный код получается более оптимизированным, приближенным к тому, который получат наши пользователи. Здесь dart runtime содержит только ту дополнительную информацию, которая нужна для профилирования нашего приложения.

В этом режиме используется AOT-компиляция и не доступен Hot reload & restart. Чтобы собрать приложение в profile-режиме, добавьте флаг --profile:

1flutter run --profile

Обычно profile-сборки собирают, чтобы наблюдать за производительностью приложения. Вы можете воспользоваться инструментом performance view, чтобы посмотреть на стабильность работы вашего приложения.

Release mode

Используется для того, чтобы собрать финальное приложение, которое попадёт в руки конечных пользователей. В этом режиме проводятся всевозможные оптимизации нашего кода, убирается неиспользуемый код, нет проверок для отлова не-критичных ошибок, отключены assert. Здесь используется AOT-компиляция. Чтобы запустить приложение в release mode добавьте флаг --release:

1`flutter run --release`

Если вы хотите просто собрать конечный артефакт для конкретной платформы воспользуйтесь командой build. Вам необходимо указать имя интересующего вас артефакта и режим сборки — --release. Например, чтобы собрать Android Package Kit (.apk), выполните следующую команду:

1`flutter build apk --release.`

По окончанию сборки, директория, куда попал артефакт будет выведена в консоль. Обычно этоbuild/app/outputs/flutter-apk/app-release.apk

👉 На самом деле когда мы вызываем команду flutter run, внутри она использует эту же команду flutter build.

Вы можете очистить директорию build/app/outputs/flutter-apk (для android), и затем запустить приложение из IDE или командной строки — собранный артефакт вновь окажется в очищенной директории, даже если вы не вызвали напрямую flutter build.

Учтите, что изменения, которые вы делаете, используя Hot restart & reload не попадают в него.

Если у вас есть MacBook, вы можете попробовать запустить команду flutter build ipa, чтобы собрать iOS-артефакт.

Подписи

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

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

Представьте, что вы решили установить/обновить приложение Яндекс.Музыки на системе Android из неизвестного источника — а им владеет злоумышленник, не обладающий ключом подписи Яндекса. В этом случае система сообщит вам, что приложение не было создано Яндексом и устанавливать его не рекомендуется

Подпись для Android

Чтобы создать собственный ключ для Android воспользуйтесь следующей командой

Для Mac:

1`keytool -genkey -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload`

Для Windows:

1`keytool -genkey -v -keystore %userprofile%\upload-keystore.jks -storetype JKS -keyalg RSA -keysize 2048 -validity 10000 -alias upload`

Важно: У вас должна быть установлена Java и библиотека keytool (идёт в комплекте с Java).

Придумайте сложный пароль и не потеряйте сгенерированный файл

👉 Если вы ещё не публиковали своё приложение в сторы то потеря ключа не так страшна — можно сгенерировать новый и использовать его. Но стоит иметь в виду, что после публикации поменять ключ, который используется для подписи, достаточно затруднительно.

Далее, чтобы собрать Android-приложение, подписанное вашим ключом, вам необходимо в директории Android-проекта создать файл с названием key.properties. Проверьте, что файл добавлен в .gitignore: в нём будут лежать пароли от сгенерированного ключа.

Добавьте в key.properties  следующее содержимое:

1storePassword=<Ваш пароль store password>
2keyPassword=<Ваш пароль от ключа>
3keyAlias=upload
4storeFile=<Полный путь до директории где лежит сгенерированный ключ>
Пример
1storePassword=my-store-password
2keyPassword=my-key-password
3keyAlias=my-custom-alias
4storeFile=C:/keys/my-first-app-key.jks

Далее откройте файл build.gradle расположенный в [корневая папка проекта]/android/app/build.gradle  и добавьте в него перед началом блока android следующие строчки:

1   def keystoreProperties = new Properties()
2   def keystorePropertiesFile = rootProject.file('key.properties')
3   if (keystorePropertiesFile.exists()) {
4       keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
5   }
6
7   android {
8         ...
9   }

Затем найдите блок buildTypes и замените его на код ниже.

Код ниже
1   signingConfigs {
2       release {
3           keyAlias keystoreProperties['keyAlias']
4           keyPassword keystoreProperties['keyPassword']
5           storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
6           storePassword keystoreProperties['storePassword']
7       }
8   }
9   buildTypes {
10       release {
11           signingConfig signingConfigs.release
12       }
13   }

Теперь при сборки вашего приложение в release-режиме оно будет подписано сгенерированным вами ключом.

Вы можете сделать отдельные ключи для debug- и releasе-режимов, или разных окружений приложения, например тестовые сборки вы можете подписывать отдельным ключом.

Подпись для iOS

Если вы хотите узнать как подписать ваше iOS приложение, ознакомьтесь с официальной документацией Apple

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

Сертификаты используются для подписи iOS-артефактов, всего их существует 4 вида:

  • developer certificate
  • distribution certificate
  • provision certificate
  • ad-hoc certificate

Если вы не публикуете свое приложение, то для разработки вам будет достаточно первого (developer). Для него не нужен Apple Developer Account и можно создать его автоматически в Xcode, когда вы попытаетесь собрать из него iOS-версию вашего приложения.

Если же вы решите опубликовать приложение, то для получения остальных сертификатов вам потребуется приобрести Apple Developer Account.

Подпись для Windows, linux, macOS

Если у вас возникнет необходимость подписать десктоп-приложение (например перед публикацией в одном из магазинов приложений), то вы можете ознакомиться со следующими материалами:

Web приложения, как правило, не нуждаются в подписи.


Теперь вы знаете про компиляцию, разные сборки Flutter-приложений и механизм подписей.

Если вы читали параграфы этой главы последовательно, то к этому моменту уже имеете твёрдое представление об основах работы с этим фреймворком (и с Dart) и можете написать собственное приложение.

В следующей главе мы углубимся во Flutter: что такое элементы (Elements), как создавать анимации (и как они устроены), что из себя предоставляют CustomPainter и RenderObject.

Будет сложно, но вы справитесь!

Чтобы добавить в заметки выделенный текст, нажмите Ctrl + E

Отмечайте параграфы как прочитанные, чтобы видеть свой прогресс обучения

Вступайте в сообщество хендбука

Здесь можно найти единомышленников, экспертов и просто интересных собеседников. А ещё — получить помощь или поделиться знаниями.
Вступить
Сообщить об ошибке
Предыдущий параграф2.22. Flutter: архитектура фреймворка
Следующий параграф3.1. Элементы: первое погружение