Google Play richiede che l'APK compresso scaricato dagli utenti non superi i 100 MB. Per la maggior parte delle app, questo spazio è sufficiente per tutto il codice e gli asset dell'app. Tuttavia, alcune app richiedono più spazio per immagini ad alta fedeltà, file multimediali o altri asset di grandi dimensioni. In precedenza, se le dimensioni compresse del download dell'app superavano i 100 MB, dovevi ospitare e scaricare autonomamente le risorse aggiuntive quando l'utente apriva l'app. L'hosting e il servizio dei file aggiuntivi possono essere costosi e l'esperienza utente è spesso tutt'altro che ideale. Per semplificare questa procedura per te e renderla più piacevole per gli utenti, Google Play ti consente di allegare due file di espansione di grandi dimensioni che integrano il tuo APK.
Google Play ospita i file di espansione per la tua app e li invia al dispositivo senza costi aggiuntivi. I file di espansione vengono salvati nell'archivio condiviso del dispositivo (la scheda SD o la partizione montabile USB, nota anche come memoria "esterna") a cui l'app può accedere. Sulla maggior parte dei dispositivi, Google Play scarica i file di espansione contemporaneamente all'APK, quindi la tua app ha tutto ciò di cui ha bisogno quando l'utente la apre per la prima volta. In alcuni casi, però, l'app deve scaricare i file da Google Play al suo avvio.
Se vuoi evitare di utilizzare i file di espansione e le dimensioni del download compresse della tua app sono superiori a 100 MB, devi caricare l'app utilizzando i bundle di app Android, che consentono dimensioni del download compresse fino a 200 MB. Inoltre, poiché l'utilizzo degli app bundle rimanda la generazione e la firma degli APK a Google Play, gli utenti scaricano APK ottimizzati contenenti solo il codice e le risorse necessarie per eseguire la tua app. Non devi creare, firmare e gestire più APK o file di espansione e gli utenti ottengono download più piccoli e ottimizzati.
Panoramica
Ogni volta che carichi un APK utilizzando Google Play Console, hai la possibilità di aggiungere uno o due file di espansione all'APK. Ogni file può avere dimensioni massime pari a 2 GB e può essere in qualsiasi formato scelto, ma ti consigliamo di utilizzare un file compresso per risparmiare larghezza di banda durante il download. Concettualmente, ogni file di espansione ha un ruolo diverso:
- Il file di espansione principale è il file di espansione principale per le risorse aggiuntive richieste dalla tua app.
- Il file di espansione patch è facoltativo ed è destinato a piccoli aggiornamenti del file di espansione principale.
Sebbene tu possa utilizzare i due file di espansione come preferisci, ti consigliamo di utilizzare il file di espansione principale per caricare le risorse principali e di aggiornarlo raramente, se non mai. Il file di espansione patch deve essere più piccolo e fungere da "supporto per le patch", venendo aggiornato con ogni release principale o secondo necessità.
Tuttavia, anche se l'aggiornamento dell'app richiede solo un nuovo file di espansione della patch, devi comunque caricare un nuovo APK con un valore versionCode
aggiornato nel file manifest. (Play Console non ti consente di caricare un file di espansione in un APK esistente).
Nota:il file di espansione patch è semanticamente uguale al file di espansione principale. Puoi utilizzare ciascun file come preferisci.
Formato del nome file
Ogni file di espansione che carichi può essere in qualsiasi formato (ZIP, PDF, MP4 e così via). Puoi anche utilizzare lo strumento JOBB per incapsulare e criptare un insieme di file di risorse e patch successive per quell'insieme. Indipendentemente dal tipo di file, Google Play li considera blob binari opachi e rinomina i file utilizzando il seguente schema:
[main|patch].<expansion-version>.<package-name>.obb
Questo schema è composto da tre componenti:
main
opatch
- Specifica se il file è il file di espansione principale o patch. Per ogni APK può esserci un solo file principale e un solo file patch.
<expansion-version>
- Si tratta di un numero intero che corrisponde al codice di versione dell'APK con cui l'espansione è associata per prima volta (corrisponde al valore
android:versionCode
dell'app).L'evidenziazione della parola"primo" è dovuta al fatto che, anche se Play Console ti consente di riutilizzare un file di espansione caricato con un nuovo APK, il nome del file di espansione non cambia, ma mantiene la versione applicata al primo caricamento.
<package-name>
- Il nome del pacchetto in stile Java della tua app.
Ad esempio, supponiamo che la versione dell'APK sia 314159 e che il nome del pacchetto sia com.example.app. Se caricai un file di espansione principale, il file viene rinominato in:
main.314159.com.example.app.obb
Posizione archiviazione
Quando Google Play scarica i file di espansione su un dispositivo, li salva nella posizione di archiviazione condivisa del sistema. Per garantire il corretto funzionamento, non devi eliminare, spostare o rinominare i file di espansione. Se la tua app deve eseguire il download direttamente da Google Play, devi salvare i file nella stessa posizione esatta.
Il metodo getObbDir()
restituisce la posizione specifica
per i file di espansione nel seguente formato:
<shared-storage>/Android/obb/<package-name>/
<shared-storage>
è il percorso dello spazio di archiviazione condiviso, disponibile dagetExternalStorageDirectory()
.<package-name>
è il nome del pacchetto in stile Java della tua app, disponibile dagetPackageName()
.
Per ogni app, in questa directory non sono presenti mai più di due file di espansione.
Uno è il file di espansione principale e l'altro è il file di espansione della patch (se necessario). Le versioni precedenti vengono sovrascritte quando aggiorni l'app con nuovi file di espansione. Da Android 4.4 (livello API 19), le app possono leggere i file di espansione OBB
senza autorizzazione di archiviazione esterna. Tuttavia, alcune implementazioni di Android 6.0 (livello API 23) e versioni successive richiedono ancora l'autorizzazione, pertanto dovrai dichiarare l'autorizzazione READ_EXTERNAL_STORAGE
nel file manifest dell'app e chiedere l'autorizzazione in fase di esecuzione nel seguente modo:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Per Android 6 e versioni successive, l'autorizzazione di accesso allo spazio di archiviazione esterno deve essere richiesta in fase di runtime. Tuttavia, alcune implementazioni di Android non richiedono l'autorizzazione per leggere i file OBB. Il seguente snippet di codice mostra come verificare l'accesso in lettura prima di richiedere l'autorizzazione per lo archiviazione esterno:
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 devi decomprimere i contenuti dei file di espansione, non eliminare i file di espansioneOBB
e non salvare i dati decompressi nella stessa directory. Devi salvare i file scompattati nella directory specificata da getExternalFilesDir()
. Tuttavia, se possibile, è meglio utilizzare un formato file di espansione che ti consenta di leggere direttamente dal file anziché dover estrarre i dati. Ad esempio, abbiamo fornito un progetto di libreria chiamato APK Expansion Zip Library che legge i dati direttamente dal file ZIP.
Attenzione: a differenza dei file APK, tutti i file salvati nello spazio di archiviazione condiviso possono essere letti dall'utente e da altre app.
Suggerimento:se pacchetti i file multimediali in un file ZIP, puoi utilizzare le chiamate di riproduzione dei contenuti multimediali sui file con controlli di offset e lunghezza (ad esempio MediaPlayer.setDataSource()
e SoundPool.load()
) senza dover decomprimere il file ZIP. Affinché ciò funzioni, non devi eseguire una compressione aggiuntiva sui file multimediali durante la creazione dei pacchetti ZIP. Ad esempio, quando utilizzi lo strumento zip
,
devi utilizzare l'opzione -n
per specificare i suffissi dei file che non devono essere
compressi:
zip -n .mp4;.ogg main_expansion media_files
Procedura di download
Nella maggior parte dei casi, Google Play scarica e salva i file di espansione contemporaneamente al download dell'APK sul dispositivo. Tuttavia, in alcuni casi Google Play non può scaricare i file di espansione o l'utente potrebbe aver eliminato i file di espansione scaricati in precedenza. Per gestire queste situazioni, l'app deve essere in grado di scaricare i file da sola all'avvio dell'attività principale, utilizzando un URL fornito da Google Play.
La procedura di download a livello generale è la seguente:
- L'utente seleziona l'installazione della tua app da Google Play.
- Se Google Play è in grado di scaricare i file di espansione (come accade per la maggior parte dei dispositivi), li scarica insieme all'APK.
Se Google Play non riesce a scaricare i file di espansione, scarica solo l'APK.
- Quando l'utente avvia l'app, quest'ultima deve verificare se i file di espansione sono già salvati sul dispositivo.
- In caso affermativo, la tua app è pronta per essere utilizzata.
- In caso contrario, l'app deve scaricare i file di espansione tramite HTTP da Google Play. La tua app deve inviare una richiesta al client di Google Play utilizzando il servizio Licenze app di Google Play, che risponde con il nome, le dimensioni del file e l'URL di ogni file di espansione. Con queste informazioni, puoi scaricare i file e salvarli nella posizione di archiviazione corretta.
Attenzione: è fondamentale includere il codice necessario per scaricare i file di espansione da Google Play nel caso in cui i file non siano già sul dispositivo all'avvio dell'app. Come spiegato nella sezione seguente relativa al download dei file di espansione, abbiamo messo a tua disposizione una libreria che semplifica notevolmente questa procedura ed esegue il download da un servizio con una quantità minima di codice.
Elenco di controllo per lo sviluppo
Ecco un riepilogo delle attività che devi svolgere per utilizzare i file di espansione con la tua app:
- Innanzitutto, determina se le dimensioni di download compresse della tua app devono essere superiori a 100 MB. Lo spazio è prezioso e devi mantenere le dimensioni totali del download il più piccole possibile. Se la tua app impiega più di 100 MB per fornire più versioni degli asset grafici per più densità dello schermo, ti consigliamo di pubblicare più APK in cui ogni APK contenga solo gli asset richiesti per le schermate di destinazione. Per ottenere i risultati migliori quando pubblichi su Google Play, carica un app bundle per Android, che include tutto il codice e le risorse compilati della tua app, ma rimanda la generazione e la firma dell'APK a Google Play.
- Determina quali risorse dell'app separare dall'APK e impacchettale in un
file da utilizzare come file di espansione principale.
In genere, devi utilizzare il secondo file di espansione patch solo quando esegui aggiornamenti al file di espansione principale. Tuttavia, se le risorse superano il limite di 2 GB per il file di espansione principale, puoi utilizzare il file patch per il resto degli asset.
- Sviluppare l'app in modo che utilizzi le risorse dei file di espansione nella posizione dello spazio di archiviazione condiviso del dispositivo.
Ricorda che non devi eliminare, spostare o rinominare i file di espansione.
Se la tua app non richiede un formato specifico, ti consigliamo di creare file ZIP per i file di espansione, quindi di leggerli utilizzando la libreria ZIP di espansione APK.
- Aggiungi alla logica principale dell'app una logica che controlli se i file di espansione sono sul dispositivo all'avvio. Se i file non sono sul dispositivo, utilizza il servizio Licenze per app di Google Play per richiedere gli URL per i file di espansione, quindi scaricali e salvali.
Per ridurre notevolmente la quantità di codice da scrivere e garantire un'esperienza utente positiva durante il download, ti consigliamo di utilizzare la Downloader Library per implementare il comportamento di download.
Se crei il tuo servizio di download anziché utilizzare la libreria, tieni presente che non devi modificare il nome dei file di espansione e devi salvarli nella corretta posizione di archiviazione.
Una volta completato lo sviluppo dell'app, segui la guida al test dei file di espansione.
Regole e limitazioni
L'aggiunta di file di espansione APK è una funzionalità disponibile quando carichi l'app tramite Play Console. Quando carichi la tua app per la prima volta o aggiorni un'app che utilizza file di espansione, devi tenere presente le seguenti regole e limitazioni:
- Ogni file di espansione non può avere dimensioni superiori a 2 GB.
- Per scaricare i file di espansione da Google Play, l'utente deve aver acquisito la tua app da Google Play. Google Play non fornirà gli URL dei file di espansione se l'app è stata installata con altri mezzi.
- Quando esegui il download dall'app, l'URL fornito da Google Play per ogni file è univoco per ogni download e scade poco dopo che è stato fornito all'app.
- Se aggiorni l'app con un nuovo APK o carichi più APK per la stessa app, puoi selezionare i file di espansione che hai caricato per un APK precedente. Il nome del file di espansione non cambia: viene mantenuta la versione ricevuta dall'APK a cui il file era associato in origine.
- Se utilizzi i file di espansione in combinazione con più APK per fornire file di espansione diversi per dispositivi diversi, devi comunque caricare APK separati per ogni dispositivo per fornire un valore
versionCode
unico e dichiarare filtri diversi per ogni APK. - Non puoi rilasciare un aggiornamento dell'app modificando solo i file di espansione. Devi caricare un nuovo APK per aggiornare l'app. Se le modifiche riguardano solo gli asset nei file di espansione, puoi aggiornare l'APK semplicemente modificando
versionCode
(e forse ancheversionName
). - Non salvare altri dati nella directory
obb/
. Se devi decomprimere alcuni dati, salvali nella posizione specificata dagetExternalFilesDir()
. - Non eliminare o rinominare il file di espansione
.obb
(a meno che non stia eseguendo un aggiornamento). In questo modo, Google Play (o la tua app stessa) scaricherà ripetutamente il file di espansione. - Quando aggiorni un file di espansione manualmente, devi eliminare il file di espansione precedente.
Download dei file di espansione
Nella maggior parte dei casi, Google Play scarica e salva i file di espansione sul dispositivo contemporaneamente all'installazione o all'aggiornamento dell'APK. In questo modo, i file di espansione sono disponibili al primo avvio dell'app. Tuttavia, in alcuni casi l'app deve scaricare i file di espansione richiedendoli da un URL fornito in una risposta del servizio Licenze per app di Google Play.
La logica di base necessaria per scaricare i file di espansione è la seguente:
- Quando l'app si avvia, cerca i file di espansione nella posizione dello spazio di archiviazione condiviso (nella directory
Android/obb/<package-name>/
).- Se i file di espansione sono presenti, non devi fare altro e la tua app può continuare.
- Se i file di espansione non sono presenti:
- Esegui una richiesta utilizzando la licenza per le app di Google Play per ottenere i nomi, le dimensioni e gli URL dei file di espansione della tua app.
- Utilizza gli URL forniti da Google Play per scaricare e salvare i file di espansione. Devi salvare i file nella posizione di archiviazione condivisa
(
Android/obb/<package-name>/
) e utilizzare il nome file esatto fornito dalla risposta di Google Play.Nota: l'URL fornito da Google Play per i file di espansione è univoco per ogni download e scade poco dopo essere stato assegnato alla tua app.
Se la tua app è senza costi (non a pagamento), probabilmente non hai utilizzato il servizio Licenze per app. È principalmente pensato per consentirti di applicare le norme di licenza per la tua app e assicurarti che l'utente abbia il diritto di utilizzarla (l'ha pagata legittimamente su Google Play). Per semplificare la funzionalità dei file di espansione, il servizio di licenza è stato migliorato per fornire una risposta alla tua app che include l'URL dei file di espansione dell'app ospitati su Google Play. Pertanto, anche se la tua app è senza costi per gli utenti, devi includere la LVL (License Verification Library) per utilizzare i file di espansione APK. Ovviamente, se la tua app è senza costi, non devi applicare la verifica della licenza: devi semplicemente che la libreria esegua la richiesta che restituisce l'URL dei file di espansione.
Nota: indipendentemente dal fatto che la tua app sia senza costi o meno, Google Play restituisce gli URL dei file di espansione solo se l'utente ha acquisito la tua app da Google Play.
Oltre all'LVL, devi disporre di un insieme di codice che scarichi i file di espansione tramite una connessione HTTP e li salvi nella posizione corretta nello spazio di archiviazione condiviso del dispositivo. Quando inserisci questa procedura nella tua app, devi prendere in considerazione diversi aspetti:
- Il dispositivo potrebbe non avere spazio sufficiente per i file di espansione, quindi devi controllare prima di avviare il download e avvisare l'utente se lo spazio non è sufficiente.
- I download di file devono avvenire in un servizio in background per evitare di bloccare l'interazione dell'utente e consentire all'utente di uscire dall'app al termine del download.
- Durante la richiesta e il download potrebbero verificarsi diversi errori che devi gestire in modo appropriato.
- La connettività di rete può cambiare durante il download, quindi devi gestire queste modifiche e, se il download viene interrotto, riprendere il download quando possibile.
- Mentre il download avviene in background, devi fornire una notifica che indique l'avanzamento del download, avvisi l'utente al termine e lo rimandi alla tua app quando viene selezionata.
Per semplificare questa operazione, abbiamo creato la Downloader Library, che richiede gli URL dei file di espansione tramite il servizio di licenze, scarica i file di espansione, esegue tutte le attività elencate sopra e consente persino alla tua attività di mettere in pausa e riprendere il download. Aggiungendo la libreria Downloader e alcuni hook di codice alla tua app, quasi tutto il lavoro per scaricare i file di espansione è già codificato per te. Pertanto, per offrire la migliore esperienza utente con il minimo sforzo, ti consigliamo di utilizzare la libreria Downloader per scaricare i file di espansione. Le informazioni riportate nelle sezioni seguenti spiegano come integrare la libreria nella tua app.
Se preferisci sviluppare una soluzione personalizzata per scaricare i file di espansione utilizzando gli URL di Google Play, devi seguire la documentazione relativa alla licenza per le app per eseguire una richiesta di licenza, quindi recuperare i nomi, le dimensioni e gli URL dei file di espansione dagli extra della risposta. Devi utilizzare la classe APKExpansionPolicy
(inclusa nella Libreria di verifica delle licenze) come criterio di licenza, che acquisisce i nomi, le dimensioni e gli URL dei file di espansione dal servizio di licenza.
Informazioni sulla libreria Downloader
Per utilizzare i file di espansione APK con la tua app e offrire la migliore esperienza utente con il minimo sforzo, ti consigliamo di utilizzare la libreria Downloader inclusa nel pacchetto della libreria di espansione APK di Google Play. Questa libreria scarica i file di espansione in un servizio in background, mostra una notifica all'utente con lo stato del download, gestisce la perdita di connettività di rete, riprende il download quando possibile e altro ancora.
Per implementare i download dei file di espansione utilizzando la libreria Downloader, devi solo:
- Estendi una sottoclasse
Service
e una sottoclasseBroadcastReceiver
speciali che richiedono ciascuna solo alcune righe di codice. - Aggiungi alla tua attività principale della logica che controlli se i file di espansione sono già stati scaricati e, in caso contrario, invoca il processo di download e mostra un'interfaccia utente di avanzamento.
- Implementa un'interfaccia di callback con alcuni metodi nell'attività principale che riceve aggiornamenti sull'avanzamento del download.
Le sezioni seguenti spiegano come configurare l'app utilizzando la libreria Downloader.
Preparazione all'utilizzo della libreria Downloader
Per utilizzare la libreria Downloader, devi scaricare due pacchetti da SDK Manager e aggiungere le librerie appropriate alla tua app.
Innanzitutto, apri Android SDK Manager (Strumenti > SDK Manager) e in Aspetto e comportamento > Impostazioni di sistema > SDK Android, seleziona la scheda Strumenti SDK per selezionare e scaricare:
- Pacchetto della libreria di licenze di Google Play
- Pacchetto della libreria di espansione APK di Google Play
Crea un nuovo modulo della libreria per la Libreria di verifica delle licenze e la Libreria del downloader. Per ogni libreria:
- Seleziona File > Nuovo > Nuovo modulo.
- Nella finestra Crea nuovo modulo, seleziona Raccolta di Android e poi Avanti.
- Specifica un nome dell'app/della libreria, ad esempio "Libreria delle licenze di Google Play" e "Libreria del downloader di Google Play", scegli Livello SDK minimo, quindi seleziona Fine.
- Seleziona File > Struttura del progetto.
- Seleziona la scheda Proprietà e in Repository
della raccolta, inserisci la raccolta dalla directory
<sdk>/extras/google/
(play_licensing/
per la raccolta di verifica della licenza oplay_apk_expansion/downloader_library/
per la raccolta del downloader). - Seleziona OK per creare il nuovo modulo.
Nota:la libreria Downloader dipende dalla Libreria di verifica della licenza. Assicurati di aggiungere la libreria di verifica della licenza alle proprietà del progetto della libreria del downloader.
In alternativa, da una riga di comando, aggiorna il progetto in modo da includere le librerie:
- Passa alla directory
<sdk>/tools/
. - Esegui
android update project
con l'opzione--library
per aggiungere al progetto sia la libreria LVL sia la libreria del downloader. Ad esempio:android update project --path ~/Android/MyApp \ --library ~/android_sdk/extras/google/market_licensing \ --library ~/android_sdk/extras/google/market_apk_expansion/downloader_library
Con la Libreria di verifica della licenza e la Libreria Downloader aggiunte alla tua app, potrai integrare rapidamente la possibilità di scaricare i file di espansione da Google Play. Il formato scelto per i file di espansione e il modo in cui li leggi dallo spazio di archiviazione condiviso è un'implementazione separata che devi prendere in considerazione in base alle esigenze della tua app.
Suggerimento: il pacchetto Apk Expansion include un'app di esempio che mostra come utilizzare la libreria Downloader in un'app. L'esempio utilizza una terza libreria disponibile nel pacchetto Apk Expansion chiamata APK Expansion Zip Library. Se prevedi di utilizzare file ZIP per i file di espansione, ti consigliamo di aggiungere anche la libreria ZIP di espansione APK alla tua app. Per saperne di più, consulta la sezione di seguito sull'utilizzo della libreria ZIP di espansione APK.
Dichiarazione delle autorizzazioni utente
Per scaricare i file di espansione, la libreria Downloader richiede diverse autorizzazioni che devi dichiarare nel file manifest dell'app. Sono:
<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>
Nota: per impostazione predefinita, la libreria Downloader richiede il livello API 4, ma la libreria ZIP di espansione APK richiede il livello API 5.
Implementazione del servizio di download
Per eseguire i download in background, la libreria Downloader fornisce la propria sottoclasse Service
chiamata DownloaderService
che devi estendere. Oltre a scaricare i file di espansione per te, DownloaderService
:
- Registra un
BroadcastReceiver
che ascolta le modifiche alla connettività di rete del dispositivo (la trasmissioneCONNECTIVITY_ACTION
) per mettere in pausa il download quando necessario (ad esempio a causa della perdita di connettività) e riprendere il download quando possibile (quando viene acquisita la connettività). - Pianifica un avviso
RTC_WAKEUP
per riprovare a scaricare i file nei casi in cui il servizio viene interrotto. - Crea un
Notification
personalizzato che mostra l'avanzamento del download e eventuali errori o modifiche dello stato. - Consente all'app di mettere in pausa e riprendere manualmente il download.
- Verifica che lo spazio di archiviazione condiviso sia montato e disponibile, che i file non esistano già e che sia disponibile spazio sufficiente, il tutto prima di scaricare i file di espansione. Successivamente, l'utente viene avvisato se una di queste condizioni non è vera.
Devi solo creare un'app che estenda la classe DownloaderService
e sostituisca tre metodi per fornire dettagli specifici dell'app:
getPublicKey()
- Deve restituire una stringa che sia la chiave pubblica RSA con codifica Base64 per il tuo account editore, disponibile nella pagina del profilo su Play Console (vedi Configurazione per la concessione in licenza).
getSALT()
- Deve restituire un array di byte casuali che
Policy
utilizza per creare unObfuscator
. Il sale garantisce che il fileSharedPreferences
offuscato in cui sono salvati i dati di licenza sia univoco e non rilevabile. getAlarmReceiverClassName()
- Deve restituire il nome della classe di
BroadcastReceiver
nella tua app che deve ricevere l'avviso che indica che il download deve essere riavviato (cosa che potrebbe accadere se il servizio di download si arresta in modo imprevisto).
Ad esempio, di seguito è riportata un'implementazione completa di 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(); } }
Avviso: devi aggiornare il valore BASE64_PUBLIC_KEY
in modo che corrisponda alla chiave pubblica appartenente al tuo account publisher. Puoi trovare la chiave in Developer Console, nelle informazioni del tuo profilo. Questo è necessario anche per testare
i download.
Ricordati di dichiarare il servizio nel file manifest:
<app ...> <service android:name=".SampleDownloaderService" /> ... </app>
Implementazione del ricevitore di sveglia
Per monitorare lo stato di avanzamento dei download dei file e riavviare il download se necessario, il DownloaderService
pianifica una sveglia RTC_WAKEUP
che invia un Intent
a un BroadcastReceiver
nella tua app. Devi definire il BroadcastReceiver
per chiamare un'API dalla libreria Downloader che controlli lo stato del download e lo riavvii se necessario.
Devi semplicemente sostituire il metodo onReceive()
per chiamare DownloaderClientMarshaller.startDownloadServiceIfRequired()
.
Ad esempio:
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(); } } }
Tieni presente che questa è la classe di cui devi restituire il nome nel metodo getAlarmReceiverClassName()
del servizio (vedi la sezione precedente).
Ricorda di dichiarare il ricevitore nel file manifest:
<app ...> <receiver android:name=".SampleAlarmReceiver" /> ... </app>
Avvio del download
L'attività principale dell'app (quella avviata dall'icona del programma di avvio) è responsabile della verifica se i file di espansione sono già sul dispositivo e dell'avvio del download, se non lo sono.
L'avvio del download utilizzando la libreria Downloader richiede le seguenti procedure:
- Controlla se i file sono stati scaricati.
La libreria Downloader include alcune API nella classe
Helper
per aiutarti con questa procedura:getExpansionAPKFileName(Context, c, boolean mainFile, int versionCode)
doesFileExist(Context c, String fileName, long fileSize)
Ad esempio, l'app di esempio fornita nel pacchetto di espansione APK chiama il metodo seguente nel metodo
onCreate()
dell'attività per verificare se i file di espansione esistono già sul 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; }
In questo caso, ogni oggetto
XAPKFile
contiene il numero di versione e le dimensioni di un file di espansione conosciuto e un valore booleano che indica se si tratta del file di espansione principale. Per maggiori dettagli, consulta la classeSampleDownloaderActivity
dell'app di esempio.Se questo metodo restituisce false, l'app deve avviare il download.
- Avvia il download chiamando il metodo statico
DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass)
.Il metodo accetta i seguenti parametri:
context
: ilContext
della tua app.notificationClient
: unPendingIntent
per avviare la tua attività principale. Viene utilizzato nelNotification
creato daDownloaderService
per mostrare l'avanzamento del download. Quando l'utente seleziona la notifica, il sistema richiama ilPendingIntent
fornito qui e dovrebbe aprire l'attività che mostra l'avanzamento del download (di solito la stessa attività che ha avviato il download).serviceClass
: l'oggettoClass
per l'implementazione diDownloaderService
, necessario per avviare il servizio e iniziare il download, se necessario.
Il metodo restituisce un numero intero che indica se il download è obbligatorio o meno. I valori possibili sono:
NO_DOWNLOAD_REQUIRED
: viene restituito se i file esistono già o se è già in corso un download.LVL_CHECK_REQUIRED
: viene restituito se è necessaria una verifica della licenza per acquisire gli URL dei file di espansione.DOWNLOAD_REQUIRED
: viene restituito se gli URL dei file di espansione sono già noti, ma non sono stati scaricati.
Il comportamento di
LVL_CHECK_REQUIRED
eDOWNLOAD_REQUIRED
è essenzialmente lo stesso e di solito non devi preoccuparti. Nell'attività principale che chiamastartDownloadServiceIfRequired()
, puoi semplicemente verificare se la risposta èNO_DOWNLOAD_REQUIRED
. Se la risposta è diversa daNO_DOWNLOAD_REQUIRED
, la raccolta Downloader avvia il download e devi aggiornare l'interfaccia utente dell'attività per visualizzare lo stato di avanzamento del download (vedi il passaggio successivo). Se la risposta èNO_DOWNLOAD_REQUIRED
, significa che i file sono disponibili e l'app può essere avviata.Ad esempio:
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 il metodo
startDownloadServiceIfRequired()
restituisce un valore diverso daNO_DOWNLOAD_REQUIRED
, crea un'istanza diIStub
chiamandoDownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService)
.IStub
fornisce un'associazione tra la tua attività e il servizio di download in modo che la tua attività riceva callback sull'avanzamento del download.Per creare un'istanza di
IStub
chiamandoCreateStub()
, devi passare un'implementazione dell'interfacciaIDownloaderClient
e la tua implementazione diDownloaderService
. La sezione successiva sulla ricezione dell'avanzamento del download illustra l'interfacciaIDownloaderClient
, che in genere devi implementare nella classeActivity
per poter aggiornare l'interfaccia utente dell'attività quando cambia lo stato del download.Ti consigliamo di chiamare
CreateStub()
per creare un'istanza diIStub
durante il metodoonCreate()
dell'attività, dopo chestartDownloadServiceIfRequired()
avvia il download.Ad esempio, nell'esempio di codice precedente per
onCreate()
, puoi rispondere al risultatostartDownloadServiceIfRequired()
come segue: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; }
Dopo il ritorno del metodo
onCreate()
, la tua attività riceve una chiamata aonResume()
, dove devi chiamareconnect()
suIStub
, passandogli ilContext
della tua app. Al contrario, devi chiamaredisconnect()
nel callbackonStop()
dell'attività.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(); }
La chiamata a
connect()
suIStub
lega la tua attività aDownloaderService
in modo che riceva callback relativi alle modifiche allo stato del download tramite l'interfacciaIDownloaderClient
.
Avanzamento del download in corso
Per ricevere aggiornamenti sull'avanzamento del download e interagire con DownloaderService
, devi implementare l'interfaccia IDownloaderClient
della Libreria Downloader.
In genere, l'attività che utilizzi per avviare il download deve implementare questa interfaccia per visualizzare lo stato di avanzamento del download e inviare richieste al servizio.
I metodi di interfaccia richiesti per IDownloaderClient
sono:
onServiceConnected(Messenger m)
- Dopo aver creato un'istanza di
IStub
nella tua attività, riceverai una chiamata a questo metodo, che passa un oggettoMessenger
collegato alla tua istanza diDownloaderService
. Per inviare richieste al servizio, ad esempio per mettere in pausa e riprendere i download, devi chiamareDownloaderServiceMarshaller.CreateProxy()
per ricevere l'interfacciaIDownloaderService
collegata al servizio.Un'implementazione consigliata ha il seguente aspetto:
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()); }
Una volta inizializzato l'oggetto
IDownloaderService
, puoi inviare comandi al servizio di download, ad esempio per mettere in pausa e riprendere il download (requestPauseDownload()
erequestContinueDownload()
). onDownloadStateChanged(int newState)
- Il servizio di download chiama questo metodo quando si verifica una modifica dello stato del download, ad esempio quando il download inizia o termina.
Il valore
newState
sarà uno dei diversi valori possibili specificati in da una delle costantiSTATE_*
della classeIDownloaderClient
.Per fornire un messaggio utile agli utenti, puoi richiedere una stringa corrispondente per ogni stato chiamando
Helpers.getDownloaderStringResourceIDFromState()
. Questo restituisce l'ID risorsa di una delle stringhe in bundle con la libreria Downloader. Ad esempio, la stringa "Download in pausa perché sei in roaming" corrisponde aSTATE_PAUSED_ROAMING
. onDownloadProgress(DownloadProgressInfo progress)
- Il servizio di download esegue questa chiamata per fornire un oggetto
DownloadProgressInfo
, che descrive varie informazioni sull'avanzamento del download, tra cui il tempo stimato rimanente, la velocità corrente, l'avanzamento complessivo e il totale, in modo da poter aggiornare l'interfaccia utente dell'avanzamento del download.
Suggerimento:per esempi di questi callback che aggiornano l'interfaccia utente relativa al progresso del download, consulta SampleDownloaderActivity
nell'app di esempio fornita con il pacchetto di espansione APK.
Ecco alcuni metodi pubblici per l'interfaccia IDownloaderService
che potresti trovare utili:
requestPauseDownload()
- Mette in pausa il download.
requestContinueDownload()
- Riprende un download in pausa.
setDownloadFlags(int flags)
- Imposta le preferenze dell'utente per i tipi di rete su cui è consentito scaricare i file. L'implementazione attuale supporta un flag,
FLAGS_DOWNLOAD_OVER_CELLULAR
, ma puoi aggiungerne altri. Per impostazione predefinita, questo flag non è attivato, pertanto l'utente deve essere connesso a una rete Wi-Fi per scaricare i file di espansione. Ti consigliamo di fornire una preferenza utente per attivare i download tramite la rete mobile. In questo caso, puoi chiamare:Kotlin
remoteService = DownloaderServiceMarshaller.CreateProxy(m).apply { ... setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR) }
Java
remoteService .setDownloadFlags(IDownloaderService.FLAGS_DOWNLOAD_OVER_CELLULAR);
Utilizzo di APKExpansionPolicy
Se decidi di creare il tuo servizio di download anziché utilizzare la libreria Downloader di Google Play, devi comunque utilizzare APKExpansionPolicy
fornito nella libreria di verifica della licenza. La classe APKExpansionPolicy
è quasi identica a ServerManagedPolicy
(disponibile nella libreria di verifica della licenza di Google Play), ma include una gestione aggiuntiva per gli extra della risposta del file di espansione APK.
Nota: se utilizzi la libreria Downloader come descritto nella sezione precedente, la libreria esegue tutte le interazioni con APKExpansionPolicy
, quindi non devi utilizzare direttamente questo APKExpansionPolicy
.
La classe include metodi che ti aiutano a ottenere le informazioni necessarie sui file di espansione disponibili:
getExpansionURLCount()
getExpansionURL(int index)
getExpansionFileName(int index)
getExpansionFileSize(int index)
Per saperne di più su come utilizzare APKExpansionPolicy
quando non
utilizzi la Downloader Library, consulta la documentazione relativa all'aggiunta delle licenze alla tua app,
che spiega come implementare un criterio di licenza come questo.
Lettura del file di espansione
Una volta salvati i file di espansione APK sul dispositivo, il modo in cui li leggi dipende dal tipo di file che hai utilizzato. Come descritto nella panoramica, i file di espansione possono essere qualsiasi tipo di file, ma vengono rinominati utilizzando un determinato formato del nome file e vengono salvati in <shared-storage>/Android/obb/<package-name>/
.
Indipendentemente da come leggi i file, devi sempre controllare prima che lo spazio di archiviazione esterno sia disponibile per la lettura. È possibile che l'utente abbia montato lo spazio di archiviazione su un computer tramite USB o che abbia effettivamente rimosso la scheda SD.
Nota:all'avvio dell'app, devi sempre controllare se lo spazio di archiviazione esterno è disponibile e leggibile chiamando getExternalStorageState()
. Restituisce una di diverse stringhe possibili
che rappresentano lo stato dell'archiviazione esterna. Affinché sia leggibile dalla tua app, il valore restituito deve essere MEDIA_MOUNTED
.
Ottenere i nomi dei file
Come descritto nella panoramica, i file di espansione APK vengono salvati utilizzando un formato di nome file specifico:
[main|patch].<expansion-version>.<package-name>.obb
Per ottenere la posizione e i nomi dei file di espansione, devi utilizzare i metodi getExternalStorageDirectory()
e getPackageName()
per creare il percorso dei file.
Ecco un metodo che puoi utilizzare nella tua app per ottenere un array contenente il percorso completo di entrambi i file di espansione:
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; }
Puoi chiamare questo metodo passando l'app Context
e la versione del file di espansione desiderata.
Esistono molti modi per determinare il numero di versione del file di espansione. Un modo semplice è memorizzare la versione in un file SharedPreferences
all'inizio del download, eseguendo una query sul nome del file di espansione con il metodo getExpansionFileName(int index)
della classe APKExpansionPolicy
. Quando vuoi accedere al file di espansione, puoi ottenere il codice di versione leggendo il file SharedPreferences
.
Per saperne di più sulla lettura dallo spazio di archiviazione condiviso, consulta la documentazione relativa allo spazio di archiviazione dei dati.
Utilizzo della libreria ZIP di espansione APK
Il pacchetto di espansione APK di Google Market include una libreria denominata APK Expansion Zip Library (situata in <sdk>/extras/google/google_market_apk_expansion/zip_file/
). Si tratta di una libreria facoltativa che ti aiuta a leggere i file di espansione quando vengono salvati come file ZIP. L'utilizzo di questa libreria ti consente di leggere facilmente le risorse dai file di espansione ZIP come file system virtuale.
La libreria ZIP di espansione APK include le seguenti classi e API:
APKExpansionSupport
- Fornisce alcuni metodi per accedere ai nomi dei file di espansione e ai file ZIP:
getAPKExpansionFiles()
- Lo stesso metodo mostrato sopra che restituisce il percorso completo dei file di espansione.
getAPKExpansionZipFile(Context ctx, int mainVersion, int patchVersion)
- Restituisce un
ZipResourceFile
che rappresenta la somma del file principale e del file patch. In altre parole, se specifichi siamainVersion
siapatchVersion
, viene restituito unZipResourceFile
che fornisce l'accesso in lettura a tutti i dati, con i dati del file patch uniti al file principale.
ZipResourceFile
- Rappresenta un file ZIP nello spazio di archiviazione condiviso ed esegue tutte le operazioni necessarie per fornire un file system virtuale basato sui file ZIP. Puoi ottenere un'istanza utilizzando
APKExpansionSupport.getAPKExpansionZipFile()
o conZipResourceFile
passando il percorso del file di espansione. Questa classe include una serie di metodi utili, ma in genere non è necessario accedere alla maggior parte di questi metodi. Ecco alcuni metodi importanti:getInputStream(String assetPath)
- Fornisce un
InputStream
per leggere un file all'interno del file ZIP.assetPath
deve essere il percorso del file desiderato, rispetto alla radice dei contenuti del file ZIP. getAssetFileDescriptor(String assetPath)
- Fornisce un
AssetFileDescriptor
per un file all'interno del file ZIP.assetPath
deve essere il percorso del file desiderato, rispetto alla radice dei contenuti del file ZIP. Questo è utile per alcune API Android che richiedono unAssetFileDescriptor
, ad esempio alcune APIMediaPlayer
.
APEZProvider
- La maggior parte delle app non ha bisogno di utilizzare questa classe. Questa classe definisce un
ContentProvider
che organizza i dati dei file ZIP tramite un fornitore di contenutiUri
per fornire l'accesso ai file per alcune API Android che prevedono l'accessoUri
ai file multimediali. Ad esempio, è utile se vuoi riprodurre un video conVideoView.setVideoURI()
.
Ignorare la compressione ZIP dei file multimediali
Se utilizzi i file di espansione per archiviare file multimediali, un file ZIP ti consente comunque di utilizzare le chiamate di riproduzione multimediale di Android che forniscono controlli di offset e durata (ad esempio MediaPlayer.setDataSource()
e
SoundPool.load()
). Affinché
questo funzioni, non devi eseguire un'ulteriore compressione dei file multimediali durante la creazione dei pacchetti ZIP. Ad esempio, quando utilizzi lo strumento zip
, devi utilizzare l'opzione -n
per specificare i suffissi dei file che non devono essere compressi:
zip -n .mp4;.ogg main_expansion media_files
Lettura da un file ZIP
Quando utilizzi la libreria ZIP di espansione APK, la lettura di un file dal file ZIP in genere richiede quanto segue:
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);
Il codice riportato sopra fornisce l'accesso a qualsiasi file esistente nel file di espansione principale o nel file di espansione patch leggendo da una mappa unita di tutti i file di entrambi i file. Per fornire il metodo getAPKExpansionFile()
, devi solo indicare l'app android.content.Context
e il numero di versione sia del file di espansione principale sia del file di espansione patch.
Se preferisci leggere da un file di espansione specifico, puoi utilizzare il costruttore ZipResourceFile
con il percorso del file di espansione desiderato:
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);
Per ulteriori informazioni sull'utilizzo di questa libreria per i file di espansione, consulta la classe SampleDownloaderActivity
dell'app di esempio, che include codice aggiuntivo per verificare i file scaricati utilizzando il CRC. Tieni presente che se utilizzi questo esempio come base per la tua implementazione, devi dichiarare le dimensioni in byte dei file di espansione nell'array xAPKS
.
Testare i file di espansione
Prima di pubblicare l'app, devi testare due cose: la lettura dei file di espansione e il loro download.
Test delle letture dei file
Prima di caricare l'app su Google Play, devi testare la capacità dell'app di leggere i file dallo spazio di archiviazione condiviso. Devi solo aggiungere i file alla posizione appropriata nello spazio di archiviazione condiviso del dispositivo e avviare l'app:
- Sul tuo dispositivo, crea la directory appropriata nello spazio di archiviazione condiviso in cui Google Play salverà i file.
Ad esempio, se il nome del pacchetto è
com.example.android
, devi creare la directoryAndroid/obb/com.example.android/
nello spazio di archiviazione condiviso. Collega il dispositivo di test al computer per montare lo spazio di archiviazione condiviso e creare manualmente questa directory. - Aggiungi manualmente i file di espansione a questa directory. Assicurati di rinominare i file in modo che corrispondano al formato del nome del file che verrà utilizzato da Google Play.
Ad esempio, indipendentemente dal tipo di file, il file di espansione principale per l'app
com.example.android
deve esseremain.0300110.com.example.android.obb
. Il codice di versione può essere qualsiasi valore. Ricorda:- Il file di espansione principale inizia sempre con
main
e il file patch inizia conpatch
. - Il nome del pacchetto corrisponde sempre a quello dell'APK a cui è allegato il file su Google Play.
- Il file di espansione principale inizia sempre con
- Ora che i file di espansione sono sul dispositivo, puoi installare ed eseguire l'app per testarli.
Ecco alcuni promemoria per la gestione dei file di espansione:
- Non eliminare o rinominare i file di espansione
.obb
(anche se estrai i dati in una posizione diversa). In questo modo, Google Play (o la tua app stessa) scaricherà ripetutamente il file di espansione. - Non salvare altri dati nella directory
obb/
. Se devi decomprimere alcuni dati, salvali nella posizione specificata dagetExternalFilesDir()
.
Testare i download di file
Poiché a volte l'app deve scaricare manualmente i file di espansione alla prima apertura, è importante testare questa procedura per assicurarsi che l'app possa eseguire query sugli URL, scaricare i file e salvarli sul dispositivo.
Per testare l'implementazione della procedura di download manuale della tua app, puoi pubblicarla nel canale di test interno, in modo che sia disponibile solo per i tester autorizzati. Se tutto funziona come previsto, l'app dovrebbe iniziare a scaricare i file di espansione non appena viene avviata l'attività principale.
Nota: in precedenza potevi testare un'app caricando una versione "bozza" non pubblicata. Questa funzionalità non è più supportata. Devi invece pubblicarla in un canale di test interno, chiuso o aperto. Per ulteriori informazioni, consulta Le app di bozza non sono più supportate.
Aggiornamento dell'app
Uno dei grandi vantaggi dell'utilizzo dei file di espansione su Google Play è la possibilità di aggiornare l'app senza scaricare di nuovo tutti gli asset originali. Poiché Google Play consente di fornire due file di espansione con ogni APK, puoi utilizzare il secondo file come "patch" che fornisce aggiornamenti e nuovi asset. In questo modo, eviterai di dover scaricare di nuovo il file di espansione principale, che potrebbe essere di grandi dimensioni e costoso per gli utenti.
Il file di espansione patch è tecnicamente uguale al file di espansione principale e né il sistema Android né Google Play eseguono patch effettive tra i file di espansione principale e patch. Il codice dell'app deve eseguire autonomamente le patch necessarie.
Se utilizzi file ZIP come file di espansione, la libreria Zip Expansion inclusa nel pacchetto APK Expansion consente di unire il file patch con il file di espansione principale.
Nota: anche se devi apportare modifiche solo al file di espansione della patch, devi comunque aggiornare l'APK affinché Google Play possa eseguire un aggiornamento.
Se non sono necessarie modifiche al codice dell'app, devi semplicemente aggiornare versionCode
nel file manifest.
Se non modifichi il file di espansione principale associato all'APK in Play Console, gli utenti che hanno installato la tua app in precedenza non scaricano il file di espansione principale. Gli utenti esistenti ricevono solo l'APK aggiornato e il nuovo file di espansione patch (mantenendo il file di espansione principale precedente).
Di seguito sono riportati alcuni problemi da tenere presenti in merito agli aggiornamenti dei file di espansione:
- Per la tua app possono essere presenti solo due file di espansione alla volta. Un file di espansione principale e un file di espansione patch. Durante l'aggiornamento di un file, Google Play elimina la versione precedente (e lo stesso deve fare la tua app quando esegui aggiornamenti manuali).
- Quando aggiungi un file di espansione patch, il sistema Android non applica effettivamente la patch all'app o al file di espansione principale. Devi progettare l'app in modo che supporti i dati delle patch. Tuttavia, il pacchetto Apk Expansion include una libreria per l'utilizzo dei file ZIP come file di espansione, che unisce i dati del file patch al file di espansione principale in modo da poter leggere facilmente tutti i dati del file di espansione.