Как мы интегрировали Huawei Mobile Services в два этапа

Всем привет, меня зовут Ленар Садыков, и я вместе с командой развиваю и поддерживаю приложение для клиентов Lamoda на базе Android. Сегодня расскажу, как мы добавили поддержку Huawei Mobile Services и Huawei App Gallery.

В мае 2019 года Департамент торговли США внес Huawei в черный список. Вследствие Google отказался от сотрудничества с Huawei, а Huawei, в свою очередь, перестал распространять устройства с сервисами Google. В ответ на это китайский гигант представил миру Huawei Mobile Services, в том числе — магазин приложений Huawei App Gallery.

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

image


Причины

Причина проста — это наши пользователи. На диаграмме представлена статистика за август–сентябрь 2020 года: 27,8% пользователей используют устройства Huawei.

f-on1lx4wrpoxzq-vcn4parhgjy.jpeg

Кроме этого, нас подкупили бонусы от компании в виде полной оплаты разработки, бесплатного доступа к сервисам и продвижения в своем сторе. А еще они предоставили собственные устройства для разработки и тестирования.


План

На сегодняшний день в мире существуют два типа устройств от Huawei. Первый тип — устройства, которые были выпущены до конфликта с набором сервисов Google и Huawei. Второй тип — устройства только с сервисами Huawei, которые были выпущены после конфликта.

Так как App Gallery позволяет публиковать сборку для каждого из типов, мы решили интегрировать сервисы в два этапа:


  1. Внести минимальные изменения для возможности опубликоваться в App Gallery.
  2. Поддержать все необходимые нам сервисы от Huawei.

На первом этапе мы планировали поддержать только те устройства, которые были выпущены до конфликта и имеют на борту сервисы Google, на втором этапе — все существующие устройства Huawei на базе Android.


Этап I: Минимальные изменения

Наше приложение имеет прямую связь с Google Play Store из-за двух сервисов: Dynamic Delivery и In-App Updates. Dynamic Delivery помогает докачать часть приложения, в нашем случае это виртуальная примерочная для кроссовок. In-App Updates позволяет приложению обновляться в фоновом режиме.

Следовательно, приложение, установленное из App Gallery, должно использовать аналогичные сервисы из Huawei Mobile Services. Аналог Dynamic Delivery для Huawei — Dynamic Ability. Но у Huawei нет конкретного сервиса, который мог бы заменить In-App Updates, однако есть похожая функциональность.

Так как пользователь может установить наше приложение на одно устройство с разных сторов, нужно было научиться определять, откуда же оно было скачано. На первом этапе мы решили использовать package name стора. В Android его можно вытащить через метод getInstallerPackageName у PackageManager. Для Google Play это будет значение com.android.vending, для App Gallery — com.huawei.appmarket.


Dynamic Ability

Мы добавили сервис Dynamic Ability в приложение в соответствии с документацией (также есть кодлаба). Реализация очень похожа на гугловский Dynamic Delivery. Процесс скачивания динамического модуля построен на основе статусов — в данном случае отображается диалоговое окно прогресса скачивания и сообщение о том, что оно завершено.

Но отладка обернулась настоящей болью: аналога internal testing нет, поэтому пришлось тестировать через open testing, у которого есть свое ревью, хоть и ускоренное. Из-за этого сборка может появиться в сторе через час или через сутки.

Из сложностей реализации можно выделить тот момент, что динамический модуль никак не хотел скачиваться. Проблема была в его манифесте. Для Google Dynamic Delivery у нас было прописано так:



   
       
   

Huawei Dynamic Ability заработал после того, как написали такой код:



После того как мы починили скачивание, у нас нашлась новая поломка: модуль скачивался, но прогресс скачивания не отображался. Оказалось, проблема была в статусах. В нашем случае порядок статусов Google Dynamic Delivery такой:


  • PENDING,
  • DOWNLOADING,
  • INSTALLED.

Статусы для Huawei Dynamic Ability:


  • DOWNLOADING,
  • DOWNLOADED,
  • INSTALLED.

Альтернатива Google In-App Updates

Реализация In-App Updates от Huawei выглядит очень просто: есть возможность открыть только один вид диалогового окна. Процесс скачивания не реализован — происходит обычный редирект на страницу приложения в App Gallery. Так как диалоговое окно можно всегда закрыть, данную функциональность без доработок можно использовать только для soft update. Для force update мы добавили дополнительный блокирующий экран.

i_ycs5ayt6i9qxrp3gr57ksvyuq.gif
Soft update от Google и Альтернатива от Huawei


Adjust

Также на первый этап мы вынесли обновление Adjust — это система аналитики, которая отслеживает действия пользователя перед установкой приложения. Например, вы видите рекламу нашего приложения в Facebook. Кликаете на нее, переходите в Google Play, скачиваете приложение. В этом случае с помощью Adjust мы поймем, что пользователь установил приложение из Google Play, перейдя из рекламы в Facebook.

Хоть Adjust и отчитался о поддержке App Gallery, этого оказалось недостаточно: мы всегда получали данные, что приложение установлено из Google Play. Чтобы разделять данные от Adjust по сторам, мы добавили в versionName постфикс -huawei. Например, если пользователь скачал наше приложение с Google Play, он видит версию 3.77.0, а если из App Gallery, то 3.77.0-huawei. Благодаря разным versionName, наши аналитики могут разделять установки по сторам. При этом versionCode остался одинаковым, что позволяет корректно обновиться из любого стора.


Этап II: Полная интеграция

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


  1. Ветвление в коде.
  2. Зависимости, которые никогда не будут использоваться. Для Google Play — это зависимости от Huawei-сервисов, а для App Gallery — зависимости от Google-сервисов.

Решение — productFlavor. Первая проблема решается определением флейворов: Android Studio сам производит ветвление. А вторая — с помощью метода подключения зависимостей. Например, Google-сервисы подключаем с помощью googleImplementation, а Huawei-сервисы — с помощью huaweiImplementation.

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

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

bqrqdtfgf4emcxpohzq_2h8ghyi.png

Например, есть основной модуль приложения app и есть модуль с мобильными сервисами mobile-services. В нашем случае, в модуле app мог бы быть такой код:

class MapFragment : Fragment() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       //...
       val map = Map()
       map.draw()
       //..
   }
}

Абстракция над картами Map находилась бы в модуле mobile-services без привязки к флейворам:

class Map() {
   fun draw() {
       drawFlavorMap()
   }
}

А вот сама реализация отрисовки карт находилась бы в модуле mobile-services под соответствующим флейвором.

Для флейвора google:

fun drawFlavorMap() {
   val map = GoogleMap()
   map.drawMap()
}

Для флейвора huawei:

fun drawFlavorMap() {
   val map = HuaweiMap()
   map.drawMap()
}

Интеграция оставшихся сервисов

После внедрения флейворов и отдельного модуля для сервисов мы интегрировали остальные необходимые сервисы Huawei: Map Kit, Analytics Kit, Push Kit, Location Kit, Crash Service. В основном, все предсказуемо и делается по документации. Выделю пару важных моментов.


Карты от Huawei

Карты от Huawei вполне подходят в качестве замены карт от Google. Однако у них есть два недостатка:


  • Нет возможности указать цвет кластера (точки, которые объединяют в себе несколько других точек). Поэтому на карте отображаются кластеры разных цветов.
  • Иногда неправильно указывается количество точек в кластере.

Мы спрашивали, как можно решить эти проблемы у разработчиков Huawei, но не добились какой-то конкретики. В целом, карты справляются со своей задачей, пусть и не настолько идеально, как привычные Google-карты.

xaooqoxkw8vir0ugrb4rfey9vw0.gif
Работа Google-карт и Huawei-карт


Пуши от Huawei

На дебажной сборке у нас не заработали пуши, потому что в файле agconnect-services.json был указан продовый package name нашего приложения. Проблема решилась после того, как мы создали в App Gallery Connect новое приложение с нужным package name.

После этого agconnect-services.json стал содержать такие строки:

"appInfos":[
        {
            "package_name":"com.lamoda.lite.test",
            "app_id":"123456789"
        },
        {
            "package_name":"com.lamoda.lite",
            "app_id":"9876"
        }
    ]

Как мы живем с двумя сторами


CI/CD

Мы используем Bamboo и gradle-таски. Небольшая сложность возникла только в тот момент, когда мы переходили на флейворы — тогда из-за изменения названий gradle-тасок сломался наш CI. Например, bundleRelease разделился на bundleHuaweiRelease и bundleGoogleRelease.
В остальном все хорошо: некоторые Bamboo-планы продублировались из-за флейворов, некоторые так же, как и раньше, работают только с Google-сборкой. Кстати, для публикации в App Gallery мы используем вот этот плагин.


Разработка

Мы редко переключаемся на Huawei-флейвор, благодаря нашему модулю mobileservices и тонким абстракциям внутри него. Crash rate сборки из App Gallery также стабилен — за полгода мы еще не сталкивались с крашем, который был бы связан только с Huawei Mobile Services.


QA

У тестировщиков прибавилось работы: теперь на релизе они тестируют дополнительную сборку, предназначенную для App Gallery.


Итог

Все работает так, как и предполагалось: за полгода зарегистрировано больше 2 миллионов установок из App Gallery. Также мы видим, что пользователи Huawei из Google Play Store «перетекают» в Huawei App Gallery, а это значит, что интеграция прошла успешно, и мы смогли подстроиться под наших пользователей.

© Habrahabr.ru