File di espansione APK

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 grafica ad alta fedeltà, file multimediali o altre risorse di grandi dimensioni. In precedenza, se le dimensioni compresse del download dell'app superavano i 100 MB, dovevi ospitare e scaricare le risorse aggiuntive autonomamente quando l'utente apriva l'app. L'hosting e la pubblicazione dei file aggiuntivi possono essere costosi e l'esperienza utente spesso non è ideale. Per semplificare questo processo e renderlo più piacevole per gli utenti, Google Play ti consente di allegare due file di espansione di grandi dimensioni che integrano l'APK.

Google Play ospita i file di espansione della tua app e li pubblica sul dispositivo senza costi per te. I file di espansione vengono salvati nell'archivio condiviso del dispositivo (la scheda SD o la partizione montabile USB, nota anche come archivio "esterno") a cui l'app può accedere. Sulla maggior parte dei dispositivi, Google Play scarica i file di espansione contemporaneamente all'APK, in modo che l'app abbia tutto ciò di cui ha bisogno quando l'utente la apre per la prima volta. In alcuni casi, tuttavia, l'app deve scaricare i file da Google Play all'avvio.

Se vuoi evitare di utilizzare i file di espansione e le dimensioni compresse del download dell'app sono superiori a 100 MB, devi caricare l'app utilizzando Android App Bundles, che consente dimensioni compresse del download fino a 500 MB. Inoltre, poiché l'utilizzo degli app bundle rimanda la generazione e la firma degli APK a Google Play, gli utenti scaricano APK ottimizzati con solo il codice e le risorse necessari per eseguire l'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 tu scelga, ma ti consigliamo di utilizzare un file compresso per risparmiare larghezza di banda durante il download. A livello concettuale, ogni file di espansione svolge 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 e destinato a piccoli aggiornamenti del file di espansione principale.

Anche se puoi utilizzare i due file di espansione nel modo che preferisci, ti consigliamo di utilizzare il file di espansione principale per distribuire le risorse principali e di aggiornarlo raramente, se non mai. Il file di espansione patch deve essere più piccolo e fungere da "vettore di patch", venendo aggiornato a ogni release principale o in base alle necessità.

Tuttavia, anche se l'aggiornamento dell'app richiede solo un nuovo file di espansione patch, devi comunque caricare un nuovo APK con un versionCode aggiornato nel manifest. (Play Console non 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 ogni file nel modo che preferisci.

Formato del nome file

Ogni file di espansione che carichi può essere in qualsiasi formato tu scelga (ZIP, PDF, MP4 e così via). Puoi anche utilizzare lo strumento JOBB per incapsulare e criptare un insieme di file di risorse e le 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 o patch
Specifica se il file è il file di espansione principale o patch. Può esserci un solo file principale e un solo file patch per ogni APK.
<expansion-version>
Si tratta di un numero intero che corrisponde al codice di versione dell'APK a cui l'espansione è associata per la prima volta (corrisponde al valore android:versionCode dell'app).

L'aggettivo"primo" è enfatizzato perché, sebbene Play Console ti consenta di riutilizzare un file di espansione caricato con un nuovo APK, il nome del file di espansione non cambia: mantiene la versione applicata al momento del 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 il nome del pacchetto sia com.example.app. Se carichi un file di espansione principale, il file viene rinominato in:

main.314159.com.example.app.obb

Località di 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. Nel caso in cui l'app debba eseguire il download da Google Play, devi salvare i file nella stessa posizione.

Il metodo getObbDir() restituisce la posizione specifica dei file di espansione nel seguente formato:

<shared-storage>/Android/obb/<package-name>/

Per ogni app, in questa directory non sono mai presenti più di due file di espansione. Uno è il file di espansione principale e l'altro è il file di espansione patch (se necessario). Le versioni precedenti vengono sovrascritte quando aggiorni l'app con nuovi file di espansione. A partire da Android 4.4 (livello API 19), le app possono leggere i file di espansione OBB senza l'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 manifest dell'app e chiedere l'autorizzazione in fase di runtime nel seguente modo:

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Per Android 6 e versioni successive, l'autorizzazione di accesso alla memoria esterna 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 chiedere l'autorizzazione di archiviazione esterna:

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 espansione OBB in un secondo momento e non salvare i dati decompressi nella stessa directory. Salva i file decompressi nella directory specificata da getExternalFilesDir(). Tuttavia, se possibile, è meglio utilizzare un formato di file di espansione che consenta di leggere direttamente dal file anziché richiedere di decomprimere 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 comprimi i file multimediali in un file ZIP, puoi utilizzare le chiamate di riproduzione multimediale sui file con controlli di offset e lunghezza (ad esempio MediaPlayer.setDataSource() e SoundPool.load()) senza dover decomprimere il file ZIP. Affinché questa operazione 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

La maggior parte delle volte, Google Play scarica e salva i file di espansione contemporaneamente all'APK sul dispositivo. Tuttavia, in alcuni casi Google Play non può scaricare i file di espansione oppure 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 all'avvio dell'attività principale, utilizzando un URL fornito da Google Play.

Il processo di download a livello generale è il seguente:

  1. L'utente sceglie di installare la tua app da Google Play.
  2. Se Google Play è in grado di scaricare i file di espansione (come avviene 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.

  3. Quando l'utente avvia l'app, questa deve verificare se i file di espansione sono già salvati sul dispositivo.
    1. In caso affermativo, la tua app è pronta.
    2. 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 Google Play utilizzando il servizio di 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, scarica i file e salvali 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à presenti sul dispositivo all'avvio dell'app. Come descritto nella sezione seguente sul 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 da parte tua.

Elenco di controllo per lo sviluppo

Ecco un riepilogo delle attività da svolgere per utilizzare i file di espansione con la tua app:

  1. Innanzitutto, determina se le dimensioni compresse del download dell'app devono essere superiori a 100 MB. Lo spazio è prezioso e devi ridurre al minimo le dimensioni totali del download. Se la tua app utilizza più di 100 MB per fornire più versioni degli asset grafici per più densità dello schermo, valuta la possibilità di pubblicare più APK in cui ogni APK contiene solo gli asset necessari per gli schermi a cui è destinato. Per ottenere risultati ottimali durante la pubblicazione su Google Play, carica un Android App Bundle, che include tutto il codice e le risorse compilati della tua app, ma rimanda la generazione e la firma dell'APK a Google Play.
  2. Determina quali risorse dell'app separare dall'APK e comprimile 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.

  3. Sviluppa l'app in modo che utilizzi le risorse dei file di espansione nella posizione di archiviazione condivisa 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.

  4. Aggiungi alla principale attività della tua app una logica che controlli se i file di espansione sono presenti sul dispositivo all'avvio. Se i file non sono sul dispositivo, utilizza il servizio di gestione licenze app di Google Play per richiedere gli URL dei file di espansione, quindi scaricali e salvali.

    Per ridurre notevolmente la quantità di codice da scrivere e garantire una buona esperienza utente durante il download, ti consigliamo di utilizzare la libreria Downloader per implementare il comportamento di download.

    Se crei un tuo servizio di download anziché utilizzare la libreria, tieni presente che non devi modificare il nome dei file di espansione e devi salvarli nella posizione di archiviazione corretta.

Una volta terminato lo sviluppo dell'app, segui la guida per testare i file di espansione.

Regole e limitazioni

L'aggiunta di file di espansione APK è una funzionalità disponibile quando carichi l'app utilizzando Play Console. Quando carichi la tua app per la prima volta o aggiorni un'app che utilizza file di espansione, devi essere a conoscenza delle seguenti regole e limitazioni:

  1. Ogni file di espansione non può superare i 2 GB.
  2. 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.
  3. Quando esegui il download dall'app, l'URL che Google Play fornisce per ogni file è univoco per ogni download e ogni URL scade poco dopo essere stato fornito alla tua app.
  4. 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: mantiene la versione ricevuta dall'APK a cui il file era originariamente associato.
  5. 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.
  6. Non puoi rilasciare un aggiornamento della tua 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 magari anche versionName).
  7. Non salvare altri dati nella directory obb/. Se devi decomprimere alcuni dati, salvali nella posizione specificata da getExternalFilesDir().
  8. Non eliminare o rinominare il file di espansione .obb (a meno che tu non stia eseguendo un aggiornamento). In questo modo, Google Play (o la tua app) scaricherà ripetutamente il file di espansione.
  9. Quando aggiorni manualmente un file di espansione, 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 quando l'app viene avviata per la prima volta. Tuttavia, in alcuni casi l'app deve scaricare i file di espansione autonomamente richiedendoli da un URL fornito in una risposta dal servizio di licenze app di Google Play.

La logica di base necessaria per scaricare i file di espansione è la seguente:

  1. Quando l'app viene avviata, cerca i file di espansione nella posizione di archiviazione condivisa (nella directory Android/obb/<package-name>/).
    1. Se i file di espansione sono presenti, non devi fare altro e la tua app può continuare.
    2. Se i file di espansione non sono presenti:
      1. Esegui una richiesta utilizzando la licenza app di Google Play per ottenere i nomi, le dimensioni e gli URL dei file di espansione della tua app.
      2. Utilizza gli URL forniti da Google Play per scaricare i file di espansione e salvarli. 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 che Google Play fornisce per i tuoi file di espansione è univoco per ogni download e scade poco dopo essere stato fornito alla tua app.

Se la tua app è senza costi (non a pagamento), probabilmente non hai utilizzato il servizio di gestione licenze app. È progettata principalmente per consentirti di applicare le norme sulle licenze per la tua app e assicurarti che l'utente abbia il diritto di utilizzare la tua app (l'abbia pagata legittimamente su Google Play). Per facilitare la funzionalità dei file di espansione, il servizio di gestione delle licenze è 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 libreria di verifica delle licenze (LVL) per utilizzare i file di espansione APK. Naturalmente, se la tua app è senza costi, non devi applicare la verifica della licenza. Ti serve solo la libreria per eseguire 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 alla LVL, hai bisogno di un insieme di codici che scarichino i file di espansione tramite una connessione HTTP e li salvino nella posizione corretta della memoria condivisa del dispositivo. Quando integri questa procedura nella tua app, devi tenere in considerazione diversi aspetti:

  • Il dispositivo potrebbe non avere spazio sufficiente per i file di espansione, quindi devi controllare prima di iniziare 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 consentirgli di uscire dall'app durante il completamento 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 interrotto, riprendere il download quando possibile.
  • Mentre il download viene eseguito in background, devi fornire una notifica che indichi l'avanzamento del download, avvisi l'utente al termine e lo riporti alla tua app quando viene selezionata.

Per semplificare questo lavoro, abbiamo creato la libreria Downloader, che richiede gli URL dei file di espansione tramite il servizio di gestione delle licenze, scarica i file di espansione, esegue tutte le attività elencate sopra e consente persino di mettere in pausa e riprendere l'attività di download. Se aggiungi la libreria Downloader e alcuni hook di codice alla tua app, quasi tutto il lavoro di download dei file di espansione è già codificato per te. Pertanto, per offrire la migliore esperienza utente con il minimo sforzo da parte tua, ti consigliamo di utilizzare la libreria Downloader per scaricare i file di espansione. Le informazioni 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 licensing dell'app per eseguire una richiesta di licenza, quindi recuperare i nomi, le dimensioni e gli URL dei file di espansione dalle informazioni 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 licenze.

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 da parte tua, 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 sottoclasse BroadcastReceiver che richiedono solo poche righe di codice.
  • Aggiungi una logica all'attività principale che verifichi se i file di espansione sono già stati scaricati e, in caso contrario, richiami la procedura di download e visualizzi un'interfaccia utente di avanzamento.
  • Implementa un'interfaccia di callback con alcuni metodi nell'attività principale che riceve aggiornamenti sullo stato di 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.

Per prima cosa, apri SDK Manager di Android (Tools > SDK Manager) e, in Appearance & Behavior > System Settings > SDK Android, seleziona la scheda Strumenti SDK per selezionare e scaricare:

  • Pacchetto della libreria di gestione delle 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 Downloader. Per ogni biblioteca:

  1. Seleziona File > Nuovo > Nuovo modulo.
  2. Nella finestra Crea nuovo modulo, seleziona Libreria Android, quindi seleziona Avanti.
  3. Specifica un nome di app/libreria, ad esempio "Libreria di licenze Google Play" e "Libreria di download di Google Play", scegli Livello SDK minimo, quindi seleziona Fine.
  4. Seleziona File > Struttura del progetto.
  5. Seleziona la scheda Proprietà e, in Repository raccolta, inserisci la raccolta dalla directory <sdk>/extras/google/ (play_licensing/ per la libreria di verifica delle licenze o play_apk_expansion/downloader_library/ per la libreria Downloader).
  6. Seleziona Ok per creare il nuovo modulo.

Nota:la libreria Downloader dipende dalla libreria di verifica delle licenze. Assicurati di aggiungere la libreria di verifica delle licenze alle proprietà del progetto della libreria Downloader.

In alternativa, da una riga di comando, aggiorna il progetto in modo da includere le librerie:

  1. Passa alla directory <sdk>/tools/.
  2. Esegui android update project con l'opzione --library per aggiungere sia la libreria LVL che quella di download al tuo progetto. 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 l'aggiunta della libreria di verifica delle licenze e della libreria Downloader alla tua app, potrai integrare rapidamente la possibilità di scaricare file di espansione da Google Play. Il formato che scegli per i file di espansione e il modo in cui li leggi dallo spazio di archiviazione condiviso sono un'implementazione separata che devi prendere in considerazione in base alle esigenze della tua app.

Suggerimento: il pacchetto di espansione APK include un'app di esempio che mostra come utilizzare la libreria Downloader in un'app. L'esempio utilizza una libreria di terze parti disponibile nel pacchetto di espansione APK chiamata libreria di compressione APK. Se prevedi di utilizzare file ZIP per i file di espansione, ti consigliamo di aggiungere anche la libreria di compressione ZIP di espansione APK alla tua app. Per ulteriori informazioni, consulta la sezione seguente sull'utilizzo della libreria di compressione 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, mentre la libreria APK Expansion Zip richiede il livello API 5.

Implementazione del servizio di download

Per eseguire i download in background, la libreria Downloader fornisce una propria sottoclasse Service chiamata DownloaderService che devi estendere. Oltre a scaricare i file di espansione, DownloaderService:

  • Registra un BroadcastReceiver che ascolta le modifiche alla connettività di rete del dispositivo (la trasmissione CONNECTIVITY_ACTION) per mettere in pausa il download quando necessario (ad esempio a causa della perdita di connettività) e riprenderlo quando possibile (viene acquisita la connettività).
  • Pianifica un allarme RTC_WAKEUP per riprovare a scaricare i casi in cui il servizio viene interrotto.
  • Crea un Notification personalizzato che mostra l'avanzamento del download e eventuali errori o modifiche di 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 ci sia spazio sufficiente, il tutto prima di scaricare i file di espansione. e avvisa l'utente se una di queste condizioni non è vera.

Tutto ciò che devi fare è creare una classe nella tua app che estenda la classe DownloaderService ed eseguire l'override di tre metodi per fornire dettagli sull'app specifici:

getPublicKey()
Deve restituire una stringa che è la chiave pubblica RSA codificata in base64 per il tuo account publisher, disponibile nella pagina del profilo di Play Console (vedi Configurazione per la licenza).
getSALT()
Deve restituire un array di byte casuali che la licenza Policy utilizza per creare un Obfuscator. Il sale garantisce che il file SharedPreferences offuscato in cui vengono salvati i dati di licenza sia unico e non rilevabile.
getAlarmReceiverClassName()
Deve restituire il nome della classe di BroadcastReceiver nella tua app che deve ricevere l'allarme che indica che il download deve essere riavviato (il che potrebbe accadere se il servizio di download si interrompe in modo imprevisto).

Ad esempio, ecco 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 di BASE64_PUBLIC_KEY in modo che corrisponda alla chiave pubblica del tuo account editore. Puoi trovare la chiave in Developer Console nella sezione delle informazioni del profilo. Questa operazione è necessaria anche durante il test dei download.

Ricordati di dichiarare il servizio nel file manifest:

<app ...>
    <service android:name=".SampleDownloaderService" />
    ...
</app>

Implementazione del ricevitore di allarmi

Per monitorare l'avanzamento dei download dei file e riavviarli se necessario, l'app DownloaderService pianifica una sveglia RTC_WAKEUP che invia un Intent a un BroadcastReceiver nella tua app. Devi definire l'BroadcastReceiver per chiamare un'API dalla libreria Downloader che controlla lo stato del download e lo riavvia se necessario.

Devi semplicemente eseguire l'override del 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 per cui devi restituire il nome nel metodo getAlarmReceiverClassName() del tuo 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 della tua app (quella avviata dall'icona in Avvio applicazioni) è responsabile della verifica della presenza dei file di espansione sul dispositivo e dell'avvio del download in caso contrario.

L'avvio del download utilizzando la libreria Downloader richiede le seguenti procedure:

  1. Controlla se i file sono stati scaricati.

    La libreria Downloader include alcune API nella classe Helper per facilitare questo processo:

    • 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 seguente metodo 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 del file di un file di espansione noto e un valore booleano che indica se si tratta del file di espansione principale. Per maggiori dettagli, consulta la classe SampleDownloaderActivity dell'app di esempio.

    Se questo metodo restituisce false, l'app deve avviare il download.

  2. Avvia il download chiamando il metodo statico DownloaderClientMarshaller.startDownloadServiceIfRequired(Context c, PendingIntent notificationClient, Class<?> serviceClass).

    Il metodo accetta i seguenti parametri:

    • context: il Context della tua app.
    • notificationClient: un PendingIntent per avviare l'attività principale. Viene utilizzato in Notification creato da DownloaderService per mostrare l'avanzamento del download. Quando l'utente seleziona la notifica, il sistema richiama l'PendingIntent che fornisci qui e dovrebbe aprire l'attività che mostra l'avanzamento del download (di solito la stessa attività che ha avviato il download).
    • serviceClass: l'oggetto Class per l'implementazione di DownloaderService, necessario per avviare il servizio e iniziare il download, se necessario.

    Il metodo restituisce un numero intero che indica se il download è necessario o meno. I valori possibili sono:

    • NO_DOWNLOAD_REQUIRED: restituito se i file esistono già o se è già in corso un download.
    • LVL_CHECK_REQUIRED: restituito se è necessaria una verifica della licenza per acquisire gli URL dei file di espansione.
    • DOWNLOAD_REQUIRED: restituito se gli URL dei file di espansione sono già noti, ma non sono stati scaricati.

    Il comportamento di LVL_CHECK_REQUIRED e DOWNLOAD_REQUIRED è essenzialmente lo stesso e in genere non devi preoccuparti. Nell'attività principale che chiama startDownloadServiceIfRequired(), puoi semplicemente controllare se la risposta è NO_DOWNLOAD_REQUIRED. Se la risposta è diversa da NO_DOWNLOAD_REQUIRED, la libreria Downloader avvia il download e devi aggiornare l'interfaccia utente dell'attività per visualizzare l'avanzamento del download (vedi il passaggio successivo). Se la risposta è NO_DOWNLOAD_REQUIRED, i file sono disponibili e la tua 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
    }
  3. Quando il metodo startDownloadServiceIfRequired() restituisce un valore diverso da NO_DOWNLOAD_REQUIRED, crea un'istanza di IStub chiamando DownloaderClientMarshaller.CreateStub(IDownloaderClient client, Class<?> downloaderService). IStub fornisce un binding tra la tua attività e il servizio di download in modo che la tua attività riceva callback sullo stato di avanzamento del download.

    Per creare un'istanza di IStub chiamando CreateStub(), devi trasmettergli un'implementazione dell'interfaccia IDownloaderClient e la tua implementazione di DownloaderService. La sezione successiva su Ricezione dell'avanzamento del download descrive l'interfaccia IDownloaderClient, che in genere devi implementare nella classe Activity per poter aggiornare la UI dell'attività quando lo stato del download cambia.

    Ti consigliamo di chiamare CreateStub() per creare un'istanza di IStub durante il metodo onCreate() dell'attività, dopo che startDownloadServiceIfRequired() avvia il download.

    Ad esempio, nel campione di codice precedente per onCreate(), puoi rispondere al risultato startDownloadServiceIfRequired() in questo modo:

    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 la restituzione del metodo onCreate(), la tua attività riceve una chiamata a onResume(), dove devi chiamare connect() su IStub, passando l'Context della tua app. Al contrario, devi chiamare disconnect() nel callback onStop() 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() su IStub associa la tua attività a DownloaderService in modo che la tua attività riceva callback relativi alle modifiche allo stato del download tramite l'interfaccia IDownloaderClient.

Ricezione dell'avanzamento del download

Per ricevere aggiornamenti sullo stato di 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 l'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 nell'attività, riceverai una chiamata a questo metodo, che passa un oggetto Messenger connesso alla tua istanza di DownloaderService. Per inviare richieste al servizio, ad esempio per mettere in pausa e riprendere i download, devi chiamare DownloaderServiceMarshaller.CreateProxy() per ricevere l'interfaccia IDownloaderService connessa 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());
}

Con l'oggetto IDownloaderService inizializzato, puoi inviare comandi al servizio di download, ad esempio per mettere in pausa e riprendere il download (requestPauseDownload() e requestContinueDownload()).

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 di newState sarà uno dei vari valori possibili specificati da una delle costanti STATE_* della classe IDownloaderClient.

Per fornire un messaggio utile ai tuoi utenti, puoi richiedere una stringa corrispondente per ogni stato chiamando Helpers.getDownloaderStringResourceIDFromState(). Questo restituisce l'ID risorsa per una delle stringhe incluse nella libreria Downloader. Ad esempio, la stringa "Download in pausa perché sei in roaming" corrisponde a STATE_PAUSED_ROAMING.

onDownloadProgress(DownloadProgressInfo progress)
Il servizio di download chiama questo metodo per fornire un oggetto DownloadProgressInfo, che descrive varie informazioni sull'avanzamento del download, tra cui il tempo stimato rimanente, la velocità attuale, l'avanzamento complessivo e il totale, in modo da poter aggiornare la UI di avanzamento del download.

Suggerimento:per esempi di questi callback che aggiornano l'interfaccia utente di avanzamento del download, consulta SampleDownloaderActivity nell'app di esempio fornita con il pacchetto di espansione APK.

Alcuni metodi pubblici per l'interfaccia IDownloaderService che potresti trovare utili sono:

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 è possibile scaricare i file. L'implementazione attuale supporta un flag, FLAGS_DOWNLOAD_OVER_CELLULAR, ma puoi aggiungerne altri. Per impostazione predefinita, questo flag non è abilitato, quindi l'utente deve essere connesso alla rete Wi-Fi per scaricare i file di espansione. Potresti voler fornire una preferenza utente per attivare i download tramite la rete cellulare. 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 un tuo servizio di download anziché utilizzare la libreria Downloader di Google Play, devi comunque utilizzare APKExpansionPolicy fornito nella libreria di verifica delle licenze. La classe APKExpansionPolicy è quasi identica a ServerManagedPolicy (disponibile nella libreria di verifica delle licenze di Google Play), ma include una gestione aggiuntiva per le informazioni extra della risposta del file di espansione APK.

Nota:se utilizzi la libreria Downloader come descritto nella sezione precedente, la libreria esegue tutta l'interazione con APKExpansionPolicy, quindi non devi utilizzare questa classe direttamente.

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 libreria Downloader, consulta la documentazione relativa all'aggiunta di licenze alla tua app, che spiega come implementare una norma sulle licenze come questa.

Lettura del file di espansione

Una volta salvati i file di espansione APK sul dispositivo, la modalità di lettura dei file dipende dal tipo di file che hai utilizzato. Come descritto nella panoramica, i file di espansione possono essere di qualsiasi tipo, ma vengono rinominati utilizzando un particolare formato del nome file e vengono salvati in <shared-storage>/Android/obb/<package-name>/.

Indipendentemente da come leggi i file, devi sempre verificare che l'archiviazione esterna sia disponibile per la lettura. È possibile che l'utente abbia montato lo spazio di archiviazione su un computer tramite USB o abbia effettivamente rimosso la scheda SD.

Nota:all'avvio dell'app, devi sempre verificare se lo spazio di archiviazione esterno è disponibile e leggibile chiamando getExternalStorageState(). Restituisce una delle varie 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 specifico per il nome file:

[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'Context della tua app e la versione del file di espansione che preferisci.

Esistono molti modi per determinare il numero di versione del file di espansione. Un modo semplice è salvare la versione in un file SharedPreferences quando inizia il download, eseguendo una query sul nome del file di espansione con il metodo getExpansionFileName(int index) della classe APKExpansionPolicy. Puoi quindi ottenere il codice versione leggendo il file SharedPreferences quando vuoi accedere al file di espansione.

Per saperne di più sulla lettura dall'archivio condiviso, consulta la documentazione Data Storage.

Utilizzo della libreria ZIP di espansione APK

Il pacchetto di espansione APK di Google Market include una libreria chiamata Libreria zip di espansione APK (che si trova 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. ovvero, se specifichi sia mainVersion sia patchVersion, viene restituito un ZipResourceFile che fornisce l'accesso in lettura a tutti i dati, con i dati del file patch uniti a quelli del file principale.
ZipResourceFile
Rappresenta un file ZIP nello spazio di archiviazione condiviso ed esegue tutto il lavoro per fornire un file system virtuale basato sui file ZIP. Puoi ottenere un'istanza utilizzando APKExpansionSupport.getAPKExpansionZipFile() o ZipResourceFile 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. 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, relativo 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, relativo alla radice dei contenuti del file ZIP. Ciò è utile per alcune API Android che richiedono un AssetFileDescriptor, ad esempio alcune API MediaPlayer.
APEZProvider
La maggior parte delle app non ha bisogno di utilizzare questa classe. Questa classe definisce un ContentProvider che esegue il marshalling dei dati dai file ZIP tramite un content provider Uri per fornire l'accesso ai file per determinate API Android che prevedono l'accesso Uri ai file multimediali. Ad esempio, questa funzionalità è utile se vuoi riprodurre un video con VideoView.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 Android che forniscono controlli di offset e lunghezza (ad esempio MediaPlayer.setDataSource() e SoundPool.load()). Affinché ciò funzioni, non devi eseguire una compressione aggiuntiva 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 richiede in genere 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 presente 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'android.content.Context della tua app e il numero di versione sia per il file di espansione principale sia per il 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 controllo ciclico di ridondanza (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 aspetti: la lettura dei file di espansione e il download dei file.

Test delle letture dei file

Prima di caricare l'app su Google Play, devi testare la sua capacità di leggere i file dallo spazio di archiviazione condiviso. Tutto quello che devi fare è aggiungere i file nella posizione appropriata nell'archivio condiviso del dispositivo e avviare l'app:

  1. Sul dispositivo, crea la directory appropriata nello spazio di archiviazione condiviso in cui Google Play salverà i tuoi file.

    Ad esempio, se il nome del pacchetto è com.example.android, devi creare la directory Android/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.)

  2. Aggiungi manualmente i file di espansione a questa directory. Assicurati di rinominare i file in modo che corrispondano al formato del nome file che Google Play utilizzerà.

    Ad esempio, indipendentemente dal tipo di file, il file di espansione principale per l'app com.example.android deve essere main.0300110.com.example.android.obb. Il codice versione può essere qualsiasi valore. Ricorda:

    • Il file di espansione principale inizia sempre con main e il file patch inizia con patch.
    • Il nome del pacchetto corrisponde sempre a quello dell'APK a cui è allegato il file su Google Play.
  3. Ora che i file di espansione si trovano sul dispositivo, puoi installare ed eseguire l'app per testare i file di espansione.

Ecco alcuni promemoria sulla gestione dei file di espansione:

  • Non eliminare o rinominare i file di espansione .obb (anche se decomprimi i dati in una posizione diversa). In questo modo, Google Play (o la tua app) scaricherà ripetutamente il file di espansione.
  • Non salvare altri dati nella directory obb/. Se devi decomprimere alcuni dati, salvali nella posizione specificata da getExternalFilesDir().

Test dei download di file

Poiché a volte l'app deve scaricare manualmente i file di espansione quando viene aperta per la prima volta, è importante testare questa procedura per assicurarsi che l'app possa eseguire correttamente query per gli 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 era possibile 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, vedi Le app 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 nuovamente tutti gli asset originali. Poiché Google Play ti consente di fornire due file di espansione con ogni APK, puoi utilizzare il secondo file come "patch" che fornisce aggiornamenti e nuove risorse. In questo modo si evita la necessità di scaricare nuovamente 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 l'applicazione di patch 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 APK Expansion Zip inclusa nel pacchetto di espansione APK include la possibilità 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 esegua un aggiornamento. Se non sono necessarie modifiche al codice nell'app, devi semplicemente aggiornare versionCode nel manifest.

Se non modifichi il file di espansione principale associato all'APK in Play Console, gli utenti che hanno installato in precedenza la tua app non scaricheranno il file di espansione principale. Gli utenti esistenti ricevono solo l'APK aggiornato e il nuovo file di espansione patch (mantenendo il precedente file di espansione principale).

Ecco alcuni problemi da tenere presente in merito agli aggiornamenti dei file di espansione:

  • Possono esserci solo due file di espansione per la tua app alla volta. Un file di espansione principale e un file di espansione patch. Durante un aggiornamento di un file, Google Play elimina la versione precedente (e così deve fare la tua app quando esegue aggiornamenti manuali).
  • Quando aggiungi un file di espansione patch, il sistema Android non applica patch alla tua app o al file di espansione principale. Devi progettare la tua app in modo che supporti i dati della patch. Tuttavia, il pacchetto di espansione APK include una libreria per l'utilizzo di file ZIP come file di espansione, che unisce i dati del file patch nel file di espansione principale in modo da poter leggere facilmente tutti i dati del file di espansione.