Adotta le ottimizzazioni in modo incrementale

Per impostazione predefinita, R8 esegue molte ottimizzazioni per migliorare le prestazioni e le dimensioni, ma è possibile che le ottimizzazioni non funzionino immediatamente per la tua app. Se attivi R8 (o abiliti la modalità completa) in un'app di grandi dimensioni per la prima volta, prova ad adottare le ottimizzazioni in modo incrementale: disattiva temporaneamente l'offuscamento e abilita R8 per porzioni di codice alla volta, anziché per tutto il codice dell'app. Ti consigliamo di adottare questo approccio incrementale durante lo sviluppo locale, ma puoi utilizzarlo anche durante i test di controllo qualità interni o persino in produzione come implementazione graduale. I passaggi esatti da seguire dipendono dalla sequenza temporale desiderata e dalla tua fiducia nella copertura dei test pre-release.

Limitare le ottimizzazioni

R8 esegue molti tipi di ottimizzazioni, tra cui la rimozione del codice, la riscrittura del codice e la rimozione delle risorse. Di seguito sono riportate alcune descrizioni di alto livello dei tipi di ottimizzazione:

  • Riduzione del codice (o tree shaking): rimuove il codice non referenziato
  • Offuscamento (o minimizzazione degli identificatori): abbrevia i nomi di classi e metodi
  • Ottimizzazione: riscrive il codice, ad esempio l'inlining

Per ridurre la probabilità di errori, puoi iniziare abilitando solo alcune di queste ottimizzazioni.

Abilitare solo il tree shaking

La riduzione del codice, nota anche come tree shaking, rimuove il codice che sembra non essere referenziato. Ti consigliamo di iniziare solo con il tree shaking, perché è il più semplice.

Per abilitare solo il tree shaking, aggiungi quanto segue al file proguard-rules.pro per disattivare gli altri tipi di ottimizzazioni. La disattivazione dell'offuscamento è fondamentale perché semplifica notevolmente la lettura delle analisi dello stack.

-dontobfuscate // Use temporarily to turn off identifier minification
-dontoptimize // Use temporarily to turn off optimization

Alla fine, non ti consigliamo di pubblicare questa configurazione, perché limita drasticamente la capacità di R8 di ottimizzare il codice, ma è un ottimo punto di partenza quando adotti R8 per la prima volta in una codebase di grandi dimensioni con problemi da risolvere.

Utilizzare la modalità di compatibilità

Per impostazione predefinita, R8 viene eseguito in modalità completa. La modalità completa offre prestazioni e risparmi di dimensioni notevolmente migliorati, ma puoi disattivarla temporaneamente e utilizzare la modalità di compatibilità quando abiliti la minimizzazione per la prima volta.

Per utilizzare la modalità di compatibilità, utilizza la seguente impostazione nel file gradle.properties:

android.enableR8.fullMode = false // Use temporarily to disable full mode

Abilitare il resto delle ottimizzazioni

Dopo aver verificato che il tree shaking funziona per la tua app, puoi rimuovere le impostazioni precedenti per riattivare l'offuscamento, l'ottimizzazione e la modalità completa di R8. Tieni presente che l'offuscamento può rendere più difficile il debug, motivo per cui ti consigliamo di risolvere prima i problemi di tree shaking.

Per ulteriori informazioni sulla deoffuscamento delle analisi dello stack, consulta Recuperare l'analisi dello stack originale.

Limitare l'ambito di ottimizzazione

Una build completamente ottimizzata ottimizza tutto il codice in ogni libreria e pacchetto, quindi è normale riscontrare problemi con R8 quando lo attivi per la prima volta. Se riscontri un problema di ottimizzazione in una parte dell'app, non disattivare completamente R8, altrimenti perderai i vantaggi in tutte le altre parti. Disattiva temporaneamente R8 solo nelle parti dell'app che causano problemi.

Utilizzare le regole di conservazione a livello di pacchetto

Ti consigliamo di utilizzare le regole di conservazione a livello di pacchetto per disattivare temporaneamente R8 in alcune parti dell'app. Dovresti sempre tornare indietro per risolvere questi problemi di ottimizzazione in un secondo momento; in genere, si tratta di una soluzione temporanea per aggirare le aree problematiche.

Ad esempio, se una parte dell'app utilizza molto Gson e causa problemi di ottimizzazione, la soluzione ideale è aggiungere regole di conservazione più mirate o passare a una soluzione di generazione del codice. Tuttavia, per sbloccare l'ottimizzazione del resto dell'app, puoi inserire il codice che definisce i tipi Gson in un sottopacchetto dedicato e aggiungere una regola come questa al file proguard-rules.pro:

-keep class com.myapp.json.** { *; }

Se una libreria che stai utilizzando ha riflessione sui componenti interni, puoi aggiungere in modo simile una regola di conservazione per l'intera libreria. Dovrai esaminare il codice o il file JAR/AAR della libreria per trovare il pacchetto appropriato da conservare. Anche in questo caso, non è consigliabile mantenere questa configurazione a lungo termine, ma può sbloccare l'ottimizzazione del resto dell'app:

-keep class com.somelibrary.** { *; }

Rimuovere le regole di conservazione a livello di pacchetto

Una volta che l'app funziona correttamente con le regole di conservazione a livello di pacchetto, devi tornare indietro e aggiungere regole di conservazione mirate oppure rimuovere l'utilizzo della riflessione o la libreria che richiede la regola di conservazione.

Ad esempio, in AndroidX è molto comune conservare tutte le regole che estendono una determinata classe per conservare solo le classi pertinenti. In genere, la riflessione deve essere rivolta solo alle classi o ai metodi che estendono determinate classi astratte, implementano determinate interfacce o in classi con un'annotazione di runtime specifica. Ognuno di questi è un modo supportato per definire le regole di conservazione in modo da non aver bisogno di regole di conservazione a livello di pacchetto nell'app finale completamente ottimizzata.

Se la riflessione è presente nella tua codebase, puoi sostituire le regole di conservazione a livello di pacchetto con Jetpack Keep. Le regole generate sono condizionali: R8 conserva la destinazione di una chiamata riflessiva solo se conserva anche il codice che effettua la chiamata. Per eseguire la migrazione di un pacchetto, utilizza i controlli lint di Jetpack Keep per trovare e annotare le chiamate riflessive, quindi rimuovi la regola a livello di pacchetto.