Привет, Хабр! С мая 2019 года из-за санкций США мы остались без приложений и API для Android от Google. Из-за этого нашим устройствам грозило будущее без push-уведомлений, магазина и облачных сервисов.
Естественно, мы не опустили руки, а разработали и запустили платформу Huawei Mobile Services, которая заменила сервисы Google для наших устройств. Чтобы вы с ней познакомились и интегрировали в свои приложения, мы собрали 10 самых популярных вопросов, которые возникают у разработчиков, впервые столкнувшихся с HMS.
Естественно, мы не опустили руки, а разработали и запустили платформу Huawei Mobile Services, которая заменила сервисы Google для наших устройств. Чтобы вы с ней познакомились и интегрировали в свои приложения, мы собрали 10 самых популярных вопросов, которые возникают у разработчиков, впервые столкнувшихся с HMS.
1. Про GMS я прекрасно все знаю, а что включает платформа HMS?
Платформа HMS полностью заменяет GMS, поэтому почти на каждый сервис Google у нас есть альтернатива: браузер, голосовой помощник, магазин приложений, облачные сервисы и инструменты для разработчиков.

Так как мы разрабатывали сервисы с нуля, то сразу разделили их на группы:
- Сервисы AppGallery Connect позволяют загружать в облако Huawei темы, контент, распространять приложения и собирать статистику и отчеты об их использовании и падениях.
- Сервисы HMS Core включают 14 инструментов, которые обеспечивают базовые функции телефона и взаимодействие с пользователями: push-сообщения, рекламу, авторизацию, аналитику и обмен сообщениями.
- Инструменты для специальных возможностей, как и у Google, включают в себя платформы для работы с VR, AR и enterprise-разработки, например, для AI и IoT.
- Инструменты для разработчиков — это собственный плагин для Android Studio (DevEco Studio) и сервисы облачного тестирования. Также для удобства разработки есть конвертер, который преобразует для работы с HMS код, связанный с GMS. Конвертер полностью поддерживает миграцию push-сообщений, рекламы и частично — кода для inapp-покупок.
Лучше всего начать знакомство с сервисов HMS Core, так как именно с ними будет взаимодействовать большинство приложений. С их помощью можно своевременно узнавать о сбоях в приложении, отслеживать продуктовые метрики и общаться с пользователями. Например, Push Kit отсылает push-сообщения пользователям:

2. Как мне быстро адаптировать свои приложения для работы с HMS и протестировать их без телефонов Huawei или Honor??
А вот тут мы постарались: сделали облачные сервисы тестирования и дебаггинга, которым у Google нет аналога, и конвертер для адаптации готового кода для нашей платформы.
Облачные сервисы для разработки и тестирования доступны на главном экране консоли сразу после регистрации на платформе и подтверждения доступа. С их помощью можно из веб-интерфейса запускать эмулятор на серверах Huawei, чтобы не покупать ферму устройств или мощные компьютеры.
Экран с сервисами можно конфигурировать, добавляя или удаляя элементы:

В Cloud testing прямо из консоли перед релизом запускаются автотесты и отслеживаются обнаруженные баги:

В Cloud Debugging можно получить доступ к эмуляторам. Для этого нужно с помощью поиска по региону, серии, версии андроида и версии EMUI выбрать целевой телефон:

Прямо из веб-интерфейса можно загрузить приложение и покликать по нему:

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

Такой способ будет удобен для разработчиков тяжеловесных приложений, так как дает возможность проводить тесты на большом количестве устройств прямо из веб-консоли.
Еще одним удобным инструментом для быстрой адаптации приложений для нашей платформы является Huawei Core ToolKit plugin для Android Studio, который конвертирует код, работающий с GMS, под нашу платформу. Установить плагин можно прямо из встроенного магазина:

После установки в трее появится пункт HMS. Чтобы начать конвертацию, нужно выбрать New Conversion:

Перед конвертацией необходимо указать директорию для резервного копирования:

И подтвердить конвертацию:

В коде проекта можно вручную отследить каждое изменение и либо применить его, либо проигнорировать. Дополнительно после конвертации в директории проекта появится отдельный модуль с README. md, где можно будет посмотреть, что делает сконвертированный код.

3. Что с аутентификацией пользователя?
По аналогии с «Sign with Google» и «Sign with Apple» у нас есть свой сервис «Sign with HUAWEI». Он является частью Account Kit и работает со всеми пользовательскими устройствами Huawei и Honor:

4. С пользователем понятно, а как идентифицировать телефон?
Мы поддерживаем работу с Android id, а для верификации по номеру телефона в Account Kit у нас есть аналог SMSRetriever. Эта функция активируется на 5 минут командой:
val task = ReadSmsManager.start(this@MainActivity)
task.addOnCompleteListener {
if (task.isSuccessful) {
// The service is enabled successfully. Continue with the process.
Toast.makeText(this, "ReadSms service has been enabled.", Toast.LENGTH_LONG).show()
} else {
Toast.makeText(this, "The service failed to be enabled.", Toast.LENGTH_LONG).show()
}
}
Также должен быть включен Broadcast Receiver с фильтром на READ_SMS_BROADCAST_ACTION:
val intentFilter = IntentFilter(READ_SMS_BROADCAST_ACTION)
registerReceiver(MyBroadcastReceiver(), intentFilter)
Чтобы Account Kit увидел СМС, оно должно выглядеть так:
prefix_flag short message verification code is XXXXXX hash_value
prefix_flag — это префикс сообщения. Account Kit распознает <#>, [#] и невидимые Unicode символы, например, \u200b.
short message verification code is XXXXXX — текст для пользователя.
hash_value — хеш от имени пакета.
Пользователь получит сообщение «<#> Habratest: 458329 — Just test code qkASxAkMJOE».
5. В моем приложении есть реклама. Я могу ее показывать с помощью HMS?
Пока рекламная платформа доступна только для корпоративных аккаунтов, но скоро её смогут внедрить в свои приложения все разработчики. Для этого у нас уже готов свой Ads Kit. В нем можно создавать баннеры и интегрировать их в приложение. Баннеры представляют собой объекты View в Android. Добавить их можно так:
<com.huawei.hms.ads.banner.BannerView
android:id="@+id/hw_banner_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
hwads:adId="testw6vs28auh3"
hwads:bannerSize="BANNER_SIZE_360_57"/>
Кроме банерной рекламы, есть Native Ads, которые встраиваются через верстку:
<com.huawei.hms.ads.nativead.NativeView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
... >
<RelativeLayout
... >
<com.huawei.hms.ads.nativead.MediaView
android:id="@+id/ad_media"
... />
<RelativeLayout
... >
<TextView
android:id="@+id/ad_title"
... />
// Other assets.
...
</RelativeLayout>
// Other assets.
...
</RelativeLayout>
</com.huawei.hms.ads.nativead.NativeView>

Также есть Rewarded Ads, Interstitial Ads и реклама на сплеш-скрине — Splash Ads, обо всем этом можно прочитать в документации:

Платформа позволяет зарабатывать на встроенных покупках. Чтобы создать этот инструмент, в консоли разработчика необходимо добавить продукт:

И в самом приложении уже добавить код покупки:
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int pos, long l) {
String productId = (String) products.get(pos).get(DemoActivity.this.item_productId);
gotoPay(DemoActivity.this, productId, IapClient.PriceType.INAPP_CONSUMABLE);
}
});
Если приложение использует свою систему оплаты, то будет полезно прочитать про Wallet Kit. Этот SDK позволяет хранить безопасно данные банковских, подарочных и скидочных карт, купонов и билетов на транспорт или мероприятия. Включить функции Wallet Kit также можно из консоли разработчика:

6. А приложение сможет одновременно работать и с HMS, и с GMS?
Да, проблем не будет. Подключение двух сервисов в приложении ничем не отличается от установки одной библиотеки. Единственное, лучше проверять и инициализировать библиотеки в зависимости от поддержки.
Для AppGallery не обязательно делать отдельную сборку. Можно загружать ту же сборку, что и в остальные маркеты, и, например, в рантайме выбирать, инициализировать ли HMS/GMS. Конечно, лучше разделить эти сборки, но концептуально у разработчика есть свобода выбора.

После добавлении библиотеки в семпл APK «прибавляет в весе» всего ~500кб. Для сравнения, GMS «съедают» чуть больше — примерно 600кб.

7. Тогда встает вопрос о работе на «родном» Android от Google. Как будет функционировать приложение с Push Kit от Huawei без HMS на устройстве?
Если на устройстве без HMS запустить HMS библиотеку, ничего страшного не случится. Например, если инициализировать Push Kit, в лог посыпятся ошибки вида Failed to find HMS apk, а пользователя встретит экран с просьбой установить HMS:

Лучше убирать запросы к HMS под if’ы. Например, так можно проверить, есть ли на устройстве HMS:
fun isHmsAvailable(context: Context): Boolean {
val result =
HuaweiApiAvailability.getInstance().isHuaweiMobileServicesAvailable(context)
val isAvailable = ConnectionResult.SUCCESS == result
Log.i(TAG, "isHmsAvailable: " + isAvailable);
return isAvailable;
}
Обратная ситуация (когда GMS нет) тоже возможна:
fun isGmsAvailable(context: Context): Boolean {
val result =
GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(context)
val isAvailable = ConnectionResult.SUCCESS == result
Log.i(TAG, "isGmsAvailable: " + isAvailable);
return isAvailable;
}
8. Давайте поговорим про аналоги сервисов Google. Чем можно заменить firebase database?
На данный момент существует сервис, очень похожий на Firebase Database — Cloud DB, который находится в Beta-доступе:

Импортировать данные можно так:
CloudDBZoneTask<Integer> upsertTask = mCloudDBZone.executeUpsert(bookInfo);
if (mUiCallBack == null) {
return;
}
upsertTask.addOnSuccessListener(new OnSuccessListener<Integer>() {
@Override
public void onSuccess(Integer cloudDBZoneResult) {
Log.w(TAG, "upsert " + cloudDBZoneResult + " records");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
mUiCallBack.updateUiOnError("Insert book info failed");
}
});
Экспортировать чуть сложнее из-за особенности облачной базы данных. После регистрации слушателя устройство будет оповещено об изменении данных на сервере и обновит их у себя локально:
private OnSnapshotListener<BookInfo> mSnapshotListener = new OnSnapshotListener<BookInfo>() {
@Override
public void onSnapshot(CloudDBZoneSnapshot<BookInfo> cloudDBZoneSnapshot, AGConnectCloudDBException e) {
if (e != null) {
Log.w(TAG, "onSnapshot: " + e.getMessage());
return;
}
CloudDBZoneObjectList<BookInfo> snapshotObjects = cloudDBZoneSnapshot.getSnapshotObjects();
List<BookInfo> bookInfos = new ArrayList<>();
...
9. Ок, заинтересовали. А что у вас с магазином приложений? Как идет ревью, можно ли добавлять бета-тестировщиков в Huawei AppGallery?
Правила публикации не сильно отличаются от правил в Google Play и Apple AppStore, а сама проверка приложения занимает два рабочих дня. Правда, для рынка КНР потребуются дополнительные документы, такие как сертификат об авторском праве.
Функция бета-теста в AppGallery есть. Можно настроить ранний доступ или ограничивать установку приложения, например, по моделям Huawei/Honor:

Также для удобного релиза приложений Huawei AppGallery поддерживает загрузку App Bundle и Split APK:

10. Можно ли настроить автодеплой в Huawei AppGallery?
Да, это легко можно сделать через Publishing API AppGallery. Для начала нужно получить Upload URL:
HttpGet get = new HttpGet("https://connect-api.cloud.huawei.com/api/publish/v2/upload-url?appId=" + appId + "&suffix=" + suffix);
get.setHeader("Authorization", "Bearer " + token);
get.setHeader("client_id", clientId);
Затем загрузить файл:
HttpEntity reqEntity = MultipartEntityBuilder.create()
.addPart("file", bin)
.addTextBody("authCode", authCode) // Obtain the authentication code.
.addTextBody("fileCount", "1")
.addTextBody("parseType","1")
.build();
И финально обновить информацию о сборке:
HttpPut put = new HttpPut(domain + "/publish/v2/app-info?appId=" + appId);
put.setHeader("Authorization", "Bearer " + token);
put.setHeader("client_id", clientId);
JSONObject keyString = new JSONObject();
//Request Body
keyString.put("defaultLang", "zh-CN");
keyString.put("isFree", 0);
keyString.put("childType", 15);
keyString.put("grandChildType", 10043);
Уфф, вроде ответили. Правда, в процессе работы вопросов будет гораздо больше, но вы всегда можете найти документацию по нашим сервисам на официальном портале. Там же можно получить техническую поддержку, посмотреть обучающие видео и зарегистрировать учетную запись разработчика.
Хабр, 谢谢您的关注!
Хабр, 谢谢您的关注!