Вам не нужны наборы Android App Bundle. Пользователи Realm, слушайте!

Недавно мы достигли предела загрузки нашего приложения в Google Play Store, который составляет 100 МБ. Вместо использования Android App Bundle мы хотели посмотреть, сможем ли мы уменьшить размер нашего приложения без него.

Итак, мы проанализировали наш APK:

Обратите внимание на пять папок в папке lib.

Они занимают много места! Оказывается, эти папки представляют собой скомпилированные ABI, которые в основном представляют собой машинный код Android. Вот так код, который вы пишете, фактически запускается на процессоре вашего устройства. Вашему устройству нужна одна из этих папок для запуска вашего кода.

Так почему в нашем APK пять ABI, когда устройству нужен только один? Короче говоря, мы используем Realm для нашего приложения, а Realm использует NDK или Native Development Kit. Поэтому, когда мы создаем наш APK, мы получаем скомпилированный код для каждой платформы, встроенной в наш APK. Иди разберись!

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

Один APK, чтобы править всеми…?

Итак, что мы можем сделать, так это разделить наш универсальный APK на несколько APK, разделенных на ABI. Код для этого прост и хорошо документирован здесь. Добавьте этот код в файл Gradle вашего приложения:

android {
    splits {
        abi {
            enable true

            reset()

            include 'armeabi-v7a','arm64-v8a', 'x86', 'x86_64', 'armeabi'

            universalApk false
        }
    }
...
}

Ниже в файле добавьте следующее:

ext.abiCodes = ["armeabi":1, "x86":2, "armeabi-v7a":3, "arm64-v8a":4, "x86_64":5] // this order matters

import com.android.build.OutputFile
android.applicationVariants.all { variant ->
    variant.outputs.each { output ->


        def baseAbiVersionCode =             project.ext.abiCodes.get(output.getFilter(OutputFile.ABI))

        if (baseAbiVersionCode != null) {
            output.versionCodeOverride =
                    baseAbiVersionCode * 1000 + variant.versionCode
        }
    }
}

Это хорошо задокументировано, но здесь есть подводный камень. Порядок abiCodesmatters. Этот заказ у нас работает. По сути, 32-разрядная версия типа ABI должна иметь более низкий код версии, чем 64-разрядная версия. x86 должен быть кодом версии ниже, чем x86_64. armeabi должен быть кодом версии ниже, чем armeabi-v7a и arm64-v8a.

Говоря об этих кодах ABI, откуда взялись эти значения? Хороший вопрос. Существует x86 (32 и 64 бит), три версии ARM и MIPS ABI. Это много сокращений. Что из этого мне нужно включить?

Мы читали об этом, и это было непонятно. Некоторые включают только 64-битные ABI. Некоторые говорили, что нужно избавиться от MIPS ABI, что мы и сделали (ни одно современное устройство Android не поддерживает MIPS). Мы использовали официальную документацию Android и каталог устройств Google Play Console.

Каталог устройств позволяет мне фильтровать поддерживаемые устройства по ABI. У нас есть устройства, использующие пять ABI в соответствии с этим:

Мы решили включить все пять этих ABI. Согласно документации Android, я думаю, что мы могли бы удалить armeabi из APK, которые мы загружаем в Play Store, без потери поддержки устройств. Но нет причин не загружать его, и я лучше перестрахуюсь, чем сожалею.

Итак, создайте подписанные APK-файлы и загрузите все пять! Это должно выглядеть примерно так:

Вы можете видеть, что каждый APK-файл примерно вдвое меньше. Отлично! И что теперь? Как убедиться, что каждый пользователь получает правильный APK? Что ж, на самом деле тебе не нужно ничего делать с этим. Play Store полностью отвечает за доставку APK. Он определяет, какое устройство есть у пользователя, какой ему нужен ABI, и загружает правильный ABI!

Но на самом деле выигрыш намного превышает размер загрузки, указанный выше.

Когда ваше приложение загружено, оно больше, чем «Размер загрузки приложения», указанный в Play Store.

Это скриншоты похожих устройств Pixel до и после разделения APK. Прирост «размера приложения» составляет около 50%, но мы экономим более 100 МБ на устройствах Pixel!

Магазин Google Play выглядит для вашего конечного пользователя точно так же, и им не нужно ничего функционально отличаться. В следующем обновлении их приложение будет вдвое меньше!

Поэтому мы уменьшили размер нашего приложения на 50%, и вашим конечным пользователям не нужно ничего делать. Большой. Но давайте обратимся к слону в комнате. Почему мы не использовали только наборы Android App Bundle? Он делает то же самое, что замечательно резюмирует этот GIF:

Вы заметите, что к устройству справа идет только один ABI. Вот что мы только что сделали! Он также упаковывает только один язык и один набор ресурсов на устройство. Фактически вы также можете сделать это самостоятельно так же, как мы сделали это с ABI. Подробнее здесь. Мы не сделали этого, потому что ABI действительно раздували наше приложение в то время.

Мы определили объем работ, необходимых для использования AAB, а не разбили его сами. Оба варианта стоят очень дешево, но мы не хотели передавать подписку Google. Это не ограничивается только распространением в Google Play Store. Мы также уже придумали, как подписывать все наши CI, поэтому то, что подписание выполнялось нами, не было большой победой.

Теперь мы также можем тестировать каждый APK по отдельности. Не должно быть никаких различий в способах преформы каждого APK, но если в одном есть очень низкоуровневая ошибка, вы можете установить и отладить этот APK индивидуально, а не полагаться на весь APK-файл, связанный с AAB.

Наконец, переход на AAB - это необратимый процесс. После того, как вы подписались, вы не можете отказаться. Теперь ваше приложение подписано Google, и вы больше не сможете подписать его самостоятельно. Ключ подписи вашего приложения принадлежит Google.

Теперь мы признаем, что большинство из этих причин - это просто шляпа из фольги. Но затраты на то, чтобы сделать это самостоятельно, настолько низки, что на самом деле это не большой компромисс. Честно говоря, было очень весело!

Бонусный ход: теперь, когда вы открыли шлюзы для нескольких APK, вы можете делать довольно крутые вещи с динамической доставкой.

Допустим, у вас есть бета-программа и вы запускаете новую функцию только для части новых пользователей. Типичный процесс заключался бы в том, чтобы упаковать функцию в приложение и ограничить доступ только для некоторых пользователей с некоторыми функциями. Но при этом в ваше приложение добавляется функция, которую может увидеть не каждый пользователь.

Что вы можете сделать, так это загрузить свой базовый APK и загрузить APK функции. Затем с помощью динамической доставки вы можете распространять и объединять APK-файл функции только для тех пользователей, которым он нужен. Об этом написана отличная статья здесь.

Спасибо за чтение и аплодисменты, если эта статья вам помогла!