Google Play требует, чтобы размер сжатого APK-файла, который загружают пользователи, не превышал 100 МБ. Для большинства приложений этого достаточно места для всего кода и ресурсов приложения. Однако некоторым приложениям требуется больше места для высококачественной графики, мультимедийных файлов или других крупных ресурсов. Раньше, если размер сжатой загрузки вашего приложения превышал 100 МБ, вам приходилось самостоятельно размещать и загружать дополнительные ресурсы, когда пользователь открывает приложение. Хостинг и обслуживание дополнительных файлов могут быть дорогостоящими, а пользовательский опыт часто не идеален. Чтобы облегчить вам этот процесс и сделать его более приятным для пользователей, Google Play позволяет вам прикрепить два больших файла расширения, дополняющих ваш APK.
Google Play размещает файлы расширения для вашего приложения и бесплатно доставляет их на устройство. Файлы расширения сохраняются в общем хранилище устройства (SD-карта или раздел, подключаемый через USB; также известное как «внешнее» хранилище), где ваше приложение может получить к ним доступ. На большинстве устройств Google Play загружает файлы расширения одновременно с загрузкой APK, поэтому в вашем приложении есть все необходимое, когда пользователь открывает его в первый раз. Однако в некоторых случаях ваше приложение должно загружать файлы из Google Play при запуске.
Если вы не хотите использовать файлы расширения, а размер сжатой загрузки вашего приложения превышает 100 МБ, вместо этого вам следует загрузить приложение с помощью пакетов Android App Bundle , которые позволяют загрузить сжатый размер загрузки до 200 МБ. Кроме того, поскольку использование пакетов приложений откладывает создание APK и подписание в Google Play, пользователи загружают оптимизированные APK, содержащие только код и ресурсы, необходимые для запуска вашего приложения. Вам не нужно создавать, подписывать и управлять несколькими APK-файлами или файлами расширений, а пользователи получают меньшие по размеру и более оптимизированные загрузки.
Обзор
Каждый раз, когда вы загружаете APK с помощью консоли Google Play, у вас есть возможность добавить в APK один или два файла расширения. Каждый файл может иметь размер до 2 ГБ и иметь любой выбранный вами формат, но мы рекомендуем использовать сжатый файл для экономии полосы пропускания во время загрузки. Концептуально каждый файл расширения играет различную роль:
- Основной файл расширения — это основной файл расширения для дополнительных ресурсов, необходимых вашему приложению.
- Файл расширения патча является необязательным и предназначен для небольших обновлений основного файла расширения.
Хотя вы можете использовать два файла расширения по своему усмотрению, мы рекомендуем, чтобы основной файл расширения доставлял основные ресурсы и обновлялся редко, если вообще когда-либо; файл расширения исправлений должен быть меньшего размера и служить «носителем исправлений», обновляясь с каждым основным выпуском или по мере необходимости.
Однако даже если для обновления вашего приложения требуется только новый файл расширения исправления, вам все равно необходимо загрузить новый APK с обновленным versionCode
в манифесте. (Play Console не позволяет загружать файл расширения в существующий APK.)
Примечание. Файл расширения патча семантически такой же, как и основной файл расширения — вы можете использовать каждый файл по своему усмотрению.
Формат имени файла
Каждый загружаемый вами файл расширения может иметь любой выбранный вами формат (ZIP, PDF, MP4 и т. д.). Вы также можете использовать инструмент JOBB для инкапсуляции и шифрования набора файлов ресурсов и последующих исправлений для этого набора. Независимо от типа файла, Google Play считает их непрозрачными двоичными объектами и переименовывает файлы по следующей схеме:
[main|patch].<expansion-version>.<package-name>.obb
В этой схеме есть три компонента:
-
main
илиpatch
- Указывает, является ли файл основным или файлом расширения исправления. Для каждого APK может быть только один основной файл и один файл исправления.
-
<expansion-version>
- Это целое число, соответствующее коду версии APK, с которым впервые связано расширение (оно соответствует значению
android:versionCode
приложения).«Первый» подчеркивается, потому что, хотя Play Console позволяет повторно использовать загруженный файл расширения с новым APK, имя файла расширения не меняется — оно сохраняет версию, примененную к нему при первой загрузке файла.
-
<package-name>
- Имя пакета вашего приложения в стиле Java.
Например, предположим, что ваша версия APK — 314159, а имя вашего пакета — com.example.app. Если вы загружаете основной файл расширения, он переименовывается в:
main.314159.com.example.app.obb
Место хранения
Когда Google Play загружает файлы расширения на устройство, они сохраняются в общем хранилище системы. Чтобы обеспечить правильное поведение, нельзя удалять, перемещать или переименовывать файлы расширения. Если ваше приложение должно само выполнить загрузку из Google Play, вам необходимо сохранить файлы в том же месте.
Метод getObbDir()
возвращает конкретное местоположение файлов расширения в следующей форме:
<shared-storage>/Android/obb/<package-name>/
-
<shared-storage>
— это путь к общему пространству хранения, доступному изgetExternalStorageDirectory()
. -
<package-name>
— это имя пакета вашего приложения в стиле Java, доступное изgetPackageName()
.
Для каждого приложения в этом каталоге не может быть более двух файлов расширения. Один из них — основной файл расширения, а другой — файл расширения исправлений (при необходимости). Предыдущие версии перезаписываются при обновлении приложения новыми файлами расширения. Начиная с Android 4.4 (уровень API 19), приложения могут читать файлы расширения OBB
без разрешения внешнего хранилища. Однако некоторые реализации Android 6.0 (уровень API 23) и более поздних версий по-прежнему требуют разрешения, поэтому вам нужно будет объявить разрешение READ_EXTERNAL_STORAGE
в манифесте приложения и запросить разрешение во время выполнения следующим образом:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Для Android версии 6 и более поздних версий разрешение на внешнее хранилище необходимо запрашивать во время выполнения. Однако некоторые реализации Android не требуют разрешения на чтение файлов OBB. В следующем фрагменте кода показано, как проверить доступ на чтение перед запросом разрешения на внешнее хранилище:
Котлин
val obb = File(obb_filename) var open_failed = false try { BufferedReader(FileReader(obb)).also { br -> ReadObbFile(br) } } catch (e: IOException) { open_failed = true } if (open_failed) { // request READ_EXTERNAL_STORAGE permission before reading OBB file ReadObbFileWithPermission() }
Ява
File obb = new File(obb_filename); boolean open_failed = false; try { BufferedReader br = new BufferedReader(new FileReader(obb)); open_failed = false; ReadObbFile(br); } catch (IOException e) { open_failed = true; } if (open_failed) { // request READ_EXTERNAL_STORAGE permission before reading OBB file ReadObbFileWithPermission(); }
Если вам необходимо распаковать содержимое файлов расширения, не удаляйте после этого файлы расширения OBB
и не сохраняйте распакованные данные в том же каталоге. Вам следует сохранить распакованные файлы в каталоге, указанном getExternalFilesDir()
. Однако, если возможно, лучше всего использовать формат файла расширения, который позволяет вам читать непосредственно из файла, а не требовать распаковки данных. Например, мы предоставили проект библиотеки под названием APK Expansion Zip Library , который считывает ваши данные непосредственно из ZIP-файла.
Внимание. В отличие от файлов APK, любые файлы, сохраненные в общем хранилище, могут быть прочитаны пользователем и другими приложениями.
Совет: Если вы упаковываете медиафайлы в ZIP-архив, вы можете использовать вызовы воспроизведения мультимедиа для файлов с элементами управления смещением и длиной (например MediaPlayer.setDataSource()
и SoundPool.load()
) без необходимости распаковывать ZIP-архив. Чтобы это работало, вы не должны выполнять дополнительное сжатие медиафайлов при создании ZIP-пакетов. Например, при использовании инструмента zip
вам следует использовать опцию -n
, чтобы указать суффиксы файлов, которые не следует сжимать:
zip -n .mp4;.ogg main_expansion media_files
Процесс загрузки
В большинстве случаев Google Play загружает и сохраняет файлы расширений одновременно с загрузкой APK на устройство. Однако в некоторых случаях Google Play не может загрузить файлы расширения или пользователь мог удалить ранее загруженные файлы расширения. Чтобы справиться с такими ситуациями, ваше приложение должно иметь возможность загружать файлы самостоятельно при запуске основного действия, используя URL-адрес, предоставленный Google Play.
Процесс загрузки с высокого уровня выглядит так:
- Пользователь выбирает установку вашего приложения из Google Play.
- Если Google Play может загружать файлы расширения (что характерно для большинства устройств), он загружает их вместе с APK.
Если Google Play не может загрузить файлы расширения, он загружает только APK.
- Когда пользователь запускает ваше приложение, оно должно проверить, сохранены ли уже файлы расширения на устройстве.
- Если да, ваше приложение готово к работе.
- Если нет, ваше приложение должно загрузить файлы расширения по HTTP из Google Play. Ваше приложение должно отправить запрос клиенту Google Play с помощью службы лицензирования приложений Google Play, которая отвечает именем, размером файла и URL-адресом для каждого файла расширения. Используя эту информацию, вы затем загружаете файлы и сохраняете их в нужном месте хранения .
Внимание! Крайне важно включить необходимый код для загрузки файлов расширения из Google Play на тот случай, если файлы еще не находятся на устройстве при запуске вашего приложения. Как обсуждается в следующем разделе о загрузке файлов расширения , мы предоставили вам библиотеку, которая значительно упрощает этот процесс и выполняет загрузку из службы с минимальным количеством вашего кода.
Контрольный список разработки
Вот краткое описание задач, которые вам следует выполнить, чтобы использовать файлы расширения в вашем приложении:
- Сначала определите, должен ли размер загрузки вашего приложения в сжатом виде превышать 100 МБ. Пространство очень ценно, и общий размер загружаемого файла должен быть как можно меньшим. Если ваше приложение использует более 100 МБ для предоставления нескольких версий ваших графических ресурсов для разных плотностей экрана, рассмотрите возможность публикации нескольких APK-файлов , в которых каждый APK-файл содержит только ресурсы, необходимые для целевых экранов. Для достижения наилучших результатов при публикации в Google Play загрузите пакет Android App Bundle , который включает в себя весь скомпилированный код и ресурсы вашего приложения, но не позволяет создавать APK и подписываться на Google Play.
- Определите, какие ресурсы приложения следует отделить от вашего APK, и упакуйте их в файл, который будет использоваться в качестве основного файла расширения.
Обычно при обновлении основного файла расширения следует использовать только второй файл расширения исправления. Однако если ваши ресурсы превышают лимит в 2 ГБ для основного файла расширения, вы можете использовать файл исправления для остальных ваших ресурсов.
- Разработайте свое приложение так, чтобы оно использовало ресурсы из файлов расширения в общем хранилище устройства.
Помните, что вы не должны удалять, перемещать или переименовывать файлы расширения.
Если ваше приложение не требует определенного формата, мы предлагаем вам создать ZIP-файлы для файлов расширения, а затем прочитать их с помощью Zip-библиотеки расширений APK .
- Добавьте в основное действие вашего приложения логику, которая проверяет наличие файлов расширения на устройстве при запуске. Если файлов нет на устройстве, воспользуйтесь службой лицензирования приложений Google Play, чтобы запросить URL-адреса файлов расширения, а затем загрузите и сохраните их.
Чтобы значительно сократить объем кода, который вам необходимо написать, и обеспечить удобство работы пользователя во время загрузки, мы рекомендуем вам использовать библиотеку Downloader для реализации вашего поведения при загрузке.
Если вы создаете собственную службу загрузки вместо использования библиотеки, имейте в виду, что вы не должны изменять имена файлов расширения и должны сохранять их в правильном месте хранения .
Завершив разработку приложения, следуйте руководству по тестированию файлов расширения .
Правила и ограничения
Добавление файлов расширения APK — это функция, доступная при загрузке приложения с помощью Play Console. При первой загрузке приложения или обновлении приложения, использующего файлы расширения, вы должны учитывать следующие правила и ограничения:
- Размер каждого файла расширения не может превышать 2 ГБ.
- Чтобы загрузить файлы расширения из Google Play, пользователь должен приобрести ваше приложение из Google Play . Google Play не будет предоставлять URL-адреса ваших файлов расширения, если приложение было установлено другим способом.
- При загрузке из вашего приложения URL-адрес, который Google Play предоставляет для каждого файла, уникален для каждой загрузки, и срок действия каждого из них истекает вскоре после его передачи в ваше приложение.
- Если вы обновляете свое приложение новым APK или загружаете несколько APK для одного и того же приложения, вы можете выбрать файлы расширения, которые вы загрузили для предыдущего APK. Имя файла расширения не меняется — оно сохраняет версию, полученную APK, с которым файл был изначально связан.
- Если вы используете файлы расширения в сочетании с несколькими APK-файлами , чтобы предоставить разные файлы расширения для разных устройств, вам все равно придется загружать отдельные APK для каждого устройства, чтобы предоставить уникальное значение
versionCode
и объявить разные фильтры для каждого APK. - Вы не можете обновить свое приложение, изменив только файлы расширения — вам необходимо загрузить новый APK , чтобы обновить свое приложение. Если ваши изменения касаются только ресурсов в файлах расширения, вы можете обновить APK, просто изменив
versionCode
(и, возможно, такжеversionName
). - Не сохраняйте другие данные в каталог
obb/
. Если вам необходимо распаковать некоторые данные, сохраните их в место, указанноеgetExternalFilesDir()
. - Не удаляйте и не переименовывайте файл расширения
.obb
(если только вы не выполняете обновление). Это приведет к тому, что Google Play (или само ваше приложение) будет неоднократно загружать файл расширения. - При обновлении файла расширения вручную необходимо удалить предыдущий файл расширения.
Загрузка файлов расширения
В большинстве случаев Google Play загружает и сохраняет файлы расширений на устройство одновременно с установкой или обновлением APK. Таким образом, файлы расширения будут доступны при первом запуске вашего приложения. Однако в некоторых случаях ваше приложение должно само загрузить файлы расширения, запросив их по URL-адресу, предоставленному вам в ответе службы лицензирования приложений Google Play.
Основная логика, необходимая для загрузки файлов расширения, следующая:
- При запуске приложения найдите файлы расширения в общем хранилище (в каталоге
Android/obb/<package-name>/
).- Если файлы расширения есть, все готово, и ваше приложение может продолжать работу.
- Если файлов расширения нет :
- Выполните запрос с помощью лицензирования приложений Google Play, чтобы получить имена, размеры и URL-адреса файлов расширения вашего приложения.
- Используйте URL-адреса, предоставленные Google Play, чтобы загрузить файлы расширения и сохранить их. Вы должны сохранить файлы в общем хранилище (
Android/obb/<package-name>/
) и использовать точное имя файла, указанное в ответе Google Play.Примечание. URL-адрес, который Google Play предоставляет для ваших файлов расширения, уникален для каждой загрузки, и срок действия каждого из них истекает вскоре после того, как он будет передан вашему приложению.
Если ваше приложение бесплатное (а не платное), возможно, вы не пользовались службой лицензирования приложений . В первую очередь он предназначен для обеспечения соблюдения политик лицензирования вашего приложения и обеспечения того, чтобы пользователь имел право использовать ваше приложение (он или она по праву заплатили за него в Google Play). Чтобы облегчить функциональность файлов расширения, служба лицензирования была усовершенствована и теперь предоставляет ответ вашему приложению, включающий URL-адрес файлов расширения вашего приложения, размещенных в Google Play. Таким образом, даже если ваше приложение бесплатно для пользователей, вам необходимо включить библиотеку проверки лицензий (LVL), чтобы использовать файлы расширения APK. Конечно, если ваше приложение бесплатное, вам не нужно принудительно проверять лицензию — вам просто нужна библиотека для выполнения запроса, который возвращает URL-адрес ваших файлов расширения.
Примечание. Независимо от того, является ли ваше приложение бесплатным или нет, Google Play возвращает URL-адреса файлов расширения, только если пользователь приобрел ваше приложение из Google Play.
В дополнение к LVL вам понадобится набор кода, который загружает файлы расширения через HTTP-соединение и сохраняет их в нужном месте в общем хранилище устройства. При встраивании этой процедуры в свое приложение следует принять во внимание несколько вопросов:
- На устройстве может не хватить места для файлов расширения, поэтому перед началом загрузки следует проверить это и предупредить пользователя, если места недостаточно.
- Загрузка файлов должна происходить в фоновой службе, чтобы избежать блокировки взаимодействия с пользователем и позволить пользователю покинуть ваше приложение после завершения загрузки.
- Во время запроса и загрузки могут возникнуть различные ошибки, которые необходимо корректно обрабатывать.
- Сетевое подключение может измениться во время загрузки, поэтому вам следует обрабатывать такие изменения и в случае прерывания возобновить загрузку, если это возможно.
- Хотя загрузка происходит в фоновом режиме, вы должны предоставить уведомление, показывающее ход загрузки, уведомляющее пользователя о ее завершении и возвращающее пользователя в ваше приложение, если оно выбрано.
Чтобы упростить вам эту работу, мы создали библиотеку загрузчика , которая запрашивает URL-адреса файлов расширения через службу лицензирования, загружает файлы расширения, выполняет все перечисленные выше задачи и даже позволяет вашей активности приостанавливать и возобновлять загрузку. . Добавив в свое приложение библиотеку Downloader и несколько перехватчиков кода, почти вся работа по загрузке файлов расширения уже запрограммирована за вас. Таким образом, чтобы обеспечить наилучшее взаимодействие с пользователем с минимальными усилиями с вашей стороны, мы рекомендуем вам использовать библиотеку загрузчика для загрузки файлов расширения. Информация в следующих разделах объясняет, как интегрировать библиотеку в ваше приложение.
Если вы предпочитаете разработать собственное решение для загрузки файлов расширения с использованием URL-адресов Google Play, вам необходимо следовать документации по лицензированию приложения , чтобы выполнить запрос на лицензию, а затем получить имена, размеры и URL-адреса файлов расширения из дополнительных ответов. Вам следует использовать класс APKExpansionPolicy
(включенный в библиотеку проверки лицензий) в качестве политики лицензирования, которая фиксирует имена, размеры и URL-адреса файлов расширения из службы лицензирования.
О библиотеке загрузчика
Чтобы использовать файлы расширения APK с вашим приложением и обеспечить наилучшее взаимодействие с пользователем с минимальными усилиями с вашей стороны, мы рекомендуем вам использовать библиотеку загрузчика, которая включена в пакет библиотеки расширений APK Google Play. Эта библиотека загружает ваши файлы расширения в фоновом режиме, показывает пользователю уведомление о состоянии загрузки, обрабатывает потерю сетевого подключения, возобновляет загрузку, когда это возможно, и многое другое.
Чтобы реализовать загрузку файлов расширения с помощью библиотеки Downloader, все, что вам нужно сделать, это:
- Расширьте специальный подкласс
Service
и подклассBroadcastReceiver
, каждый из которых потребует от вас всего несколько строк кода. - Добавьте в свое основное действие некоторую логику, которая проверяет, были ли уже загружены файлы расширения, и, если нет, запускает процесс загрузки и отображает пользовательский интерфейс прогресса.
- Реализуйте интерфейс обратного вызова с несколькими методами в своем основном действии, который будет получать обновления о ходе загрузки.
В следующих разделах объясняется, как настроить приложение с помощью библиотеки загрузчика.
Подготовка к использованию библиотеки загрузчика
Чтобы использовать библиотеку загрузчика, вам необходимо загрузить два пакета из диспетчера SDK и добавить соответствующие библиотеки в свое приложение.
Сначала откройте диспетчер Android SDK ( Инструменты > Диспетчер SDK ) и в разделе «Внешний вид и поведение» > «Настройки системы» > Android SDK выберите вкладку «Инструменты SDK» , чтобы выбрать и загрузить:
- Пакет библиотеки лицензирования Google Play
- Пакет библиотеки расширений Google Play APK
Создайте новый библиотечный модуль для библиотеки проверки лицензии и библиотеки загрузчика. Для каждой библиотеки:
- Выберите «Файл» > «Создать» > «Новый модуль» .
- В окне «Создать новый модуль» выберите «Библиотека Android» , а затем нажмите «Далее» .
- Укажите имя приложения/библиотеки , например «Библиотека лицензий Google Play» и «Библиотека загрузчика Google Play», выберите «Минимальный уровень SDK» и нажмите «Готово» .
- Выберите «Файл» > «Структура проекта» .
- Выберите вкладку «Свойства» и в «Репозитории библиотек» введите библиотеку из каталога
<sdk>/extras/google/
(play_licensing/
для библиотеки проверки лицензии илиplay_apk_expansion/downloader_library/
для библиотеки загрузчика). - Выберите ОК , чтобы создать новый модуль.
Примечание. Библиотека загрузчика зависит от библиотеки проверки лицензии. Обязательно добавьте библиотеку проверки лицензий в свойства проекта библиотеки загрузчика.
Или из командной строки обновите проект, включив в него библиотеки:
- Перейдите в каталог
<sdk>/tools/
. - Выполните
android update project
с параметром--library
, чтобы добавить в проект как LVL, так и библиотеку загрузчика. Например:android update project --path ~/Android/MyApp \ --library ~/android_sdk/extras/google/market_licensing \ --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
Добавив в свое приложение библиотеку проверки лицензии и библиотеку загрузчика, вы сможете быстро интегрировать возможность загрузки файлов расширения из Google Play. Формат, который вы выбираете для файлов расширения, и способ их чтения из общего хранилища — это отдельная реализация, которую вам следует учитывать в зависимости от потребностей вашего приложения.
Совет: Пакет расширения Apk включает пример приложения, в котором показано, как использовать библиотеку загрузчика в приложении. В образце используется третья библиотека, доступная в пакете расширения Apk, которая называется Zip-библиотека расширения APK. Если вы планируете использовать ZIP-файлы для файлов расширений, мы предлагаем вам также добавить ZIP-библиотеку расширений APK в ваше приложение. Для получения дополнительной информации см. раздел ниже об использовании ZIP-библиотеки расширения APK .
Объявление разрешений пользователя
Чтобы загрузить файлы расширения, библиотеке загрузчика требуется несколько разрешений, которые вы должны объявить в файле манифеста вашего приложения. Они есть:
<manifest ...> <!-- Required to access Google Play Licensing --> <uses-permission android:name="com.android.vending.CHECK_LICENSE" /> <!-- Required to download files from Google Play --> <uses-permission android:name="android.permission.INTERNET" /> <!-- Required to keep CPU alive while downloading files (NOT to keep screen awake) --> <uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- Required to poll the state of the network connection and respond to changes --> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <!-- Required to check whether Wi-Fi is enabled --> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> <!-- Required to read and write the expansion files on shared storage --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
Примечание. По умолчанию для библиотеки загрузчика требуется уровень API 4, но для Zip-библиотеки расширения APK требуется уровень API 5.
Реализация службы загрузчика
Для выполнения загрузок в фоновом режиме библиотека Downloader предоставляет собственный подкласс Service
под названием DownloaderService
, который вам следует расширить. Помимо загрузки файлов расширения, DownloaderService
также:
- Регистрирует
BroadcastReceiver
, который прослушивает изменения в сетевом подключении устройства (рассылкаCONNECTIVITY_ACTION
), чтобы при необходимости приостановить загрузку (например, из-за потери подключения) и возобновить загрузку, когда это возможно (соединение установлено). - Планирует сигнал тревоги
RTC_WAKEUP
для повторной попытки загрузки в случаях, когда служба отключается. - Создает пользовательское
Notification
, отображающее ход загрузки и любые ошибки или изменения состояния. - Позволяет вашему приложению вручную приостанавливать и возобновлять загрузку.
- Перед загрузкой файлов расширения проверяется, что общее хранилище подключено и доступно, что файлы еще не существуют и достаточно места. Затем уведомляет пользователя, если что-либо из этого не соответствует действительности.
Все, что вам нужно сделать, — это создать в приложении класс, расширяющий класс DownloaderService
, и переопределить три метода для предоставления конкретных сведений о приложении:
-
getPublicKey()
- Это должно возвращать строку, которая представляет собой открытый ключ RSA в кодировке Base64 для вашей учетной записи издателя, доступный на странице профиля в Play Console (см. Настройка лицензирования ).
-
getSALT()
- Это должно возвращать массив случайных байтов, который
Policy
лицензирования использует для созданияObfuscator
. Соль гарантирует, что ваш запутанный файлSharedPreferences
, в котором сохраняются ваши данные о лицензировании, будет уникальным и недоступным для обнаружения. -
getAlarmReceiverClassName()
- Это должно возвращать имя класса
BroadcastReceiver
в вашем приложении, которое должно получить сигнал тревоги, указывающий на необходимость перезапуска загрузки (что может произойти, если служба загрузчика неожиданно остановится).
Например, вот полная реализация DownloaderService
:
Котлин
// You must use the public key belonging to your publisher account const val BASE64_PUBLIC_KEY = "YourLVLKey" // You should also modify this salt val SALT = byteArrayOf( 1, 42, -12, -1, 54, 98, -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84 ) class SampleDownloaderService : DownloaderService() { override fun getPublicKey(): String = BASE64_PUBLIC_KEY override fun getSALT(): ByteArray = SALT override fun getAlarmReceiverClassName(): String = SampleAlarmReceiver::class.java.name }
Ява
public class SampleDownloaderService extends DownloaderService { // You must use the public key belonging to your publisher account public static final String BASE64_PUBLIC_KEY = "YourLVLKey"; // You should also modify this salt public static final byte[] SALT = new byte[] { 1, 42, -12, -1, 54, 98, -100, -12, 43, 2, -8, -4, 9, 5, -106, -107, -33, 45, -1, 84 }; @Override public String getPublicKey() { return BASE64_PUBLIC_KEY; } @Override public byte[] getSALT() { return SALT; } @Override public String getAlarmReceiverClassName() { return SampleAlarmReceiver.class.getName(); } }
Примечание. Вам необходимо обновить значение BASE64_PUBLIC_KEY
чтобы оно стало открытым ключом, принадлежащим вашей учетной записи издателя. Вы можете найти ключ в консоли разработчика под информацией вашего профиля. Это необходимо даже при тестировании ваших загрузок.
Не забудьте объявить службу в файле манифеста:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
Реализация приемника сигналов тревоги
Чтобы отслеживать ход загрузки файлов и при необходимости перезапускать загрузку, DownloaderService
планирует сигнал тревоги RTC_WAKEUP
, который передает Intent
BroadcastReceiver
в вашем приложении. Вы должны определить BroadcastReceiver
для вызова API из библиотеки загрузчика, который проверяет состояние загрузки и при необходимости перезапускает ее.
Вам просто нужно переопределить метод onReceive()
для вызова DownloaderClientMarshaller.startDownloadServiceIfRequired()
.
Например:
Котлин
class SampleAlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { try { DownloaderClientMarshaller.startDownloadServiceIfRequired( context, intent, SampleDownloaderService::class.java ) } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() } } }
Ява
public class SampleAlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { try { DownloaderClientMarshaller.startDownloadServiceIfRequired(context, intent, SampleDownloaderService.class); } catch (NameNotFoundException e) { e.printStackTrace(); } } }
Обратите внимание, что это класс, для которого вы должны вернуть имя в методе getAlarmReceiverClassName()
вашего сервиса (см. предыдущий раздел).
Не забудьте объявить получателя в файле манифеста:
<app ...> <receiver android:name=".SampleAlarmReceiver" /> ... </app>
Начинаем загрузку
Основное действие в вашем приложении (запускаемое значком запуска) отвечает за проверку наличия файлов расширения на устройстве и инициацию загрузки, если это не так.
Для запуска загрузки с помощью библиотеки Downloader необходимо выполнить следующие процедуры:
- Проверьте, загрузились ли файлы.
Библиотека загрузчика включает в себя несколько API-интерфейсов класса
Helper
, которые помогут в этом процессе:-
getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
-
doesFileExist(Context c, String fileName, long fileSize)
Например, пример приложения, представленный в пакете расширения Apk, вызывает следующий метод в методе
onCreate()
действия, чтобы проверить, существуют ли уже файлы расширения на устройстве:Котлин
fun expansionFilesDelivered(): Boolean { xAPKS.forEach { xf -> Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion).also { fileName -> if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false)) return false } } return true }
Ява
boolean expansionFilesDelivered() { for (XAPKFile xf : xAPKS) { String fileName = Helpers.getExpansionAPKFileName(this, xf.isBase, xf.fileVersion); if (!Helpers.doesFileExist(this, fileName, xf.fileSize, false)) return false; } return true; }
В этом случае каждый объект
XAPKFile
содержит номер версии и размер файла известного файла расширения, а также логическое значение того, является ли это основным файлом расширения. (Подробную информацию см. в классеSampleDownloaderActivity
примера приложения.)Если этот метод возвращает false, приложение должно начать загрузку.
-
- Запустите загрузку, вызвав статический метод
DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass)
.Метод принимает следующие параметры:
-
context
:Context
вашего приложения. -
notificationClient
:PendingIntent
для начала вашего основного действия. Это используется вNotification
, которое создаетDownloaderService
, чтобы показать ход загрузки. Когда пользователь выбирает уведомление, система вызываетPendingIntent
который вы указали здесь, и должна открыть действие, которое показывает ход загрузки (обычно то же самое действие, которое запустило загрузку). -
serviceClass
: объектClass
для вашей реализацииDownloaderService
, необходимый для запуска службы и начала загрузки, если необходимо.
Метод возвращает целое число, указывающее, требуется ли загрузка. Возможные значения:
-
NO_DOWNLOAD_REQUIRED
: возвращается, если файлы уже существуют или загрузка уже выполняется. -
LVL_CHECK_REQUIRED
: возвращается, если для получения URL-адресов файлов расширения требуется проверка лицензии. -
DOWNLOAD_REQUIRED
: возвращается, если URL-адреса файлов расширения уже известны, но не были загружены.
Поведение
LVL_CHECK_REQUIRED
иDOWNLOAD_REQUIRED
по существу одинаково, и обычно вам не нужно о них беспокоиться. В вашем основном действии, которое вызываетstartDownloadServiceIfRequired()
, вы можете просто проверить, является ли ответNO_DOWNLOAD_REQUIRED
. Если ответ отличается отNO_DOWNLOAD_REQUIRED
, библиотека загрузчика начинает загрузку, и вам следует обновить пользовательский интерфейс активности, чтобы отобразить ход загрузки (см. следующий шаг). Если ответ NO_DOWNLOAD_REQUIREDNO_DOWNLOAD_REQUIRED
файлы доступны и ваше приложение может запуститься.Например:
Котлин
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Check if expansion files are available before going any further if (!expansionFilesDelivered()) { val pendingIntent = // Build an Intent to start this activity from the Notification Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP }.let { notifierIntent -> PendingIntent.getActivity( this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT ) } // Start the download service (if required) val startResult: Int = DownloaderClientMarshaller.startDownloadServiceIfRequired( this, pendingIntent, SampleDownloaderService::class.java ) // If download has started, initialize this activity to show // download progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // This is where you do set up to display the download // progress (next step) ... return } // If the download wasn't necessary, fall through to start the app } startApp() // Expansion files are available, start the app }
Ява
@Override public void onCreate(Bundle savedInstanceState) { // Check if expansion files are available before going any further if (!expansionFilesDelivered()) { // Build an Intent to start this activity from the Notification Intent notifierIntent = new Intent(this, MainActivity.getClass()); notifierIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); ... PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notifierIntent, PendingIntent.FLAG_UPDATE_CURRENT); // Start the download service (if required) int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); // If download has started, initialize this activity to show // download progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // This is where you do set up to display the download // progress (next step) ... return; } // If the download wasn't necessary, fall through to start the app } startApp(); // Expansion files are available, start the app }
-
- Когда метод
startDownloadServiceIfRequired()
возвращает что-либо , кромеNO_DOWNLOAD_REQUIRED
, создайте экземплярIStub
, вызвавDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService)
.IStub
обеспечивает привязку вашей активности к службе загрузчика, так что ваша активность получает обратные вызовы о ходе загрузки.Чтобы создать
IStub
с помощью вызоваCreateStub()
, вы должны передать ему реализацию интерфейсаIDownloaderClient
и вашу реализациюDownloaderService
. В следующем разделе « Получение прогресса загрузки» обсуждается интерфейсIDownloaderClient
, который обычно следует реализовать в классеActivity
, чтобы можно было обновлять пользовательский интерфейс действия при изменении состояния загрузки.Мы рекомендуем вам вызвать
CreateStub()
для создания экземпляра вашегоIStub
во время методаonCreate()
вашего действия, после того какstartDownloadServiceIfRequired()
начнет загрузку.Например, в предыдущем примере кода для
onCreate()
вы можете ответить на результатstartDownloadServiceIfRequired()
следующим образом:Котлин
// Start the download service (if required) val startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired( this@MainActivity, pendingIntent, SampleDownloaderService::class.java ) // If download has started, initialize activity to show progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // Instantiate a member instance of IStub downloaderClientStub = DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService::class.java) // Inflate layout that shows download progress setContentView(R.layout.downloader_ui) return }
Ява
// Start the download service (if required) int startResult = DownloaderClientMarshaller.startDownloadServiceIfRequired(this, pendingIntent, SampleDownloaderService.class); // If download has started, initialize activity to show progress if (startResult != DownloaderClientMarshaller.NO_DOWNLOAD_REQUIRED) { // Instantiate a member instance of IStub downloaderClientStub = DownloaderClientMarshaller.CreateStub(this, SampleDownloaderService.class); // Inflate layout that shows download progress setContentView(R.layout.downloader_ui); return; }
После возврата метода
onCreate()
ваша активность получает вызовonResume()
, где вы затем должны вызватьconnect()
вIStub
, передав емуContext
вашего приложения. И наоборот, вам следует вызвать методdisconnect()
в обратном вызовеonStop()
вашей активности.Котлин
override fun onResume() { downloaderClientStub?.connect(this) super.onResume() } override fun onStop() { downloaderClientStub?.disconnect(this) super.onStop() }
Ява
@Override protected void onResume() { if (null != downloaderClientStub) { downloaderClientStub.connect(this); } super.onResume(); } @Override protected void onStop() { if (null != downloaderClientStub) { downloaderClientStub.disconnect(this); } super.onStop(); }
Вызов
connect()
вIStub
привязывает вашу активность кDownloaderService
, так что ваша активность получает обратные вызовы относительно изменений состояния загрузки через интерфейсIDownloaderClient
.
Получение прогресса загрузки
Чтобы получать обновления о ходе загрузки и взаимодействовать с DownloaderService
, необходимо реализовать интерфейс IDownloaderClient
библиотеки Downloader. Обычно действие, которое вы используете для запуска загрузки, должно реализовывать этот интерфейс, чтобы отображать ход загрузки и отправлять запросы в службу.
Необходимые методы интерфейса для IDownloaderClient
:
-
onServiceConnected(Messenger m)
- После того как вы создадите экземпляр
IStub
в своей деятельности, вы получите вызов этого метода, который передает объектMessenger
, связанный с вашим экземпляромDownloaderService
. Чтобы отправить запросы службе, например приостановить и возобновить загрузку, необходимо вызватьDownloaderServiceMarshaller.CreateProxy()
, чтобы получить интерфейсIDownloaderService
подключенный к службе.Рекомендуемая реализация выглядит следующим образом:
Котлин
private var remoteService: IDownloaderService? = null ... override fun onServiceConnected(m: Messenger) { remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { downloaderClientStub?.messenger?.also { messenger -> onClientUpdated(messenger) } } }
Ява
private IDownloaderService remoteService; ... @Override public void onServiceConnected(Messenger m) { remoteService = DownloaderServiceMarshaller.CreateProxy(m); remoteService.onClientUpdated(downloaderClientStub.getMessenger()); }
После инициализации объекта
IDownloaderService
вы можете отправлять команды службе загрузчика, например приостанавливать и возобновлять загрузку (requestPauseDownload()
иrequestContinueDownload()
). -
onDownloadStateChanged(int newState)
- Служба загрузки вызывает это, когда происходит изменение состояния загрузки, например, когда загрузка начинается или завершается.
Значением
newState
будет одно из нескольких возможных значений, указанных в одной из константSTATE_*
классаIDownloaderClient
.Чтобы предоставить пользователям полезное сообщение, вы можете запросить соответствующую строку для каждого состояния, вызвав
Helpers.getDownloaderStringResourceIDFromState()
. Это возвращает идентификатор ресурса для одной из строк, включенных в библиотеку загрузчика. Например, строка «Загрузка приостановлена, поскольку вы находитесь в роуминге» соответствуетSTATE_PAUSED_ROAMING
. -
onDownloadProgress(DownloadProgressInfo progress)
- Служба загрузки вызывает это для доставки объекта
DownloadProgressInfo
, который описывает различную информацию о ходе загрузки, включая расчетное оставшееся время, текущую скорость, общий прогресс и общее количество, чтобы вы могли обновить пользовательский интерфейс процесса загрузки.
Совет: примеры этих обратных вызовов, которые обновляют пользовательский интерфейс процесса загрузки, см. в разделе SampleDownloaderActivity
в образце приложения, поставляемом с пакетом расширения Apk.
Некоторые общедоступные методы интерфейса IDownloaderService
, которые могут оказаться вам полезными:
-
requestPauseDownload()
- Приостанавливает загрузку.
-
requestContinueDownload()
- Возобновляет приостановленную загрузку.
-
setDownloadFlags(int flags)
- Устанавливает пользовательские предпочтения для типов сетей, в которых можно загружать файлы. Текущая реализация поддерживает один флаг
FLAGS_DOWNLOAD_OVER_CELLULAR
, но вы можете добавить и другие. По умолчанию этот флаг не включен, поэтому для загрузки файлов расширения пользователю необходимо подключиться к Wi-Fi. Возможно, вы захотите указать предпочтения пользователя для включения загрузки через сотовую сеть. В этом случае вы можете позвонить:Котлин
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
Ява
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
Используйте APKExpansionPolicy
Если вы решите создать собственную службу загрузчика вместо использования библиотеки загрузчиков Google Play, вам все равно следует использовать APKExpansionPolicy
, предоставленный в библиотеке проверки лицензий. Класс APKExpansionPolicy
почти идентичен классу ServerManagedPolicy
(доступен в библиотеке проверки лицензий Google Play), но включает дополнительную обработку дополнительных ответов файла расширения APK.
Примечание. Если вы используете библиотеку загрузчиков , как обсуждалось в предыдущем разделе, библиотека выполняет все взаимодействие с APKExpansionPolicy
, поэтому вам не нужно использовать этот класс напрямую.
Класс включает методы, которые помогут вам получить необходимую информацию о доступных файлах расширения:
-
getExpansionURLCount()
-
getExpansionURL(int index)
-
getExpansionFileName(int index)
-
getExpansionFileSize(int index)
Для получения дополнительной информации о том, как использовать APKExpansionPolicy
когда вы не используете библиотеку загрузчиков , см. Документацию для добавления лицензирования в ваше приложение , в котором объясняется, как реализовать такую политику лицензии.
Чтение файла расширения
После того, как ваши файлы расширения APK сохраняются на устройстве, то, как вы читаете файлы, зависит от типа используемого вами файла. Как обсуждалось в обзоре , ваши файлы расширения могут быть любым видом файла, который вы хотите, но переименованы в отдельный формат имени файла и сохраняются в <shared-storage>/Android/obb/<package-name>/
.
Независимо от того, как вы читаете свои файлы, вы всегда должны сначала проверить, что внешнее хранилище доступно для чтения. Есть вероятность, что пользователь имеет хранилище, установленное на компьютере над USB или фактически удалил SD -карту.
ПРИМЕЧАНИЕ. Когда ваше приложение запускается, вы всегда должны проверять, доступно ли пространство для внешнего хранилища и читается, вызывая getExternalStorageState()
. Это возвращает одну из нескольких возможных строк, которые представляют состояние внешнего хранилища. Для того, чтобы он был читаемым вашим приложением, возвращаемое значение должно быть MEDIA_MOUNTED
.
Получение имен файлов
Как описано в обзоре , ваши файлы расширения APK сохраняются с использованием конкретного формата имени файла:
[main|patch].<expansion-version>.<package-name>.obb
Чтобы получить местоположение и имена ваших файлов расширения, вы должны использовать методы getExternalStorageDirectory()
и getPackageName()
для построения пути к вашим файлам.
Вот метод, который вы можете использовать в своем приложении, чтобы получить массив, содержащий полный путь к обоим вашим файлам расширения:
Котлин
fun getAPKExpansionFiles(ctx: Context, mainVersion: Int, patchVersion: Int): Array<String> { val packageName = ctx.packageName val ret = mutableListOf<String>() if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) { // Build the full path to the app's expansion files val root = Environment.getExternalStorageDirectory() val expPath = File(root.toString() + EXP_PATH + packageName) // Check that expansion file path exists if (expPath.exists()) { if (mainVersion > 0) { val strMainPath = "$expPath${File.separator}main.$mainVersion.$packageName.obb" val main = File(strMainPath) if (main.isFile) { ret += strMainPath } } if (patchVersion > 0) { val strPatchPath = "$expPath${File.separator}patch.$mainVersion.$packageName.obb" val main = File(strPatchPath) if (main.isFile) { ret += strPatchPath } } } } return ret.toTypedArray() }
Ява
// The shared path to all app expansion files private final static String EXP_PATH = "/Android/obb/"; static String[] getAPKExpansionFiles(Context ctx, int mainVersion, int patchVersion) { String packageName = ctx.getPackageName(); Vector<String> ret = new Vector<String>(); if (Environment.getExternalStorageState() .equals(Environment.MEDIA_MOUNTED)) { // Build the full path to the app's expansion files File root = Environment.getExternalStorageDirectory(); File expPath = new File(root.toString() + EXP_PATH + packageName); // Check that expansion file path exists if (expPath.exists()) { if ( mainVersion > 0 ) { String strMainPath = expPath + File.separator + "main." + mainVersion + "." + packageName + ".obb"; File main = new File(strMainPath); if ( main.isFile() ) { ret.add(strMainPath); } } if ( patchVersion > 0 ) { String strPatchPath = expPath + File.separator + "patch." + mainVersion + "." + packageName + ".obb"; File main = new File(strPatchPath); if ( main.isFile() ) { ret.add(strPatchPath); } } } } String[] retArray = new String[ret.size()]; ret.toArray(retArray); return retArray; }
Вы можете вызвать этот метод, передав его Context
приложения и нужную версию файла расширения.
Есть много способов определить номер версии файла расширения. Одним из простых способов является сохранение версии в файле SharedPreferences
при начале загрузки, запрашивая имя файла расширения с помощью метода APKExpansionPolicy
класса getExpansionFileName(int index)
. Затем вы можете получить код версии, прочитав файл SharedPreferences
, когда вы хотите получить доступ к файлу расширения.
Для получения дополнительной информации о чтении из общего хранилища см. В документации хранения данных .
Использование библиотеки ZIP расширения APK
Пакет расширения APK на рынке Google включает в себя библиотеку APK Expansion Library (расположенная в <sdk>/extras/google/google_market_apk_expansion/zip_file/
). Это дополнительная библиотека, которая помогает вам прочитать ваши файлы расширения, когда они сохраняются в виде ZIP -файлов. Использование этой библиотеки позволяет легко читать ресурсы из ваших файлов расширения ZIP в качестве виртуальной файловой системы.
Библиотека ZIP расширения APK включает в себя следующие классы и API:
-
APKExpansionSupport
- Предоставляет некоторые методы для доступа к именам файлов расширения и файлов ZIP:
-
getAPKExpansionFiles()
- Тот же метод, указанный выше, который возвращает полный путь файла в оба файла расширения.
-
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
- Возвращает
ZipResourceFile
представляющий сумму как основного файла, так и файла Patch. То есть, если вы указываете какmainVersion
, так иpatchVersion
, это возвращаетZipResourceFile
, который обеспечивает доступ к чтению ко всем данным, при этом данные файла патча объединены поверх основного файла.
-
-
ZipResourceFile
- Представляет zip -файл на общем хранилище и выполняет всю работу, чтобы предоставить виртуальную файловую систему на основе ваших файлов zip. Вы можете получить экземпляр, используя
APKExpansionSupport.getAPKExpansionZipFile()
или сZipResourceFile
, пропустив его путь к вашему файлу расширения. Этот класс включает в себя множество полезных методов, но вам, как правило, вам не нужно получить доступ к большинству из них. Пара важных методов:-
getInputStream(String assetPath)
- Предоставляет
InputStream
для чтения файла в файле ZIP.assetPath
должен быть пути к желаемому файлу относительно корня содержания файла Zip. -
getAssetFileDescriptor(String assetPath)
- Предоставляет
AssetFileDescriptor
для файла в файле ZIP.assetPath
должен быть пути к желаемому файлу относительно корня содержания файла Zip. Это полезно для определенных API -интерфейсов Android, которые требуютAssetFileDescriptor
, например, некоторые API -интерфейсыMediaPlayer
.
-
-
APEZProvider
- Большинству приложений не нужно использовать этот класс. Этот класс определяет
ContentProvider
, который маршал данные из файлов ZIP черезUri
поставщика контента, чтобы предоставить доступ к файлам для определенных API Android, которые ожидают доступUri
к медиа -файлам. Например, это полезно, если вы хотите воспроизвести видео сVideoView.setVideoURI()
.
Пропуск сжатия молнии медиа -файлов
Если вы используете свои файлы расширения для хранения медиа -файлов, zip -файл по -прежнему позволяет использовать вызовы воспроизведения Android Media, которые обеспечивают элементы управления смещением и длиной (например, MediaPlayer.setDataSource()
и SoundPool.load()
). Чтобы это работало, вы не должны выполнять дополнительное сжатие в медиа -файлах при создании пакетов ZIP. Например, при использовании инструмента zip
вы должны использовать опцию -n
, чтобы указать суффиксы файла, которые не следует сжимать:
zip -n .mp4;.ogg main_expansion media_files
Чтение из zip -файла
При использовании библиотеки ZIP расширения APK, чтение файла из вашего ZIP обычно требует следующего:
Котлин
// Get a ZipResourceFile representing a merger of both the main and patch files val expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion) // Get an input stream for a known file inside the expansion file ZIPs expansionFile.getInputStream(pathToFileInsideZip).use { ... }
Ява
// Get a ZipResourceFile representing a merger of both the main and patch files ZipResourceFile expansionFile = APKExpansionSupport.getAPKExpansionZipFile(appContext, mainVersion, patchVersion); // Get an input stream for a known file inside the expansion file ZIPs InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
Приведенный выше код обеспечивает доступ к любому файлу, который существует либо в вашем основном файле расширения, либо в файле расширения исправления, считывая из объединенной карты всех файлов из обоих файлов. Все, что вам нужно для предоставления метода getAPKExpansionFile()
- это ваше приложение android.content.Context
и номер версии как для основного файла расширения, так и для файла расширения.
Если вы предпочитаете прочитать из определенного файла расширения, вы можете использовать конструктор ZipResourceFile
с путем к желаемому файлу расширения:
Котлин
// Get a ZipResourceFile representing a specific expansion file val expansionFile = ZipResourceFile(filePathToMyZip) // Get an input stream for a known file inside the expansion file ZIPs expansionFile.getInputStream(pathToFileInsideZip).use { ... }
Ява
// Get a ZipResourceFile representing a specific expansion file ZipResourceFile expansionFile = new ZipResourceFile(filePathToMyZip); // Get an input stream for a known file inside the expansion file ZIPs InputStream fileStream = expansionFile.getInputStream(pathToFileInsideZip);
Для получения дополнительной информации об использовании этой библиотеки для ваших файлов расширения, посмотрите на класс SampleDownloaderActivity
приложения приложения, который включает в себя дополнительный код для проверки загруженных файлов с использованием CRC. Остерегайтесь, что если вы используете этот пример в качестве основы для вашей собственной реализации, требуется, чтобы вы объявили байт размером с ваши файлы расширения в массиве xAPKS
.
Тестирование файлов расширения
Перед публикацией вашего приложения есть две вещи, которые вы должны проверить: чтение файлов расширения и загрузка файлов.
Файл тестирования считывает
Прежде чем загрузить свое приложение в Google Play, вы должны проверить способность вашего приложения читать файлы из общего хранилища. Все, что вам нужно сделать, это добавить файлы в соответствующее место в общем хранилище устройства и запустить ваше приложение:
- На вашем устройстве создайте соответствующий каталог на общем хранилище, где Google Play сохранит ваши файлы.
Например, если имя вашего пакета является
com.example.android
, вам необходимо создать каталогAndroid/obb/com.example.android/
в общем пространстве хранения. (Подключите ваше испытательное устройство к компьютеру, чтобы установить общее хранилище и вручную создать этот каталог.) - Вручную добавьте файлы расширения в этот каталог. Убедитесь, что вы переименуете свои файлы в соответствии с форматом имени файла , который будет использовать Google Play.
Например, независимо от типа файла, основной файл расширения для приложения
com.example.android
должен бытьmain.0300110.com.example.android.obb
. Код версии может быть любым значением, которое вы хотите. Просто помните:- Основной файл расширения всегда начинается с
main
, а патч -файл начинается сpatch
. - Имя пакета всегда соответствует имени APK, к которому файл прикреплен в Google Play.
- Основной файл расширения всегда начинается с
- Теперь, когда файлы расширения (ы) находятся на устройстве, вы можете установить и запустить свое приложение для проверки файлов расширения.
Вот некоторые напоминания об обращении с файлами расширения:
- Не удаляйте и не переименуйте файлы расширения
.obb
(даже если вы распакуете данные в другое место). Это приведет к тому, что Google Play (или ваше приложение) неоднократно загружает файл расширения. - Не сохраняйте другие данные в свой каталог
obb/
. Если вы должны распаковать некоторые данные, сохраните их в местоположение, указанноеgetExternalFilesDir()
.
Тестирование загрузки файлов
Поскольку ваше приложение иногда должно вручную загружать файлы расширения, когда оно впервые открывается, важно, чтобы вы проверили этот процесс, чтобы убедиться, что ваше приложение может успешно запросить URL -адреса, загружать файлы и сохранить их на устройство.
Чтобы проверить реализацию вашего приложения процедуры загрузки ручной работы, вы можете опубликовать его во внутренней тестовой дорожке, чтобы оно было доступно только для авторизованных тестеров. Если все работает, как и ожидалось, ваше приложение должно начать загружать файлы расширения, как только начинается основная деятельность.
Примечание. Ранее вы можете проверить приложение, загрузив неопубликованную версию «черновика». Эта функциональность больше не поддерживается. Вместо этого вы должны опубликовать его на внутреннем, закрытом или открытом треке тестирования. Для получения дополнительной информации см. Приложения, которые больше не поддерживаются .
Обновление вашего приложения
Одним из замечательных преимуществ использования файлов расширения в Google Play является возможность обновлять ваше приложение без повторной загрузки всех исходных активов. Поскольку Google Play позволяет вам предоставлять два файла расширения с каждым APK, вы можете использовать второй файл в качестве «патча», который предоставляет обновления и новые активы. Это избегает необходимости повторной загрузки основного файла расширения, который может быть большим и дорогим для пользователей.
Файл расширения патча технически такой же, как и основной файл расширения, и ни система Android, ни Google Play не выполняют фактическое исправление между вашим основным и патч -файлами. Код вашего приложения должен выполнять любые необходимые исправления.
Если вы используете zip -файлы в качестве файлов расширения, библиотека ZIP расширения APK , которая включена в пакет расширения APK, включает в себя возможность объединить ваш пататный файл с основным файлом расширения.
Примечание. Даже если вам нужно только внести изменения в файл расширения патча, вы все равно должны обновить APK, чтобы Google Play выполнил обновление. Если вам не требуются изменения кода в приложении, вам следует просто обновить versionCode
в манифесте.
Пока вы не измените основной файл расширения, связанный с APK в консоли PLAY, пользователи, которые ранее установили ваше приложение, не будут загружать основной файл расширения. Существующие пользователи получают только обновленный APK и новый файл расширения патчей (сохраняя предыдущий основной файл расширения).
Вот несколько вопросов, которые следует учитывать в отношении обновлений для файлов расширения:
- Для вашего приложения может быть только два файла расширения. Один основной файл расширения и один файл расширения патча. Во время обновления в файл Google Play удаляет предыдущую версию (и, так и ваше приложение, при выполнении ручных обновлений).
- При добавлении файла расширения патча система Android на самом деле не исправляет ваше приложение или основной файл расширения. Вы должны спроектировать свое приложение для поддержки данных. Тем не менее, пакет расширения APK включает в себя библиотеку для использования файлов ZIP в качестве файлов расширения, которые объединяют данные из файла патча в основной файл расширения, чтобы вы могли легко прочитать все данные файла расширения.