Введение: ссылка, которая открывает приложение

«Чем deep link отличается от app link?» — вопрос, который проверяет, понимаете ли вы, как устроена intent-система и как приложение «договаривается» со своим сайтом.

Deep links — то, на чём стоит добрая половина продуктовых механик: ссылка из рекламной кампании открывает карточку товара, push-уведомление ведёт на нужный экран, кнопка «поделиться» передаёт ссылку на контент. На собеседовании тему спрашивают реже, чем lifecycle, но именно она отделяет кандидата, который скопировал intent filter из интернета, от того, кто понимает, что происходит под капотом.

Разберу по порядку: что такое deep link, как приложение принимает ссылки через intent filter и как app link снимает главную боль deep link — диалог выбора приложения.

Deep link — это любая ссылка (URI), которая открывает содержимое приложения вместо браузера. В документации сказано прямо: deep links — стандартная возможность платформы, построенная на системе intents.

Работает это так: у URI есть схема — http, https или своя, вроде myapp://. Когда пользователь кликает по ссылке, система формирует implicit intent с действием ACTION_VIEW и данными URI, а дальше смотрит, какие приложения зарегистрировались на такой формат ссылки.

Есть два вида deep links:

  • Своя схема — myapp://product/42. Работает на всех версиях Android, но такая ссылка не является веб-ссылкой: браузер её не откроет, и другие приложения не поймут, что с ней делать.
  • Веб-схема — https://example.com/product/42. Привычная ссылка, но без верификации система всё равно не знает, что именно ваше приложение должно её обрабатывать.

Главный минус обычного deep link — диалог выбора. Если на ссылку зарегистрировались несколько приложений (или браузер), система спросит пользователя, чем открыть. Один лишний шаг — и конверсия из рекламной кампании заметно падает.

Intent filter: как приложение «принимает» ссылки

Чтобы приложение обрабатывало ссылки, в манифесте нужен intent filter, описывающий, какие URI оно готово принимать. Минимальный набор из гайда по deep links:

  • android.intent.action.VIEW — действие просмотра ссылки;
  • android.intent.category.BROWSABLE — обязательная категория: без неё браузер не сможет открыть ваше приложение по ссылке;
  • android.intent.category.DEFAULT — чтобы приложение отвечало на implicit intents;
  • <data> — схема и адрес: android:scheme, android:host, а при необходимости android:pathPrefix или android:path.
AndroidManifest.xml
<activity android:name=".ProductActivity" android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:scheme="https"
            android:host="example.com"
            android:pathPrefix="/product" />
    </intent-filter>
</activity>

Обратите внимание: в android:pathPrefix ведущий слеш обязателен — "/product", а не "product". Мелочь, из-за которой фильтр молча не срабатывает.

Когда пользователь кликает по https://example.com/product/42, система находит вашу Activity и передаёт ей intent. Дальше дело за кодом:

ProductActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    handleIntent(intent)
}

override fun onNewIntent(intent: Intent) {
    super.onNewIntent(intent)
    handleIntent(intent)
}

private fun handleIntent(intent: Intent?) {
    val uri = intent?.data ?: return
    val productId = uri.lastPathSegment?.toLongOrNull() ?: return
    showProduct(productId)
}

onNewIntent нужен для singleTop / singleTask: если Activity уже существует, система не создаёт её заново, а вызывает onNewIntent.

Кстати, о handleIntent: в реальном проекте важно не забывать про intent?.data, потому что Activity может запускаться не только по deep link, а с null-intent'ом или обычным ACTION_MAIN. Кандидаты, которые не проверяют data, получают краш при первом же запуске приложения с иконки после перехода по ссылке — это классика, которую я вижу на разборах кода.

App Link — это deep link, который прошёл верификацию: приложение доказало системе, что действительно связано с сайтом. После этого ссылка на ваш домен открывается сразу, без диалога выбора. Работает на Android 6+ (API 23) на устройствах с сервисами Google.

Как устроена верификация по гайду о верификации app links:

  1. В intent filter ставится флаг android:autoVerify="true".
  2. При установке приложения система запрашивает у каждого домена из фильтров файл Digital Asset Links: https://<домен>/.well-known/assetlinks.json.
  3. В файле перечислены пакеты и SHA-256 отпечатки сертификатов, которым разрешено открывать ссылки этого домена.
  4. Если данные совпали — верификация пройдена, и ссылки открывают приложение без вопросов.
AndroidManifest.xml
<activity android:name=".ProductActivity" android:exported="true">
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data android:scheme="https" android:host="example.com" />
    </intent-filter>
</activity>

Сам файл digital asset links выглядит так:

.well-known/assetlinks.json
[
  {
    "relation": ["delegate_permission/common.handle_all_urls"],
    "target": {
      "namespace": "android_app",
      "package_name": "com.example.app",
      "sha256_cert_fingerprints": [
        "14:6D:E9:83:C5:73:06:50:D8:EE:B9:95:2F:34:FC:64:AD:A3:5C:62"
      ]
    }
  }
]

Суть app link в одном предложении

App link — это deep link, который доказывает системе: «этот домен — наш». Диалог выбора не нужен, потому что система уверена, куда вести пользователя.

Нюанс из документации: на Android 11 (API 30) и ниже приложение становится обработчиком ссылок по умолчанию, только если файл Digital Asset Links найден для всех хостов в манифесте. Один неподтверждённый домен — и преимущество теряется целиком. На Android 12+ правила мягче: система обрабатывает каждый хост отдельно.

Важно понимать и что будет, если верификация не прошла. Приложение не «сломается»: ссылка просто продолжит работать как обычный deep link — с диалогом выбора приложения. То есть app link — это не отдельный механизм, а надстройка над deep link: механизм один и тот же, меняется только доверие системы. Эту мысль полезно озвучить на собеседовании, она показывает понимание.

Отдельно про диагностику: статус доменов проверяется командой adb shell pm get-app-links --package <пакет> — если верификация не прошла, вы увидите, какой именно хост «красный». На проектах, где app links переставали работать после смены сертификата подписи, такой вывод спасает часы отладки.

Deep link vs App Link: разница для собеседования

На собеседовании тему удобно разложить на две части: механика (intent filter, scheme) и верификация (digital asset links). Вот опорный чек-лист ответа:

  • Deep link — любой URI в ваше приложение; работает на всех версиях Android; может показать диалог выбора приложения.
  • App link — верифицированный deep link с autoVerify="true" и файлом assetlinks.json на сайте; открывается без диалога; требует Android 6+.
  • Intent filter — ACTION_VIEW + BROWSABLE + DEFAULT + <data> со схемой и хостом; pathPrefix — только с ведущим слешем.
  • Своя схема (myapp://) работает везде, но браузер её не понимает; для app link нужна именно веб-схема https.
  • Проверка верификации — сброс состояния через adb shell pm set-app-links --package <пакет> 0 all, затем открыть тестовую ссылку.

Ошибка кандидата: «в assetlinks.json кладут пару имён — и всё заработает». В реальности файл лежит на сайте, а не в проекте, и он привязан к конкретному сертификату подписи: сменили ключ подписи — обновили отпечатки в файле, иначе верификация сломается и вернётся диалог выбора.

Для глубины ответа полезно упомянуть свежий тренд: с Android 15 появились Dynamic App Links — правила обработки ссылок можно менять без выпуска новой версии приложения (обзор deep links). Достаточно сказать, что вы в курсе.

Готовитесь к собеседованию по Android?

Темы вроде deep links проверяют не память, а понимание intent-системы — и именно тут кандидаты с опытом 1–2 года чаще всего «плывут» на уточняющих вопросах. Пришлю бесплатный чек-лист подготовки к собеседованию — конкретные пункты, что закрыть и в каком порядке.

Итоги

  • Deep link — механизм платформы: URI в приложение через intent-систему, с возможным диалогом выбора.
  • App link — deep link с верификацией через assetlinks.json, открывается без диалога, требует Android 6+.
  • На практике — правильный intent filter, autoVerify="true" и актуальный DAL-файл на сайте.

Ответ, который закрывает тему

«Deep link — это любой URI, который ведёт в приложение через intent filter. App link — deep link, подтверждённый через Digital Asset Links: система верит, что домен принадлежит нам, и открывает приложение без диалога выбора».

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