Перейти к содержимому
8 августа 2026 г. · Публикация

Нативные приложения без нативной боли

Эта статья описывает продукт на момент публикации. Актуальные возможности см. в разделах AI Builder и Agent Teams.

Нативные приложения без нативной боли

Допустим, вы пару недель собирали в чате приложение-список задач. Сейчас это сайт — React, база данных, ничего особенного. Вы пишете «сделай мне версию для Android» и нажимаете Enter. Вот что на самом деле происходит между этим нажатием клавиши и появлением файла .aab в папке загрузок, потому что большинство платформ не показывают эту часть, а именно в скрытой части раньше и была вся боль.

Android .aab

Первым берёт управление на себя Gradle. Нативные модули вашего приложения — доступ к камере, локальное хранилище, любые плагины, подключённые сборкой, — каждый указывает, с какой версией NDK он был скомпилирован, и эти указания не всегда совпадают. Я видел, как модуль, собранный под NDK r25, отказывался линковаться с модулем, рассчитанным на r26, и ошибка при этом говорит не «несовпадение версий», а что-то про отсутствующий символ на три уровня вглубь в файле .so. Версии Kotlin ведут себя ещё коварнее: версия, закреплённая внутри одного модуля Gradle, может незаметно перекрыть версию, указанную в начале вашего build-скрипта, и сборка проходит успешно — просто получившийся бинарник падает на конкретных версиях Android уже у пользователей. Ничего экзотического в этом нет. Это стандартная плата за выпуск нативного Android-приложения, и именно поэтому команды нанимают человека, единственная работа которого — знать, какой флаг уберёт ошибку этой недели.

Сборка здесь использует настоящий инструментарий и берёт всю эту работу по разрешению конфликтов на себя:

15–25 минэтап нативной компиляции, настоящий инструментарий Gradle
  • Конфликты зависимостей выявляются до того, как превратятся в сбой во время выполнения
  • Конфигурация инструментария, которая улучшается с каждым новым сбоем, из которого извлекается урок

Более быстрая имитация этого процесса — что-то, что формирует .aab без запуска реальных задач Gradle, — собиралась бы меньше чем за минуту. Она также сломалась бы в тот момент, когда вашему приложению понадобится фоновая служба или нативная криптобиблиотека, а проверка Play Store выявила бы это уже на следующий день. Мы предпочитаем потратить эти двадцать минут.

Файл macOS .dmg

В этот единственный файл входят две сборки. Инструменты командной строки Xcode отдельно компилируют бинарник для Apple Silicon и бинарник для Intel, а затем lipo склеивают их в единый универсальный исполняемый файл.

90%+новых Mac продаются с Apple Silicon

Заманчиво выпустить один бинарник и на этом закончить — пока не вспомнишь, что многие пользователи работают на ноутбуке, выданном работодателем, а не на выбранном ими самими оборудовании, и этому ноутбуку может быть три года, и он может быть на Intel. Вместо того чтобы заставлять пользователя выяснять, какой у него чип (большинство не смогут ответить), мы выпускаем оба варианта и позволяем ОС выбрать нужный незаметно. Альтернатива, которую мы пробовали на раннем этапе, — кросс-компиляция всего с Linux-машины с использованием эмулированных инструментариев. Это быстрее. Но так же легко получить крайний случай с подписью кода, который проявляется только на реальном железе macOS 12, спустя шесть недель после выпуска, о котором сообщает растерянный пользователь, не понимающий, почему его приложение не открывается.

Установщик для Windows

Именно здесь первый запуск решает, доверяет ли пользователь приложению вообще. Windows SmartScreen ещё не знает ваш установщик — у него ещё не накопилась репутация на серверах Microsoft, — поэтому показывается синий экран «Windows защитила ваш компьютер» с жирной кнопкой «Не запускать» и едва заметной ссылкой «Подробнее», после нажатия которой появляется вариант «Всё равно запустить». macOS проделывает похожий танец: правый клик, «Открыть», подтверждение — потому что приложения вне App Store тоже не доверенные по умолчанию. Поначалу мы ссылались на обе эти ситуации в общей странице FAQ. Обращения в поддержку показали, что это не работает — человек, смотрящий на экран, предупреждающий, что его загрузка может быть вредоносной, не идёт читать документацию, он делает скриншот и спрашивает, не взломали ли его. Поэтому процесс установки определяет ОС и показывает ровно те три клика, которые нужны, без всякого FAQ. Мелкая деталь, но важен и сам файл: загрузка называется именем вашего продукта, а не названием артефакта сборки. Никто не должен объяснять в чате поддержки, что скачал файл «app-release-signed-v2-final.exe» и не может понять, тот ли это файл.

Манифест расширения браузера

Это единственный нетипичный этап во всём процессе — ни Gradle, ни NDK, ни компиляции в обычном смысле. Вместо этого есть манифест, и манифест — это переговоры с рецензентом Chrome Web Store, с которым вы никогда напрямую не пообщаетесь.

Запрошенное разрешениеРезультат проверки
<all_urls> (шире, чем требует функциональность)Двухнедельная переписка с человеком, который не скажет прямо, что именно его не устроило
activeTab (ограничено реальной потребностью)Проходит проверку в тот же день

MV3 также усложняет то, что в MV2 было простым: фоновые service worker выгружаются посреди задачи по замыслу — это решение Google, направленное на экономию заряда батареи, — и функции, которым нужно это пережить, должны быть построены с учётом этого, а не вопреки этому. По умолчанию мы задаём каждому расширению минимально необходимый набор разрешений и расширяем его только тогда, когда этого требует конкретная функция.

Ключ подписи (keystore)

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

Чат-переписка, лежащая в основе всего этого

Ничего из перечисленного не существует в отдельном «мобильном проекте». Это тот же самый разговор, в котором создавалось веб-приложение. Попросите изменить интерфейс — обновится веб-сборка; попросите следом Android-сборку — она скомпилируется из того же актуального состояния, а не из ветки, разошедшейся три недели назад. Большинство команд, которые я видел, пытаясь добавить нативную поддержку позже, в итоге поддерживают две кодовые базы, которые расходятся всё дальше — веб-приложение, выпускаемое каждый день, и нативную обёртку, которую кто-то должен не забыть подтянуть перед каждым релизом. Именно в этом разрыве и живёт устаревание, и именно его устраняет единая история сборок. Правда, это работает в обе стороны: если в чате в последнее время работали быстро и небрежно, Android-сборка унаследует и это. Это не отдельный этап полировки, а прямая компиляция того, что реально есть — что на практике заставляет держать всё в порядке, потому что нет побочной задачи «приберёмся перед публикацией», которую можно пропустить.

Когда всё готово к публикации в магазине, путь публикации передаёт эстафету вашему собственному листингу в Play Store и вашему собственному аккаунту Apple Developer. Не нашему. Мы не хотели вставать между вами и вашим собственным распространением.

Дальше в планах: сборки для App Store на iOS и macOS. Механика упаковки в целом того же вида, что описана выше, — конвейер подписи и проверки Apple — это отдельный проект, и мы лучше выпустим его работающим, чем выпустим скорее.
Публикация
ПоделитьсяXLinkedInFacebookRedditQuoraWhatsAppTelegramEmail
← Все статьи