Когда PWA уже работает в браузере, под Android его можно отдать в Play Store без переписывания на нативный код. У продукт-инженера здесь развилка: либо завернуть веб в собственный WebView-контейнер (Capacitor), либо отдать пользователю его же Chrome через Trusted Web Activity. Выбор влияет на доступ к native API, на размер пакета и на то, чей браузерный движок рисует ваш интерфейс. Разберём оба пути и механику публикации.
Варианты на Android: Capacitor, TWA, Custom Tabs
Три способа показать веб в Android-приложении различаются тем, чей это WebView и сколько вокруг него native-кода.
- Capacitor — свой контейнер на системном
WebView. Веб грузится из ассетов пакета, а JS-bridge даёт доступ к камере, файлам, push. Максимум native-контроля, но движок — системный WebView устройства, и его обновление зависит от производителя. - TWA (Trusted Web Activity) — полноэкранный Custom Tab без адресной строки. Показывает ваш боевой PWA по URL, движок — Chrome пользователя. Минимум кода, всегда свежий движок, но почти нет native-расширений сверх того, что умеет веб-платформа.
- Custom Tabs — низкоуровневый примитив, поверх которого построен TWA. В чистом виде используют для встраивания внешних страниц (вход через OAuth), а не для упаковки всего приложения.
Для упаковки выбор практически всегда между первыми двумя.
Что такое TWA
Trusted Web Activity — это Android-активность, которая запускает Chrome в полноэкранном режиме Custom Tab: без адресной строки, без браузерного UI, на весь экран. Внешне неотличимо от native, но внутри это ваш PWA, отрисованный движком Chrome, уже установленным на устройстве. Отсюда два следствия. Первое: вы не везёте свой движок — пакет весит десятки килобайт, а рендеринг всегда соответствует актуальной версии Chrome. Второе: «trusted» означает, что активность обязана доказать своё право показывать домен без адресной строки — иначе любое приложение выдавало бы чужой сайт за свой. Доказательство — двусторонняя привязка через Digital Asset Links.
Bubblewrap и сборка
TWA-проект не пишут руками — его генерирует Bubblewrap, CLI от Google Chrome Labs (@bubblewrap/cli). Он читает ваш web manifest и собирает Android-проект, подставляя значения из манифеста.
npm install -g @bubblewrap/cli
bubblewrap init --manifest https://example.com/manifest.webmanifest
bubblewrap build
init парсит manifest (имя, иконки, тема, стартовый URL) и спрашивает то, что не смог вывести; build запрашивает пароли keystore и подписывает пакет. На выходе — подписанный app-release-signed.apk для теста на устройстве и app-release-bundle.aab для загрузки в Play Store. Альтернатива без локального CLI — PWABuilder: указываете URL PWA в веб-интерфейсе, он отдаёт готовый Android-пакет на том же TWA под капотом.
Capacitor собирается иначе — через свой инструментарий и Android Studio:
npm install @capacitor/android
npx cap add android
npx cap sync
npx cap open android
cap add создаёт каталог android/, sync копирует собранный веб в native-проект, open открывает его в Android Studio, откуда и собирается .aab. Это и есть разница в трудозатратах: TWA — две команды над живым URL, Capacitor — полноценный native-проект под контролем.
Digital Asset Links
Чтобы TWA имел право показывать домен без адресной строки, нужно подтвердить владение им. Механизм — Digital Asset Links: файл на сайте указывает на приложение, а приложение — на сайт; Chrome сверяет обе стороны при запуске и, если они совпадают, убирает браузерный UI. Файл публикуется по фиксированному пути /.well-known/assetlinks.json:
[
{
"relation": ["delegate_permission/common.handle_all_urls"],
"target": {
"namespace": "android_app",
"package_name": "ru.example.app",
"sha256_cert_fingerprints": [
"AB:CD:EF:12:34:56:78:90:..."
]
}
}
]
package_name — идентификатор пакета Android, sha256_cert_fingerprints — отпечаток ключа подписи (при Play App Signing берётся из консоли Play). Если файл недоступен или отпечаток не совпадает, проверка не пройдёт и TWA откроется с адресной строкой — верный признак, что Asset Links настроены неправильно.
Публикация в Play Store
С августа 2021 все новые приложения принимаются только в формате Android App Bundle — пакетом с расширением .aab. Это формат публикации: вы загружаете один бандл, а Google Play генерирует из него оптимизированные APK под каждую конфигурацию устройства, так что пользователь скачивает меньше. Бандл подписывается ключом загрузки, а финальную подпись для распространения берёт на себя Play App Signing — отпечаток именно этого ключа и должен попасть в assetlinks.json. Дальше путь стандартный для обоих способов: создать приложение в Play Console, загрузить .aab, заполнить карточку и пройти проверку. Разница лишь в источнике бандла: у TWA это app-release-bundle.aab от Bubblewrap, у Capacitor — сборка из Android Studio.
Где это в UCP
Выбор упаковки под Android — это инженерное решение о границе между вебом и платформой, ровно как продукт-инженер проводит границы между слоями в backend. TWA отдаёт пользователю реальный PWA в его Chrome ценой минимума native-кода; Capacitor даёт больше нативного контроля ценой собственного контейнера. Решение симметрично тому, что разбирается для другой платформы в статье про iOS и WKWebView: один и тот же веб, разные обёртки и разные компромиссы по контролю и поддержке.