ProGuard

ProGuard di Xamarin.Android è uno strumento di classe Java che consente di compattare, ottimizzare ed eseguire una verifica preliminare dei file. ProGuard rileva e rimuove eventuale codice non usato e analizza e ottimizza il bytecode. Questa guida illustra il funzionamento di ProGuard e spiega come abilitare questo strumento nel progetto e come configurarlo, presentando anche alcuni esempi di configurazione.

Panoramica

ProGuard rileva e rimuove classi, campi, metodi e attributi inutilizzati dal pacchetto dell'applicazione. Può eseguire queste operazioni anche per le librerie di riferimento, consentendo di evitare il limite di 64 KB. Lo strumento ProGuard di Android SDK ottimizza anche il bytecode e rimuove le istruzioni di codice non utilizzato. ProGuard legge file JAR di input e quindi compatta, ottimizza e verifica in via preliminare tali file, scrivendo i risultati in uno o più file JAR di output.

Per elaborare i file APK di input, ProGuard esegue i passaggi seguenti:

  1. Passaggio di compattazione : ProGuard determina in modo ricorsivo le classi e i membri della classe usati. Tutte le altre classi e tutti i membri di queste vengono eliminati.

  2. Passaggio di ottimizzazione: ProGuard ottimizza ulteriormente il codice. Tra l'altro, ProGuard rende privati, statici o finali le classi e i metodi che non rappresentano punti di ingresso, rimuove i parametri inutilizzati e imposta alcuni metodi come inline.

  3. Passaggio di offuscamento : nello sviluppo nativo di Android ProGuard rinomina classi e membri di classe che non sono punti di ingresso. Il mantenimento di punti di ingresso garantisce che le classi e i membri rinominati siano ancora accessibili con i nomi originari. Tuttavia, questo passaggio non è supportato da Xamarin.Android poiché l'app viene compilata nel linguaggio intermedio (IL).

  4. Passaggio di verifica preliminare : esegue i controlli sui bytecode Java prima del runtime e annota i file di classe per il vantaggio della macchina virtuale Java. Questo è l'unico passaggio per il quale non ha bisogno conoscere i punti di ingresso.

Ognuno di questi passaggi è facoltativo. Come verrà spiegato nella prossima sezione, in Xamarin.Android ProGuard usa solo un subset di questi passaggi.

ProGuard in Xamarin.Android

La configurazione di ProGuard per Xamarin.Android non esegue l'offuscamento dei file APK. Non è in effetti possibile abilitare l'offuscamento con ProGuard, neanche usando file di configurazione personalizzati. In Xamarin.Android, quindi, ProGuard esegue solo i passaggi di compattazione e ottimizzazione:

Shrinking and optimization steps

Un elemento importante da conoscere in anticipo prima di usare ProGuard è il funzionamento di questo strumento all'interno del processo di compilazione di Xamarin.Android. Questo processo si svolge in due passaggi distinti:

  1. Linker di Xamarin.Android

  2. ProGuard

Di seguito viene descritto ognuno di questi passaggi.

Linker

Il linker di Xamarin.Android impiega l'analisi statica dell'applicazione per determinare quanto segue:

  • Gli assembly effettivamente usati.

  • I tipi effettivamente usati.

  • I membri effettivamente usati.

Il linker viene sempre eseguito prima del passaggio relativo a ProGuard. Per questo motivo, il linker può rimuovere un assembly, un tipo o un membro per il quale sia prevista l'esecuzione di ProGuard. Per altre informazioni sull'uso del linker in Xamarin.Android, vedere Linking on Android (Uso del linker in Android).

ProGuard

Dopo il completamento del passaggio relativo al linker, viene eseguito ProGuard per rimuovere bytecode Java inutilizzati. Questo è il passaggio che ottimizza il file APK.

Uso di ProGuard

Per usare ProGuard nel progetto dell'app, è prima necessario abilitare ProGuard. Per il processo di compilazione di Xamarin.Android si potrà quindi usare il file di configurazione di ProGuard predefinito o, in alternativa, crearne uno personalizzato.

Abilitazione di ProGuard

Per abilitare ProGuard nel progetto dell'app, usare la procedura seguente:

  1. Assicurarsi che per il progetto sia impostata la configurazione Release. Questo passaggio è importante perché è necessario che il linker sia in esecuzione per rendere possibile l'esecuzione di ProGuard:

    Select Release configuration

  2. Scegliere ProGuard dall'elenco a discesa Compattatore di codice nella finestra Proprietà > Opzioni Android:

    Proguard code shrinker selected

Per la maggior parte delle app Xamarin.Android, il file di configurazione ProGuard predefinito fornito da Xamarin.Android è sufficiente per rimuovere tutto (e solo) il codice inutilizzato. Per visualizzare la configurazione predefinita di ProGuard, aprire il file in obj\Release\proguard\proguard_xamarin.cfg.

L'esempio seguente illustra un tipico file di configurazione proguard_xamarin.cfg:

# This is Xamarin-specific (and enhanced) configuration.

-dontobfuscate

-keep class mono.MonoRuntimeProvider { *; <init>(...); }
-keep class mono.MonoPackageManager { *; <init>(...); }
-keep class mono.MonoPackageManager_Resources { *; <init>(...); }
-keep class mono.android.** { *; <init>(...); }
-keep class mono.java.** { *; <init>(...); }
-keep class mono.javax.** { *; <init>(...); }
-keep class opentk.platform.android.AndroidGameView { *; <init>(...); }
-keep class opentk.GameViewBase { *; <init>(...); }
-keep class opentk_1_0.platform.android.AndroidGameView { *; <init>(...); }
-keep class opentk_1_0.GameViewBase { *; <init>(...); }

-keep class android.runtime.** { <init>(***); }
-keep class assembly_mono_android.android.runtime.** { <init>(***); }
# hash for android.runtime and assembly_mono_android.android.runtime.
-keep class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }
-keepclassmembers class md52ce486a14f4bcd95899665e9d932190b.** { *; <init>(...); }

# Android's template misses fluent setters...
-keepclassmembers class * extends android.view.View {
   *** set*(***);
}

# also misses those inflated custom layout stuff from xml...
-keepclassmembers class * extends android.view.View {
   <init>(android.content.Context,android.util.AttributeSet);
   <init>(android.content.Context,android.util.AttributeSet,int);
}

La prossima sezione descrive come creare un file di configurazione di ProGuard personalizzato.

Personalizzazione di ProGuard

Facoltativamente, è possibile aggiungere un file di configurazione di ProGuard personalizzato per esercitare un controllo maggiore sugli strumenti ProGuard, ad esempio se si vogliono indicare in modo esplicito a ProGuard le classi da mantenere. A tale scopo, creare un nuovo file con estensione cfg e applicare l'azione di compilazione ProGuardConfiguration nel riquadro Proprietà di Esplora soluzioni:

ProguardConfiguration build action selected

Tenere presente che questo file di configurazione non sostituisce il file proguard_xamarin.cfg di Xamarin.Android, dato che ProGuard li usa entrambi.

In alcuni casi ProGuard non è in grado di analizzare correttamente l'applicazione e potrebbe rimuovere codice in realtà necessario. Se questo si verifica, è possibile aggiungere una riga -keep al file di configurazione ProGuard personalizzato:

-keep public class MyClass

In questo esempio, MyClass rappresenta il nome effettivo della classe che deve essere ignorata da ProGuard.

È anche possibile registrare nomi personalizzati con annotazioni [Register] e usare questi nomi per personalizzare le regole di ProGuard. È possibile registrare nomi per Adapters, Views, BroadcastReceivers, Services, ContentProviders, Activities e Fragments. Per altre informazioni sull'uso dell'attributo personalizzato [Register], vedere Working with JNI (Uso di JNI).

Opzioni di ProGuard

ProGuard offre una serie di opzioni che è possibile configurare per controllare il suo funzionamento in modo più granulare. Il manuale di ProGuard costituisce la documentazione di riferimento completa per l'uso di ProGuard.

Xamarin supporta le opzioni di ProGuard seguenti:

Le opzioni seguenti vengono ignorate da Xamarin.Android:

ProGuard e Android Nougat

Se si vuole usare ProGuard con Android 7.0 o versione successiva, è necessario scaricare una versione più recente di ProGuard, perché Android SDK non ne fornisce una nuova versione compatibile con JDK 1.8.

È possibile usare questo pacchetto NuGet per installare una versione più recente di proguard.jar. Per altre informazioni sull'aggiornamento dell'Android SDK predefinitoproguard.jar, vedere questa discussione in Stack Overflow.

Tutte le versioni di ProGuard sono disponibili in questa pagina SourceForge.

Configurazioni di ProGuard di esempio

Di seguito sono riportati due file di configurazione di ProGuard di esempio. Si noti che, in questi casi, i file JAR di input, di output e di libreria vengono forniti dal processo di compilazione di Xamarin.Android. È quindi possibile concentrarsi su altre opzioni, ad esempio -keep.

Una semplice attività Android

Nell'esempio seguente viene illustrata la configurazione per una semplice attività Android:

-injars  bin/classes
-outjars bin/classes-processed.jar
-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar

-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic

-keep public class mypackage.MyActivity

Un'applicazione Android completa

Nell'esempio seguente viene illustrata la configurazione per un'app Android completa:

-injars  bin/classes
-injars  libs
-outjars bin/classes-processed.jar
-libraryjars /usr/local/java/android-sdk/platforms/android-9/android.jar

-dontpreverify
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider

-keep public class * extends android.view.View {
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
public void set*(...);
}

-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers class * implements android.os.Parcelable {
static android.os.Parcelable$Creator CREATOR;
}

-keepclassmembers class **.R$* {
public static <fields>;
}

ProGuard e il processo di compilazione di Xamarin.Android

Le sezioni seguenti spiegano il funzionamento di ProGuard durante una compilazione Rilascio di Xamarin.Android.

Qual è il comando eseguito da ProGuard?

ProGuard è semplicemente un file .jar fornito con Android SDK. Viene quindi richiamato in un comando:

java -jar proguard.jar options ...

Attività ProGuard

L'attività ProGuard si trova all'interno dell'assembly Xamarin.Android.Build.Tasks.dll. Fa parte della destinazione _CompileToDalvikWithDx, che a sua volta fa parte della destinazione _CompileDex.

L'elenco seguente fornisce un esempio dei parametri predefiniti generati dopo la creazione di un nuovo progetto usando File > Nuovo progetto:

ProGuardJarPath = C:\Android\android-sdk\tools\proguard\lib\proguard.jar
AndroidSdkDirectory = C:\Android\android-sdk\
JavaToolPath = C:\Program Files (x86)\Java\jdk1.8.0_92\\bin
ProGuardToolPath = C:\Android\android-sdk\tools\proguard\
JavaPlatformJarPath = C:\Android\android-sdk\platforms\android-25\android.jar
ClassesOutputDirectory = obj\Release\android\bin\classes
AcwMapFile = obj\Release\acw-map.txt
ProGuardCommonXamarinConfiguration = obj\Release\proguard\proguard_xamarin.cfg
ProGuardGeneratedReferenceConfiguration = obj\Release\proguard\proguard_project_references.cfg
ProGuardGeneratedApplicationConfiguration = obj\Release\proguard\proguard_project_primary.cfg
ProGuardConfigurationFiles

    {sdk.dir}tools\proguard\proguard-android.txt;
    {intermediate.common.xamarin};
    {intermediate.references};
    {intermediate.application};
    ;

JavaLibrariesToEmbed = C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\mono.android.jar
ProGuardJarInput = obj\Release\proguard\__proguard_input__.jar
ProGuardJarOutput = obj\Release\proguard\__proguard_output__.jar
DumpOutput = obj\Release\proguard\dump.txt
PrintSeedsOutput = obj\Release\proguard\seeds.txt
PrintUsageOutput = obj\Release\proguard\usage.txt
PrintMappingOutput = obj\Release\proguard\mapping.txt

Il prossimo esempio illustra un comando ProGuard tipico eseguito nell'ambiente IDE:

C:\Program Files (x86)\Java\jdk1.8.0_92\\bin\java.exe -jar C:\Android\android-sdk\tools\proguard\lib\proguard.jar -include obj\Release\proguard\proguard_xamarin.cfg -include obj\Release\proguard\proguard_project_references.cfg -include obj\Release\proguard\proguard_project_primary.cfg "-injars 'obj\Release\proguard\__proguard_input__.jar';'C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v7.0\mono.android.jar'" "-libraryjars 'C:\Android\android-sdk\platforms\android-25\android.jar'" -outjars "obj\Release\proguard\__proguard_output__.jar" -optimizations !code/allocation/variable

Risoluzione dei problemi

Problemi relativi ai file

Il messaggio di errore seguente può essere visualizzato quando ProGuard legge il proprio file di configurazione:

Unknown option '-keep' in line 1 of file 'proguard.cfg'

Questo problema in genere si verifica in Windows a causa della codifica non corretta del file .cfg. ProGuard non è in grado di gestire eventuali caratteri byte order mark (BOM) all'interno di file di testo. Se è presente un carattere BOM, ProGuard termina con l'errore precedente.

Per evitare questo problema, modificare il file di configurazione personalizzato con un editor di testo che consenta il salvataggio del file senza un carattere BOM. Per risolvere questo problema, assicurarsi che per l'editor di testo la codifica sia impostata su UTF-8. Con l'editor di testo Notepad++, ad esempio, è possibile salvare i file senza carattere BOM selezionando Encoding (Codifica) > Encode in UTF-8 Without BOM (Codifica UTF-8 senza BOM) quando si salva il file.

Altri problemi

La pagina Troubleshooting (Risoluzione dei problemi) di ProGuard illustra i problemi più comuni che possono verificarsi durante l'uso di ProGuard e le rispettive soluzioni.

Riepilogo

Questa guida ha illustrato il funzionamento di ProGuard in Xamarin.Android e ha spiegato come abilitare questo strumento nel progetto dell'app e come configurarlo, presentando esempi di configurazioni di ProGuard e fornendo le soluzioni dei problemi più comuni. Per altre informazioni sullo strumento ProGuard e su Android, vedere Compattare il codice e le risorse.