O Google Play requer que o APK compactado que os usuários transferem por download não ultrapasse 100 MB. Para a maioria dos aplicativos, isso é mais do que suficiente para todo o código e os recursos. No entanto, alguns aplicativos precisam de mais espaço para gráficos de alta fidelidade, arquivos de mídia ou outros recursos grandes. Anteriormente, se o tamanho do download compactado do aplicativo ultrapassasse 100 MB, era necessário hospedar e fazer o download dos recursos adicionais quando o usuário abrisse o aplicativo. Hospedar e exibir os arquivos extras pode ser caro e a experiência do usuário costuma ser inferior à ideal. Para tornar esse processo mais fácil para você e mais agradável para os usuários, o Google Play permite anexar dois arquivos de expansão grandes que complementam o APK.
O Google Play hospeda os arquivos de expansão do aplicativo, os exibe no dispositivo e você não paga nada por isso. Os arquivos de expansão são salvos no local de armazenamento compartilhado do dispositivo (o cartão SD ou partição montada em USB, também conhecidos como armazenamento externo), em que o aplicativo pode acessá-los. Na maioria dos dispositivos, o Google Play faz o download dos arquivos de expansão junto com o download do APK. Assim, o app terá tudo que precisar quando o usuário o abrir pela primeira vez. Em alguns casos, no entanto, o aplicativo precisa fazer o download dos arquivos no Google Play quando é iniciado.
Se você quiser evitar o uso de arquivos de expansão e o tamanho do download compactado do aplicativo for maior que 100 MB, faça upload do aplicativo usando o Android App Bundle, que permite até 200 MB de tamanho de download compactado. Além disso, como pacotes de apps permitem gerar APKs e assinar com o Google Play, os usuários fazem o download de APKs otimizados apenas com o código e os recursos necessários para executar o aplicativo. Você não precisa criar, assinar nem gerenciar vários APKs ou arquivos de expansão, e os usuários terão downloads menores e mais otimizados.
Visão geral
Sempre que você fizer upload de um APK usando o Google Play Console, terá a opção de adicionar um ou dois arquivos de expansão ao APK. Cada arquivo pode ter até 2 GB e pode ser de qualquer formato, mas recomendamos que você use um arquivo compactado para economizar largura de banda durante o download. Conceitualmente, cada arquivo de expansão desempenha um papel diferente:
- O arquivo de expansão principal é o arquivo de expansão primário dos recursos adicionais exigidos pelo aplicativo.
- O arquivo de expansão patch é opcional e está destinado a pequenas atualizações do arquivo de expansão principal.
Embora seja possível usar os dois arquivos de expansão da maneira que quiser, recomendamos que o arquivo de expansão principal forneça os recursos principais e seja atualizado raramente (se for). O arquivo de expansão "patch" precisa ser menor e servir como uma “operadora de patch”, sendo atualizado com cada versão principal ou conforme necessário.
No entanto, mesmo que a atualização do app exija apenas um novo arquivo de expansão de patch, você ainda precisa fazer upload de um novo APK com o versionCode
atualizado no manifesto. O Play Console não permite o upload de um arquivo de expansão para um APK existente.
Observação: o arquivo de expansão de patch é semanticamente igual ao arquivo de expansão principal. Você pode usar cada arquivo da maneira que quiser.
Formato do nome do arquivo
Todos os arquivos de expansão enviados podem ser de qualquer formato (ZIP, PDF, MP4 etc.). Também é possível usar a ferramenta JOBB para encapsular e criptografar um conjunto de arquivos de recursos e patches subsequentes para esse conjunto. Independentemente do tipo de arquivo, o Google Play os considera blobs binários opacos e renomeia os arquivos usando o seguinte esquema:
[main|patch].<expansion-version>.<package-name>.obb
Há três componentes neste esquema:
main
oupatch
- Especifica se o arquivo de expansão é principal ou de patch. Pode haver somente um arquivo principal e um de patch para cada APK.
<expansion-version>
- Este é um número inteiro que corresponde ao código da versão do APK com que a expansão é associada primeiro (corresponde ao valor
android:versionCode
do app)."Primeiro" é enfatizado porque, embora o Play Console permita que você reutilize um arquivo de expansão com um novo APK, o nome do arquivo não é modificado. Ele retém a versão aplicada durante o primeiro upload.
<package-name>
- Nome do pacote no estilo Java do aplicativo.
Por exemplo, suponha que a versão do APK seja 314159 e o nome do pacote seja com.example.app. Se você fizer o upload de um arquivo de expansão principal, o arquivo será renomeado para:
main.314159.com.example.app.obb
Local de armazenamento
Quando o Google Play faz o download dos arquivos de expansão para um dispositivo, eles são salvos no local de armazenamento compartilhado do sistema. Para garantir o comportamento adequado, não exclua, mova nem renomeie os arquivos de expansão. Caso seu aplicativo precise realizar o download no próprio Google Play, salve os arquivos no mesmo local.
O método getObbDir()
retorna o local específico dos arquivos de expansão no seguinte formato:
<shared-storage>/Android/obb/<package-name>/
<shared-storage>
é o caminho para o espaço de armazenamento compartilhado, disponível emgetExternalStorageDirectory()
.<package-name>
é o nome do pacote no estilo Java do aplicativo, disponível emgetPackageName()
.
Nunca há mais de dois arquivos de expansão para cada app nesse diretório.
Um é o arquivo de expansão principal e o outro é o arquivo de expansão do patch (se necessário). As versões anteriores são substituídas quando você atualiza o aplicativo com novos arquivos de expansão. A partir do Android 4.4 (API de nível 19), os aplicativos podem ler arquivos de expansão OBB
sem permissão de armazenamento externo. No entanto, algumas implementações do Android 6.0 (API de nível 23) e versões posteriores ainda exigem permissão. Portanto, declare a permissão READ_EXTERNAL_STORAGE
no manifesto do aplicativo e solicite permissão no momento da execução da seguinte maneira:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Para o Android 6 e posteriores, a permissão de armazenamento externo precisa ser solicitada no momento da execução. No entanto, algumas implementações do Android não precisam de permissão para ler arquivos OBB. O snippet de código a seguir mostra como verificar o acesso de leitura antes de solicitar a permissão de armazenamento externo:
Kotlin
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() }
Java
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(); }
Se você precisar descompactar o conteúdo dos arquivos de expansão, não exclua os
arquivos de expansão OBB
e não salve os dados descompactados
no mesmo diretório. Salve os arquivos descompactados no diretório especificado por getExternalFilesDir()
. No entanto,
se possível, é melhor usar um formato de arquivo de expansão que permita a leitura direta do
arquivo em vez de exigir a descompactação dos dados. Por exemplo, fornecemos um projeto de biblioteca chamado Biblioteca APK Expansion Zip que lê seus dados diretamente no arquivo ZIP.
Atenção: diferentemente dos arquivos APK, qualquer arquivo salvo no armazenamento compartilhado pode ser lido pelo usuário e por outros aplicativos.
Dica: se você empacotar arquivos de mídia em um arquivo ZIP, poderá usar chamadas de reprodução de mídia nos arquivos com controles de compensação e comprimento (como MediaPlayer.setDataSource()
e SoundPool.load()
) sem precisar descompactar o ZIP. Para que isso funcione, não realize compactação adicional nos arquivos de mídia ao criar pacotes ZIP. Por exemplo, ao usar a ferramenta zip
, use a opção -n
para especificar os sufixos de arquivo que não podem ser compactados:
zip -n .mp4;.ogg main_expansion media_files
Processo de download
Na maioria das vezes, o Google Play faz o download e salva os arquivos de expansão ao mesmo tempo em que faz o download do APK no dispositivo. No entanto, em alguns casos, o Google Play não pode fazer o download dos arquivos de expansão ou o usuário pode ter excluído os arquivos de expansão salvos anteriormente. Para gerenciar essas situações, o aplicativo precisa fazer o download dos arquivos quando a atividade principal for iniciada, usando um URL fornecido pelo Google Play.
O processo de download de um nível superior funciona assim:
- O usuário escolhe instalar seu aplicativo no Google Play.
- Se o Google Play conseguir fazer o download dos arquivos de expansão, que é o caso da maioria dos dispositivos, o download será feito junto com o APK.
Se o Google Play não conseguir fazer o download dos arquivos de expansão, ele só fará o download do APK.
- Quando o usuário inicia seu aplicativo, o app precisa verificar se os arquivos de expansão já estão salvos no dispositivo.
- Em caso afirmativo, seu app estará pronto para ser usado.
- Em caso negativo, seu aplicativo precisará fazer o download dos arquivos de expansão via HTTP no Google Play. Seu aplicativo precisa enviar uma solicitação ao cliente do Google Play usando o serviço de licenciamento do app do Google Play, que responde com o nome, o tamanho e o URL de cada arquivo de expansão. Com essas informações, você faz o download dos arquivos e os salva no local de armazenamento.
Cuidado: é fundamental que você inclua o código necessário para fazer o download dos arquivos de expansão no Google Play caso os arquivos não estejam no dispositivo quando seu aplicativo for iniciado. Como discutido na seção a seguir sobre Fazer o download dos arquivos de expansão, disponibilizamos uma biblioteca que simplifica bastante esse processo e faz o download de um serviço com uma quantidade mínima de código.
Lista de verificação de desenvolvimento
Veja um resumo das tarefas a serem executadas para usar arquivos de expansão com seu aplicativo:
- Primeiro, determine se o tamanho do download compactado do aplicativo precisa ter mais de 100 MB. O espaço é precioso e é necessário manter o tamanho de download total o menor possível. Caso seu aplicativo use mais de 100 MB para fornecer diversas versões dos recursos gráficos para várias densidades de tela, considere publicar vários APKs, em que cada APK contenha apenas os recursos necessários para as telas a que se destina. Para ter os melhores resultados ao publicar no Google Play, faça upload de um Android App Bundle que inclua todos os recursos e código compilados do app, mas transfira a geração e assinatura do APK para o Google Play.
- Determine os recursos de aplicativos que precisam ser separados do APK e agrupe-os em um arquivo para usá-los como o arquivo de expansão principal.
Normalmente, você precisa usar somente o segundo arquivo de expansão de patch ao atualizar o arquivo de expansão principal. No entanto, caso seus recursos excedam o limite de 2 GB para o arquivo de expansão principal, você pode usar o arquivo de patch para o restante dos recursos.
- Desenvolva seu aplicativo para que ele use os recursos dos arquivos de expansão no
local de armazenamento compartilhado do dispositivo.
Lembre-se de que é importante evitar excluir, mover ou renomear os arquivos de expansão.
Caso seu aplicativo não exija um formato específico, sugerimos que você crie arquivos ZIP para os arquivos de expansão e leia-os usando a Biblioteca APK Expansion Zip.
- Adicione lógica à atividade principal do aplicativo para verificar se os arquivos de expansão estão no dispositivo durante a inicialização. Se os arquivos não estiverem no dispositivo, use o serviço de licenciamento do aplicativo do Google Play para solicitar URLs
para os arquivos de expansão, faça o download deles e salve-os.
Para reduzir significativamente a quantidade de código que precisa ser escrita e garantir uma boa experiência do usuário durante o download, recomendamos que você use a Biblioteca Downloader para implementar seu comportamento de download.
Se você criar o próprio serviço de download em vez de usar a biblioteca, lembre-se de não mudar o nome dos arquivos de expansão e salvá-los no local de armazenamento correto.
Depois de concluir o desenvolvimento do aplicativo, siga o guia Testar os arquivos de expansão.
Regras e limitações
A adição de arquivos de expansão do APK é um recurso disponível quando você faz upload do app usando o Play Console. Ao fazer upload do aplicativo pela primeira vez ou atualizar um aplicativo que usa arquivos de expansão, você precisa estar ciente das seguintes regras e limitações:
- Os arquivos de expansão não podem ter mais de 2 GB.
- Para fazer o download dos arquivos de expansão no Google Play, o usuário precisa ter adquirido seu aplicativo no Google Play. O Google Play não fornecerá os URLs dos arquivos de expansão se o aplicativo tiver sido instalado por outros meios.
- Ao fazer o download dentro do seu aplicativo, o URL que o Google Play fornece para cada arquivo é exclusivo para cada download e expira pouco tempo depois de ser fornecido ao aplicativo.
- Se você atualizar seu aplicativo com um novo APK ou fizer upload de vários APKs para o mesmo aplicativo, poderá selecionar os arquivos de expansão que enviou para um APK anterior. O nome do arquivo de expansão não muda. Ele mantém a versão recebida pelo APK a que o arquivo foi originalmente associado.
- Se você usar arquivos de expansão combinados com vários APKs para
fornecer diferentes arquivos de expansão a diferentes dispositivos, ainda será necessário fazer upload de APKs diferentes
a cada dispositivo para oferecer um valor
versionCode
único e declarar filtros diferentes para cada APK. - Não é possível emitir uma atualização para o app modificando somente os arquivos
de expansão. Faça upload de um novo APK para atualizar seu app. Se as mudanças dizem respeito apenas
aos recursos nos seus arquivos de expansão, você pode atualizar seu APK simplesmente modificando
versionCode
(e talvez tambémversionName
). - Não salve outros dados no diretório
obb/
. Se precisar descompactar dados, salve-os no local especificado porgetExternalFilesDir()
. - Não exclua nem renomeie o arquivo de expansão
.obb
(a menos que esteja fazendo uma atualização). Isso fará com que o Google Play (ou o próprio aplicativo) faça o download repetidamente do arquivo de expansão. - Ao atualizar um arquivo de expansão manualmente, exclua o arquivo de expansão anterior.
Fazer o download dos arquivos de expansão
Na maioria dos casos, o Google Play faz o download e salva os arquivos de expansão no dispositivo enquanto instala ou atualiza o APK. Dessa forma, os arquivos de expansão estarão disponíveis quando seu aplicativo for iniciado pela primeira vez. No entanto, em alguns casos, o próprio aplicativo precisa fazer o download dos arquivos de expansão solicitando-os de um URL fornecido em uma resposta do serviço de licenciamento do aplicativo do Google Play.
A lógica básica de que você precisa para fazer o download dos arquivos de expansão é a seguinte:
- Quando seu aplicativo for iniciado, procure os arquivos de expansão no local de armazenamento compartilhado (no diretório
Android/obb/<package-name>/
).- Se os arquivos de expansão estiverem lá, seu aplicativo poderá continuar.
- Se os arquivos de expansão não estiverem lá:
- Faça uma solicitação usando o licenciamento de aplicativos do Google Play para ver os nomes, tamanhos e URLs dos arquivos de expansão deles.
- Use os URLs fornecidos pelo Google Play para fazer o download dos arquivos de expansão e salvá-los. Você precisa salvar os arquivos no local de armazenamento compartilhado (
Android/obb/<package-name>/
) e usar o nome de arquivo exato fornecido pela resposta do Google Play.Observação: o URL fornecido pelo Google Play para os arquivos de expansão é exclusiva para cada download, e cada um expira pouco tempo depois de ser fornecido ao aplicativo.
Se o aplicativo for gratuito (não for pago), você provavelmente não usou o serviço de licenciamento do aplicativo. Ele é projetado principalmente para aplicar políticas de licenciamento ao seu aplicativo e garantir que o usuário tenha o direito de usá-lo (a pessoa pagou por ele no Google Play). Para facilitar a funcionalidade do arquivo de expansão, o serviço de licenciamento foi aprimorado para oferecer uma resposta ao seu aplicativo que inclui o URL dos arquivos de expansão do aplicativo hospedados no Google Play. Assim, mesmo que o app seja gratuito para os usuários, será necessário incluir a Biblioteca License Verification (LVL, na sigla em inglês) para usar os arquivos de expansão de APK. Obviamente, caso o app seja gratuito, você não precisará aplicar a verificação de licença. A biblioteca só será necessária para fazer a solicitação que retorna o URL dos arquivos de expansão.
Observação: independente de o app ser gratuito ou não, o Google Play vai retornar os URLs dos arquivos de expansão somente se o usuário tiver adquirido seu app no Google Play.
Além da LVL, você precisa de um conjunto de códigos que faça download dos arquivos de expansão usando uma conexão HTTP e os salve no local adequado do armazenamento compartilhado do dispositivo. Conforme você cria esse procedimento no aplicativo, há vários problemas que precisam ser considerados:
- O dispositivo pode não ter espaço suficiente para os arquivos de expansão. Por isso, verifique antes de iniciar o download e avise o usuário se não houver espaço suficiente.
- Os downloads de arquivos precisam ocorrer em um serviço em segundo plano para evitar o bloqueio da interação do usuário e permitir que ele saia do aplicativo enquanto o download é concluído.
- Vários erros podem ocorrer durante a solicitação e o download. É necessário gerenciá-los corretamente.
- A conectividade de rede pode mudar durante o download. Então, é necessário lidar com essas mudanças e, se for interrompido, retomar o download quando possível.
- Enquanto o download ocorre em segundo plano, crie uma notificação que indique o progresso do download, notifique o usuário quando ele for concluído e leve-o de volta ao seu aplicativo quando selecionado.
Para simplificar esse trabalho para você, criamos a Biblioteca Downloader, que solicita os URLs do arquivo de expansão pelo serviço de licenciamento, faz o download dos arquivos de expansão, executa todas as tarefas listadas acima e até permite pausar e retomar o download. Ao adicionar a Biblioteca Downloader e alguns hooks de código ao aplicativo, quase todo o trabalho para fazer o download dos arquivos de expansão já está codificado para você. Desse modo, para proporcionar a melhor experiência do usuário com esforço mínimo da sua parte, recomendamos que você use a Biblioteca Downloader para fazer o download dos arquivos de expansão. As informações nas seções a seguir explicam como integrar a Biblioteca ao seu aplicativo.
Se preferir desenvolver sua própria solução para fazer o download dos arquivos de expansão usando os URLs
do Google Play, siga a documentação do licenciamento
de apps para executar uma solicitação de licença e recupere os nomes,
tamanhos e URLs dos arquivos de expansão a partir dos extras de resposta. Use a classe APKExpansionPolicy
(incluída na Biblioteca de Verificação e Licença) como sua política de licenciamento, que captura os nomes, tamanhos e URLs do arquivo de expansão do serviço de licenciamento.
Sobre a Biblioteca Downloader
Para usar os arquivos de expansão do APK no seu aplicativo e proporcionar a melhor experiência do usuário com o mínimo de esforço, recomendamos que você use a Biblioteca Downloader incluída no pacote da Biblioteca Google Play APK Expansion. Essa biblioteca faz o download dos arquivos de expansão em um serviço em segundo plano, mostra uma notificação ao usuário com o status de download, gerencia a perda de conectividade da rede, retoma o download quando possível e muito mais.
Para implementar downloads de arquivos de expansão usando a Biblioteca Downloader, você só precisa fazer o seguinte:
- Estenda uma subclasse especial
Service
e uma subclasseBroadcastReceiver
para que cada uma delas precise apenas de algumas linhas de código. - Adicione lógica à atividade principal que verifique se o download dos arquivos de expansão já foi feito e, se não, invoque o processo de download e exiba uma interface de progresso.
- Implemente uma interface de callback com alguns métodos na atividade principal que recebam atualizações sobre o andamento do download.
As seções a seguir explicam como configurar o aplicativo usando a Biblioteca Downloader.
Preparação para usar a Biblioteca Downloader
Para usar a Biblioteca Downloader, você precisa fazer o download de dois pacotes do SDK Manager e adicionar as bibliotecas apropriadas ao aplicativo.
Primeiro, abra o Android SDK Manager (Tools > SDK Manager) e em Appearance & Behavior > System Settings > Android SDK, selecione a guia SDK Tools para selecionar e fazer o download do seguinte:
- Pacote da Biblioteca Google Play Licensing
- Pacote da Biblioteca Google Play APK Expansion
Crie um novo módulo de biblioteca para a Biblioteca de Verificação de Licença e para a Biblioteca Downloader. Para todas as bibliotecas:
- Selecione File > New > New Module.
- Na janela Create New Module, selecione Android Library e, em seguida, selecione Next.
- Especifique um app/nome da biblioteca como "Google Play License Library" e "Google Play Downloader Library", escolha Minimum SDK level e selecione Finish.
- Selecione File > Project Structure.
- Selecione a guia Properties e, em Library Repository, insira a biblioteca do diretório
<sdk>/extras/google/
(play_licensing/
para a Biblioteca de Verificação de Licença ouplay_apk_expansion/downloader_library/
para a Biblioteca Downloader). - Selecione OK para criar o novo módulo.
Observação: a Biblioteca Downloader depende da Biblioteca de Verificação de Licença. Adicione a Biblioteca de Verificação de Licença às propriedades do projeto da Biblioteca Downloader.
Ou, usando uma linha de comando, atualize seu projeto para incluir as bibliotecas:
- Mude os diretórios para o
<sdk>/tools/
. - Execute
android update project
com a opção--library
para adicionar a LVL e a Biblioteca Downloader ao projeto. Exemplo:android update project --path ~/Android/MyApp \ --library ~/android_sdk/extras/google/market_licensing \ --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
Com a Biblioteca de Verificação de Licença e a Biblioteca Downloader adicionadas ao app, você poderá integrar rapidamente o recurso de download de arquivos de expansão do Google Play. O formato escolhido para os arquivos de expansão e a forma como você os lê no armazenamento compartilhado são uma implementação diferente, que precisa ser considerada com base nas necessidades do seu aplicativo.
Dica: o pacote de expansão do APK inclui um app de exemplo que ensina a usar a Biblioteca Downloader em um aplicativo. O exemplo usa uma terceira biblioteca disponível no pacote de expansão do APK, a Biblioteca APK Expansion ZIP. Se você planeja usar arquivos ZIP para os arquivos de expansão, sugerimos que também adicione a Biblioteca APK Expansion ZIP ao seu aplicativo. Para mais informações, consulte a seção abaixo Usar a Biblioteca APK Expansion ZIP.
Declarar permissões do usuário
Para fazer o download dos arquivos de expansão, a Biblioteca Downloader requer várias permissões que precisam ser declaradas no arquivo de manifesto do seu aplicativo. São elas:
<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>
Observação: por padrão, a Biblioteca Downloader requer a API de nível 4, mas a Biblioteca APK Expansion Zip requer a API de nível 5.
Implementar o serviço de download
Para executar downloads em segundo plano, a Biblioteca Downloader fornece a própria subclasse Service
chamada DownloaderService
que precisa ser estendida. Além de fazer o download dos arquivos de expansão para você, o DownloaderService
também:
- registra um
BroadcastReceiver
que detecta mudanças na conectividade de rede do dispositivo (a transmissãoCONNECTIVITY_ACTION
) para pausar o download quando necessário (como devido à perda de conectividade) e retomá-lo quando possível (quando há conectividade); - programa um alarme
RTC_WAKEUP
para tentar fazer o download novamente nos casos em que o serviço é encerrado; - cria um
Notification
personalizado que exibe o progresso do download e quaisquer erros ou mudanças de estado; - permite que o aplicativo pause e retome o download manualmente;
- verifica se o armazenamento compartilhado está ativado e disponível, se os arquivos já existem e se há espaço suficiente, tudo antes de fazer o download dos arquivos de expansão. Em seguida, o usuário será notificado se algum desses itens não for verdadeiro.
Tudo o que você precisa fazer é criar uma classe no aplicativo que estenda a classe DownloaderService
e modifique três métodos para fornecer detalhes específicos do aplicativo:
getPublicKey()
- Isso retornará uma string que é a chave pública RSA codificada em Base64 para sua conta de editor, disponível na página de perfil do Play Console (consulte Configurar para licenciamento).
getSALT()
- Isso retornará uma matriz de bytes aleatórios que o licenciamento
Policy
usa para criar umObfuscator
. O sal garante que seu arquivoSharedPreferences
ofuscado em que os dados de licenciamento são salvos será exclusivo e não detectável. getAlarmReceiverClassName()
- Isso retornará o nome da classe
BroadcastReceiver
no seu aplicativo, que receberá o alarme indicando que o download precisa ser reiniciado (o que pode acontecer se o serviço de download parar inesperadamente).
Por exemplo, veja uma implementação completa de DownloaderService
:
Kotlin
// 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 }
Java
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(); } }
Aviso: atualize o valor BASE64_PUBLIC_KEY
para que seja a chave pública pertencente à sua conta de editor. Você pode encontrar a chave no Developers Console nas informações do seu perfil. Isso é necessário até mesmo ao testar seus downloads.
Lembre-se de declarar o serviço no arquivo de manifesto:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
Implementar o receptor de alarme
Para monitorar o progresso dos downloads de arquivos e reiniciar o download, se necessário, o
DownloaderService
agendará um alarme RTC_WAKEUP
que
entrega uma Intent
a um BroadcastReceiver
no
app. Você precisa definir o BroadcastReceiver
para chamar uma API
da Biblioteca Downloader que verifica o status do download e o reinicia,
se necessário.
Só é necessário substituir o método onReceive()
para chamar DownloaderClientMarshaller.startDownloadServiceIfRequired()
.
Exemplo:
Kotlin
class SampleAlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { try { DownloaderClientMarshaller.startDownloadServiceIfRequired( context, intent, SampleDownloaderService::class.java ) } catch (e: PackageManager.NameNotFoundException) { e.printStackTrace() } } }
Java
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(); } } }
Observe que essa é a classe a que você precisa retornar o nome no método getAlarmReceiverClassName()
do serviço (consulte a seção anterior).
Lembre-se de declarar o destinatário no arquivo de manifesto:
<app ...> <receiver android:name=".SampleAlarmReceiver" /> ... </app>
Iniciar o download
A principal atividade do aplicativo, iniciada pelo ícone na tela de início, é responsável por verificar se os arquivos de expansão já estão no dispositivo e iniciar o download, se não estiverem.
Para iniciar o download usando a Biblioteca Downloader são necessários os seguintes procedimentos:
- Verifique se o download dos arquivos foi feito.
A Biblioteca Downloader inclui algumas APIs na classe
Helper
para ajudar nesse processo:getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
doesFileExist(Context c, String fileName, long fileSize)
O app de exemplo fornecido no pacote de expansão do APK pode chamar o método a seguir no método
onCreate()
da atividade para verificar se os arquivos de expansão já existem no dispositivo:Kotlin
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 }
Java
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; }
Nesse caso, cada objeto
XAPKFile
contém o número da versão e o tamanho de um arquivo de expansão conhecido e um booleano que indica se é o arquivo de expansão principal. Consulte a classeSampleDownloaderActivity
do app de exemplo para mais detalhes.Se esse método retornar falso, o aplicativo iniciará o download.
- Inicie o download chamando o método estático
DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass)
.O método utiliza os seguintes parâmetros:
context
:Context
do app.notificationClient
: umaPendingIntent
para iniciar a atividade principal. É usada naNotification
que oDownloaderService
cria para mostrar o progresso do download. Quando o usuário seleciona a notificação, o sistema invoca aPendingIntent
fornecida aqui e precisa abrir a atividade que mostra o progresso do download (geralmente a mesma atividade que iniciou o download).serviceClass
: o objetoClass
para a implementação deDownloaderService
, obrigatório para iniciar o serviço e iniciar o download, se necessário.
O método retorna um número inteiro que indica se o download é obrigatório ou não. Os valores possíveis são:
NO_DOWNLOAD_REQUIRED
: retornados se os arquivos já existirem ou se um download já estiver em andamento.LVL_CHECK_REQUIRED
: retornados se uma verificação de licença for necessária para conseguir os URLs do arquivo de expansão.DOWNLOAD_REQUIRED
: retornados se os URLs do arquivo de expansão já forem conhecidos, mas não tiverem sido transferidos por download.
O comportamento para
LVL_CHECK_REQUIRED
eDOWNLOAD_REQUIRED
é essencialmente o mesmo e você normalmente não precisa se preocupar com eles. Na atividade principal que chamastartDownloadServiceIfRequired()
, você pode simplesmente verificar se a resposta éNO_DOWNLOAD_REQUIRED
. Se a resposta for algo diferente deNO_DOWNLOAD_REQUIRED
, a Biblioteca Downloader iniciará o download e será necessário atualizar a IU da atividade para exibir o progresso do download (consulte a próxima etapa). Se a resposta forNO_DOWNLOAD_REQUIRED
, os arquivos estarão disponíveis e o aplicativo poderá ser iniciado.Exemplo:
Kotlin
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 }
Java
@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 }
- Quando o método
startDownloadServiceIfRequired()
retornar algo diferente deNO_DOWNLOAD_REQUIRED
, crie uma instância deIStub
chamandoDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService)
. OIStub
fornece uma vinculação entre a atividade e o serviço de download, de modo que a atividade receba callbacks sobre o andamento do download.Para instanciar seu
IStub
chamandoCreateStub()
, passe uma implementação da interfaceIDownloaderClient
e sua implementação doDownloaderService
. A próxima seção, Receber o progresso do download, aborda a interface doIDownloaderClient
, que você precisa implementar na classeActivity
para atualizar a IU da atividade quando o estado do download mudar.Recomendamos que você chame
CreateStub()
para instanciar seuIStub
durante o métodoonCreate()
da atividade depois questartDownloadServiceIfRequired()
iniciar o download.Por exemplo, no exemplo de código anterior para
onCreate()
, você pode responder ao resultado dostartDownloadServiceIfRequired()
da seguinte forma:Kotlin
// 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 }
Java
// 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; }
Depois que o método
onCreate()
for retornado, sua atividade receberá uma chamada paraonResume()
, que é onde você precisa chamarconnect()
noIStub
, transmitindo oContext
do aplicativo. Por outro lado, chamedisconnect()
no callbackonStop()
da atividade.Kotlin
override fun onResume() { downloaderClientStub?.connect(this) super.onResume() } override fun onStop() { downloaderClientStub?.disconnect(this) super.onStop() }
Java
@Override protected void onResume() { if (null != downloaderClientStub) { downloaderClientStub.connect(this); } super.onResume(); } @Override protected void onStop() { if (null != downloaderClientStub) { downloaderClientStub.disconnect(this); } super.onStop(); }
Chamar
connect()
emIStub
vincula sua atividade aoDownloaderService
de forma que sua atividade receba callbacks referentes a mudanças no estado do download pela interfaceIDownloaderClient
.
Receber o progresso do download
Para receber atualizações sobre o progresso do download e interagir com o DownloaderService
, implemente a interface IDownloaderClient
da Biblioteca Downloader.
Normalmente, a atividade que você usa para iniciar o download precisa implementar essa interface para exibir o progresso do download e enviar solicitações ao serviço.
Os métodos de interface necessários para IDownloaderClient
são:
onServiceConnected(Messenger m)
- Depois de instanciar
IStub
na atividade, você receberá uma chamada para esse método, que transmite um objetoMessenger
conectado à instância deDownloaderService
. Para enviar solicitações ao serviço, como pausar e retomar downloads, chameDownloaderServiceMarshaller.CreateProxy()
para receber a interfaceIDownloaderService
conectada ao serviço.Uma implementação recomendada é semelhante ao seguinte:
Kotlin
private var remoteService: IDownloaderService? = null ... override fun onServiceConnected(m: Messenger) { remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { downloaderClientStub?.messenger?.also { messenger -> onClientUpdated(messenger) } } }
Java
private IDownloaderService remoteService; ... @Override public void onServiceConnected(Messenger m) { remoteService = DownloaderServiceMarshaller.CreateProxy(m); remoteService.onClientUpdated(downloaderClientStub.getMessenger()); }
Com o objeto
IDownloaderService
inicializado, você pode enviar comandos para o serviço de download, como pausar e retomar o download (requestPauseDownload()
erequestContinueDownload()
). onDownloadStateChanged(int newState)
- O serviço de download chama este método quando ocorre uma mudança no estado de download, como o início ou conclusão dele.
O valor
newState
será um dos diversos valores possíveis especificados por uma das constantesSTATE_*
da classeIDownloaderClient
.Para fornecer uma mensagem útil aos seus usuários, você pode solicitar uma sequência correspondente para cada estado chamando
Helpers.getDownloaderStringResourceIDFromState()
. Isso retorna o ID do recurso para uma das strings agrupadas com a Biblioteca Downloader. Por exemplo, a string "Download pausado porque você está em roaming" corresponde aSTATE_PAUSED_ROAMING
. onDownloadProgress(DownloadProgressInfo progress)
- O serviço de download chama este método para exibir um objeto
DownloadProgressInfo
, que descreve várias informações sobre o progresso do download, incluindo tempo estimado restante, velocidade atual, progresso geral e total para que você possa atualizar a IU de progresso do download.
Dica: para exemplos desses callbacks que atualizam a IU de progresso do download, consulte o SampleDownloaderActivity
no app de exemplo fornecido com o pacote de expansão do APK.
Alguns métodos públicos para a interface IDownloaderService
que podem ser úteis são:
requestPauseDownload()
- Pausa o download.
requestContinueDownload()
- Retoma um download pausado.
setDownloadFlags(int flags)
- Define as preferências do usuário para os tipos de rede em que não há problema fazer o download dos arquivos. A implementação atual é compatível com uma sinalização,
FLAGS_DOWNLOAD_OVER_CELLULAR
, mas você pode adicionar outras. Por padrão, essa sinalização não está ativada. Portanto, o usuário precisa ter uma conexão Wi-Fi para fazer o download de arquivos de expansão. É recomendável fornecer uma preferência de usuário para ativar os downloads pela rede celular. Nesse caso, você pode chamar:Kotlin
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
Java
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
Usar APKExpansionPolicy
Se você decidir criar seu próprio serviço de download em vez de usar a Biblioteca Downloader
do Google Play, ainda é preciso usar a APKExpansionPolicy
fornecida na Biblioteca de Verificação de Licença. A classe APKExpansionPolicy
é quase idêntica à ServerManagedPolicy
(disponível na Biblioteca de Verificação de Licença do Google Play), mas inclui mais tratamento para os extras de resposta de arquivo de expansão do APK.
Observação: se você usar a Biblioteca Downloader conforme discutido na seção anterior, a biblioteca realizará toda a interação com APKExpansionPolicy
. Não será necessário usar essa classe diretamente.
A classe inclui métodos para ajudar você a ter as informações necessárias sobre os arquivos de expansão disponíveis:
getExpansionURLCount()
getExpansionURL(int index)
getExpansionFileName(int index)
getExpansionFileSize(int index)
Para mais informações sobre como usar o APKExpansionPolicy
quando não estiver usando a Biblioteca Downloader, consulte a documentação de Adicionar licenciamento ao seu aplicativo, que explica como implementar uma política de licença como esta.
Ler o arquivo de expansão
Depois que seus arquivos de expansão do APK forem salvos no dispositivo, a maneira como você lerá arquivos
dependerá do tipo de arquivo usado. Como discutido na visão geral, seus arquivos de expansão podem ser qualquer tipo de arquivo desejado, mas são renomeados usando um formato de nome de arquivo específico e são salvos em <shared-storage>/Android/obb/<package-name>/
.
Independentemente de como você lê os arquivos, verifique sempre se o armazenamento externo está disponível para leitura. Há uma possibilidade de que o usuário tenha o armazenamento conectado a um computador por USB ou tenha removido o cartão SD.
Observação: quando seu aplicativo for iniciado, sempre verifique se o espaço de armazenamento externo está disponível e legível chamando getExternalStorageState()
. Isso retorna uma das várias strings possíveis que representam o estado do armazenamento externo. Para que possa ser lido pelo aplicativo, o valor de retorno precisa ser MEDIA_MOUNTED
.
Receber os nomes dos arquivos
Conforme descrito na visão geral, seus arquivos de expansão do APK são salvos usando um formato de nome de arquivo específico:
[main|patch].<expansion-version>.<package-name>.obb
Para ver o local e os nomes dos arquivos de expansão, use os métodos getExternalStorageDirectory()
e getPackageName()
para construir o caminho para os arquivos.
Veja um método que você pode usar no aplicativo para receber uma matriz contendo o caminho completo para os dois arquivos de expansão:
Kotlin
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() }
Java
// 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; }
Você pode chamar esse método passando a ele o Context
do seu aplicativo e a versão do arquivo de expansão que você quer.
Há várias maneiras de determinar o número da versão do arquivo de expansão. Uma maneira simples é salvar a versão em um arquivo SharedPreferences
quando o download começar, consultando o nome do arquivo de expansão com o método getExpansionFileName(int index)
da classe APKExpansionPolicy
. Então, você poderá ver o código da versão lendo o arquivo SharedPreferences
quando quiser acessar o arquivo de expansão.
Para mais informações sobre como ler no armazenamento compartilhado, consulte a documentação do Armazenamento de dados.
Usar a Biblioteca APK Expansion Zip
O pacote de expansão do APK do Google Market inclui uma biblioteca chamada "Biblioteca APK Expansion Zip", localizada em <sdk>/extras/google/google_market_apk_expansion/zip_file/
. Essa é uma biblioteca opcional que ajuda você a ler seus arquivos de expansão quando eles são salvos como arquivos ZIP. O uso dessa biblioteca permite que você leia com facilidade os recursos dos arquivos de expansão ZIP como um sistema de arquivos virtual.
A Biblioteca APK Expansion Zip inclui as seguintes classes e APIs:
APKExpansionSupport
- Fornece alguns métodos para acessar nomes de arquivos de expansão e arquivos ZIP:
getAPKExpansionFiles()
- O mesmo método mostrado acima que retorna o caminho completo do arquivo para os dois arquivos de expansão.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
- Retorna um
ZipResourceFile
que representa a soma do arquivo principal e do arquivo de patch. Ou seja, se você especificarmainVersion
epatchVersion
, isso retornará umZipResourceFile
que fornece acesso de leitura a todos os dados, com os dados do arquivo de patch mesclados com o arquivo principal.
ZipResourceFile
- Representa um arquivo ZIP no armazenamento compartilhado e realiza todo o trabalho para fornecer um sistema de arquivos virtual com base nos arquivos ZIP. Você pode ter uma instância usando
APKExpansionSupport.getAPKExpansionZipFile()
ou comZipResourceFile
transmitindo o caminho para o arquivo de expansão. Essa classe inclui uma variedade de métodos úteis, mas você geralmente não precisa acessar a maioria deles. Dois métodos importantes são:getInputStream(String assetPath)
- Fornece um
InputStream
para ler um arquivo no arquivo ZIP. OassetPath
precisa ser o caminho para o arquivo desejado, relativo à raiz do conteúdo do arquivo ZIP. getAssetFileDescriptor(String assetPath)
- Fornece um
AssetFileDescriptor
para um arquivo dentro do arquivo ZIP. OassetPath
precisa ser o caminho para o arquivo desejado, relativo à raiz do conteúdo do arquivo ZIP. Isso é útil para algumas APIs do Android que exigem umAssetFileDescriptor
, como algumas APIsMediaPlayer
.
APEZProvider
- A maioria dos aplicativos não precisa usar essa classe. Essa classe define um
ContentProvider
que organiza os dados dos arquivos ZIP usando um provedor de conteúdoUri
para fornecer acesso a arquivos para determinadas APIs do Android que esperam ter acesso aUri
s de arquivos de mídia. Por exemplo, isso é útil se você quiser reproduzir um vídeo comVideoView.setVideoURI()
.
Pular a compactação ZIP de arquivos de mídia
Se você estiver usando arquivos de expansão para armazenar arquivos de mídia, um arquivo ZIP ainda permitirá que você use chamadas de reprodução de mídia Android que fornecem controles de compensação e comprimento (como MediaPlayer.setDataSource()
e SoundPool.load()
). Para que isso funcione, não realize mais compactação nos arquivos de mídia ao criar os pacotes ZIP. Por exemplo, ao usar a ferramenta zip
, use a opção -n
para especificar os sufixos de arquivo que não podem ser compactados:
zip -n .mp4;.ogg main_expansion media_files
Ler em um arquivo ZIP
Ao usar a Biblioteca APK Expansion Zip, a leitura de um arquivo do ZIP geralmente requer o seguinte:
Kotlin
// 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 { ... }
Java
// 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);
O código acima oferece acesso a qualquer arquivo que exista no arquivo de expansão principal ou de patch, lendo em um mapa mesclado de todos os arquivos de ambos arquivos. Tudo o que você precisa para fornecer o método getAPKExpansionFile()
é o android.content.Context
do app e o número da versão do arquivo de expansão principal e do arquivo de expansão de patch.
Se preferir ler em um arquivo de expansão específico, você pode usar o construtor ZipResourceFile
com o caminho para o arquivo de expansão desejado:
Kotlin
// 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 { ... }
Java
// 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);
Para mais informações sobre como usar essa biblioteca para arquivos de expansão, consulte a classe SampleDownloaderActivity
do app de exemplo, que inclui mais código para verificar os arquivos transferidos por download usando verificação cíclica de redundância (CRC, na sigla em inglês). Saiba que, se você usar essa amostra como base para sua própria implementação, será necessário declarar o tamanho do byte dos arquivos de expansão na matriz xAPKS
.
Testar seus arquivos de expansão
Antes de publicar seu aplicativo, há duas coisas que precisam ser testadas: a leitura e o download dos arquivos de expansão.
Testar leituras de arquivos
Antes de fazer upload do seu aplicativo para o Google Play, teste a capacidade do app de ler os arquivos no armazenamento compartilhado. Você só precisa adicionar os arquivos ao local apropriado no armazenamento compartilhado do dispositivo e iniciar o aplicativo:
- No seu dispositivo, crie o diretório adequado no armazenamento compartilhado em que o Google Play salvará seus arquivos.
Por exemplo, se o nome do seu pacote for
com.example.android
, será necessário criar o diretórioAndroid/obb/com.example.android/
no espaço de armazenamento compartilhado. Conecte o dispositivo de teste ao computador para ativar o armazenamento compartilhado e crie esse diretório manualmente. - Adicione manualmente os arquivos de expansão a esse diretório. Não se esqueça de renomear os arquivos para que correspondam ao formato de nome de arquivo usado pelo Google Play.
Por exemplo, independentemente do tipo de arquivo, o arquivo de expansão principal do aplicativo
com.example.android
precisa sermain.0300110.com.example.android.obb
. O código da versão pode ser o valor que você quiser. Não se esqueça:- O arquivo de expansão principal sempre começa com
main
e o arquivo de patch começa compatch
. - O nome do pacote sempre corresponde ao do APK a que o arquivo está anexado no Google Play.
- O arquivo de expansão principal sempre começa com
- Agora que os arquivos de expansão estão no dispositivo, você pode instalar e executar seu aplicativo para testar esses arquivos.
Veja alguns lembretes sobre como gerenciar os arquivos de expansão:
- Não exclua nem renomeie os arquivos
.obb
de expansão (mesmo se você descompactar os dados para um local diferente). Isso fará com que o Google Play (ou o próprio aplicativo) faça o download repetidamente do arquivo de expansão. - Não salve outros dados no diretório
obb/
. Se precisar descompactar dados, salve-os no local especificado porgetExternalFilesDir()
.
Testar o download de arquivos
Como seu aplicativo precisa fazer o download manualmente dos arquivos de expansão quando é aberto pela primeira vez, é importante testar esse processo para garantir que o aplicativo possa consultar as URLs, fazer o download dos arquivos e salvá-los no dispositivo.
Para testar a implementação do procedimento de download manual do aplicativo, você pode publicá-lo na faixa de teste interna. Assim, ele só fica disponível para testadores autorizados. Se tudo funcionar conforme o esperado, seu aplicativo começará a fazer o download dos arquivos de expansão assim que a atividade principal começar.
Observação: antes, era possível testar um app fazendo upload de uma versão de "rascunho" não publicada. Esse recurso não está mais disponível. Em vez disso, é necessário publicá-lo em uma faixa de teste interna, fechada ou aberta. Para saber mais, consulte Aplicativos de rascunho não são mais compatíveis.
Atualizar seu app
Uma das grandes vantagens de usar arquivos de expansão no Google Play é a capacidade de atualizar seu aplicativo sem fazer de novo o download de todos os recursos originais. Como o Google Play permite que você forneça dois arquivos de expansão para cada APK, você pode usar o segundo arquivo como um patch que fornece atualizações e novos recursos. Isso evita a necessidade de fazer o download novamente do arquivo de expansão principal, que pode ser grande e caro para os usuários.
O arquivo de expansão de patch é tecnicamente igual ao arquivo de expansão principal e nem o sistema Android nem o Google Play fazem correções reais entre os arquivos de expansão principais e de patch. O código do aplicativo precisa executar os patches necessários.
Se você usar arquivos ZIP como arquivos de expansão, a Biblioteca APK Expansion Zip presente no pacote de expansão do APK incluirá a capacidade de mesclar o arquivo de patch com o arquivo de expansão principal.
Observação: mesmo que você só precise fazer modificações no arquivo de expansão do patch, atualize o APK para que o Google Play faça uma atualização.
Se você não precisar de mudanças de código no aplicativo, basta atualizar versionCode
no manifesto.
Contanto que você não modifique o arquivo de expansão principal associado ao APK no Play Console, os usuários que já instalaram o aplicativo não farão o download do arquivo de expansão principal. Os usuários existentes receberão apenas o APK atualizado e o novo arquivo de expansão de patch (mantendo o arquivo de expansão principal anterior).
Veja a seguir alguns problemas relacionados às atualizações de arquivos de expansão:
- Pode haver somente dois arquivos de expansão por vez para o aplicativo. Um arquivo de expansão principal e um arquivo de expansão de patch. Durante uma atualização em um arquivo, o Google Play exclui a versão anterior (assim como seu aplicativo precisará fazer ao realizar atualizações manuais).
- Ao adicionar um arquivo de expansão de patch, o sistema Android não corrige seu aplicativo nem o arquivo de expansão principal. Projete seu aplicativo para ser compatível com os dados de patches. No entanto, o pacote de expansão do APK inclui uma biblioteca para usar arquivos ZIP como arquivos de expansão, que mescla os dados do arquivo de patch com o arquivo de expansão principal para que você possa ler facilmente todos os dados dos arquivos de expansão.