Как мы в андроид приложение inDriver добавили поддержку Harmony OS

    Все началось с того, что министерство торговли США включило Huawei в список компаний, с которыми запрещено вести бизнес американским компаниям. Ответом Huawei стала операционная система Harmony OS для своих смартфонов, и отказ использовать в ней сервисы Google. И чтобы не потерять часть рынка, мы интегрировали в inDriver ее поддержку. Хотя «интеграция OS» звучит громко – наше приложение, как и любое другое, написанное под Android, запустится на Harmony, но для полноценной работы необходимо заменить Google-сервисы на аналогичные Huawei.



    В inDriver мы используем сервисы Google для 3 целей:

    • Отправление пуш уведомлений;
    • Определение локации;
    • Google карты.

    Push Kit – отправление push-уведомлений


    Для начала, у Huawei есть неплохая документация. Но для быстрого старта достаточно использовать step-by-step туториал.

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

    Enabling the Service

    Вас попросят установить Data Storage Location. Учтите, что его потом нельзя изменить. Локация не влияет на непосредственную работу пушей, а является лишь местом хранения и обработки данных о них.

    Integrating the HMS SDK

    После того как вы закончите интеграцию, у вас вероятно будут проблемы со сборкой проекта. Во всяком случае, так было у нас на билд сервере Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-174-generic x86_64), при этом локально на рабочем Mac mini (late 2018, Mojave) все работало нормально.

    Execution failed for task ':app:processIndriverReleaseManifest'. org.gradle.api.GradleException: ERROR: no manifest file found

    Исправить это можно, если добавить

    agcp {
       manifest false
    }

    в конец файла build.gradle на уровне приложения, чтобы отключить манифест для agcp. Тут скорее проблема в том, что agcp ищет файл-манифест, но не может его найти и загрузить, что вероятнее всего является проблемой agconnect. По крайней мере, именно так нам ответила официальная техническая поддержка Huawei.

    Configuring AndroidManifest.xml

    Не знаю почему, но тут не сказано про автоматическую инициализацию, про которую написано в пункте 2.6 Automatic Initialization полной документации. Это необходимо, если вы отправляете токен на сервер в методе

    public void onNewToken(String token) {}

    Sending Messages: HUAWEI Push Kit Console.

    С его помощью вы можете проверить работу пушей. Интересный факт: если вы в консоли выберете Type: Notification Message, то они в обход обработчика onMessageReceived(RemoteMessage message) сразу будут отображены на телефоне.

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

    Теперь у нас на телефоне есть два пуш-сервиса, но как их организовать их совместную работу? Для начала надо определить, какие сервисы доступны на телефоне. Мы у себя решили, что Google-сервисы будут для нас в приоритете. Допустим, если на телефоне доступны и Google-, и Huawei-сервисы, мы работаем с Google.

    На сервер мы отправляем флаг состояния сервисов с возможными значениями:

    - UNKNOWN
    - GOOGLE
    - HUAWEI

    fun checkServices(context: Context): PlayServicesState {
       val googleAPI = GoogleApiAvailability.getInstance()
       when (googleAPI.isGooglePlayServicesAvailable(context)) {
           ConnectionResult.SUCCESS,
           ConnectionResult.SERVICE_MISSING,
           ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED,
           ConnectionResult.SERVICE_DISABLED -> return PlayServicesState.GOOGLE
       }
       val huaweiAPI = HuaweiApiAvailability.getInstance()
       when (huaweiAPI.isHuaweiMobileNoticeAvailable(context)) {
           com.huawei.hms.api.ConnectionResult.SUCCESS,
           com.huawei.hms.api.ConnectionResult.SERVICE_MISSING,
           com.huawei.hms.api.ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED,
           com.huawei.hms.api.ConnectionResult.SERVICE_DISABLED -> return PlayServicesState.HUAWEI
       }
       return PlayServicesState.UNKNOWN
    }

    В зависимости от полученного значения, сервер будет отправлять пуш-нотификации в соответствующий сервис.

    Организовать их работу является делом техники. Я поступил просто.

    Обработка событий для FCM:

    class AppFcmListenerService : FirebaseMessagingService() {
       private val cloudMessageHandler: CloudMessageHandler = GoogleMessageHandler(this)
       override fun onMessageReceived(message: RemoteMessage) {
           cloudMessageHandler.onMessageReceived(message)
       }
    }

    и HCM:

    class AppHmsListenerService : HmsMessageService() {
       private val cloudMessageHandler: CloudMessageHandler = HuaweiMessageHandler(this)
       override fun onMessageReceived(message: RemoteMessage) {
           cloudMessageHandler.onMessageReceived(message)
       }
    }

    делегируется абстрактному классу:

    abstract class CloudMessageHandler { 
    fun onMessageReceived(message: Any) {
       val data = getData(message)
       onMessageReceived(data)
    }
    private fun onMessageReceived(data: Map<String, String>) {
       //непосредственная обработка пушей 
    }
    abstract fun getData(message: Any) :  Map<String, String> {
    }

    который имеет две имплементации:

    для FCM:

    class FirebaseMessageHandler(override val context: Context) : CloudMessageHandler(context) {
       override fun getData(message: Any): Map<String, String> {
           return (message as RemoteMessage).data
       }
    }

    для HCM:

    class HuaweiMessageHandler(override val context: Context) : CloudMessageHandler(context) {
       override fun getData(message: Any): Map<String, String> {
           return (message as RemoteMessage).dataOfMap
       }
    }

    Классы Huawei имеют почти схожий интерфейс с Google-сервисами. Это «почти» лично у меня проявилось в том, что у RemoteMessage из com.google.firebase.messaging для получения Map<String, String> нужно вызвать RemoteMessage.getData(), а для RemoteMessage из com.huawei.hms.pushgetDataOfMap().

    Location Kit – определение локации


    Определением локации может заниматься LOCATION_SERVICE, который доступен без Google и Huawei сервисов. Но для сервиса пассажирских перевозок точное определение локации является важным параметром. Благо, интерфейсы классов Location Kit ничем не отличались от одноименных для Google-сервисов, значит не отличалось и их применение в коде. Однако, сервисы Huawei оказались менее точными в работе. Надеюсь, это временная проблема.
    Также мы столкнулись с тем, что запрос геолокации идет через HMS Core, а значит для корректной работы необходимо выставить права для геолокации как «Разрешить всегда».

    location

    Замена Google карт


    В СНГ мы использовали OSM движок с тайлами 2ГИС. На первом этапе интеграции мы решили не работать с Map Kit из HUAWEI, а просто использовать движок OSM с тайлами OSM. Все просто.

    Перед релизом


    Советую всем обратить внимание на распространенные причины отказа перед запуском вашего приложения в AppGallery.

    Например, релизная команда Huawei сообщила нам о следующих нарушениях.

    Non-Standard Presentation of Hong Kong and Macau

    Китай щепетильно относится к суверенитету своих автономных административных районов и просит их назвать «region». Это вынудило нас изменить все заголовки, где упоминается страна на упоминание страны или региона.



    Information about Third-party Competing Products in App Details

    Не забудьте для оценки приложения отправить пользователя на страницу в App Gallery, а не Google Play Store :)

    В целом Huawei-сервисы аналогичны Google-сервисам и поддержка их непосредственно в коде не вызывает трудностей. Вопросы могут быть только к Huawei «экосистеме» в целом, но их можно списать на ее молодость и с большой вероятностью все будет лучше со временем.
    inDriver
    Ride-hailing app based on peer-to-peer model

    Похожие публикации

    Комментарии 8

      0
      Не подскажете, где можно детально почитать про эту Гармошку? То есть, не общие слова, что она микроядерная, распределённая, безопасная и т.д. и не про сервисы, как у Вас, а более подробно, в частности, про структуру, низкоуровневую организацию и т.д.
        0
        Не искал информации ̶п̶р̶о̶ ̶к̶и̶т̶а̶й̶с̶к̶и̶й̶ ̶ф̶о̶р̶к̶ ̶а̶н̶д̶р̶о̶и̶д̶а̶ саму ОС. Но в вашем случае я бы покопался в github.com/Awesome-HarmonyOS/HarmonyOS
          0
          Там тоже ничего конкретного. По крайней мере не на китайском. Называть Гармошку (пусть и зачёркнуто) форком Андроида странно — в ней заявлена микроядерность, на которую робот никак не тянет ибо Linux.
        0
        Как насчет отчета о сбоях и ошибках, чем удалось заменить Crashlytics Firebase?
          0

          У Huawei есть аналог с аналогичным API

            0

            Можно развернуть на своём серваке https://github.com/ACRA/acra.

              0
              Если судить по данным, что которые нам приходят в Firebase, то аналитика нормально работает на Huawei девайсах и ее можно не заменять. Официальная техническая поддержка Huawei, говорит тоже самое.
                0

                Не мерили аудиторию без гугл сервисов?

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое