L’altro giorno stavo cercando di gestire i Google Analytics in una semplice applicazione mobile che sto sviluppando per conto mio con Titanium, ma quando sono andato ad aggiungere il modulo ti.ga non sono più riuscito a compilare niente a causa di un errore dovuto evidentemente a dei conflitti tra librerie.
L’errore di compilazione era questo:
[ERROR] : UNEXPECTED TOP-LEVEL EXCEPTION:
[ERROR] : java.lang.IllegalArgumentException: already added:Lcom/google/android/gms/maps/LocationSource;
Cercando in giro ho avuto la conferma che il problema era dovuto all’inclusione di una libreria google-play-services.jar
all’interno del modulo, che faceva a cazzotti con quelle usate in un altro modulo importato nel mio progetto: ti.map.
Per completezza è necessario dire che la libreria ti.map
– insieme alle altre native di Titanium – è accessibile in /Utenti/m.piccotti/Library/Application Support/Titanium/modules/android
.
Classi duplicate, che fare
Il problema è comune e in molti sul web se ne lamentano, non solo parlando di Titanium e di questi moduli. Ovviamente non si può pensare di eliminare la libreria contenente le implementazioni delle api necessarie al modulo, ma lì per lì non sapevo che fare, finché non ho guardato meglio un altro modulo che mi serviva: ti.admob.
A differenza di ti.map il modulo ti.admob va tirato giù da GitHub e importato nel progetto, quindi mi è balzata all’occhio la presenza delle librerie google-play-services-base.jar
e google-play-services-ads.jar
; guardando il log ho notato il messaggio che mi interessava:
[DEBUG] : Skipping duplicate jar file: project_directory/modules/android/ti.admob/2.1.6/lib/google-play-services-base.jar
In sostanza Titanium evita l’importazione di librerie duplicate, ma la google-play-services.jar
presente all’interno del modulo ti.ga non risultava duplicata ai controlli di Titanium, e il risultato era una duplicazione delle classi Android in fase di compilazione in presenza del modulo all’interno del progetto.
La soluzione
Quando non si sa dove sbattere la testa conviene sempre fare come gli altri, ho quindi preso la libreria google-play-services_base.jar dal modulo ti.admob (sviluppato da quelli di Appcelerator), e mi sono andato a cercare un jar contenente solo le librerie di Google Analytics. Naturalmente non ho trovato niente di simile, ma ero sulla strada giusta.
Pulizie manuali del jar
Mi serviva un jar contenente solo le classi necessarie al funzionamento di Google Analytics. Da ignorante nello sviluppo nativo Android ho fatto quello che avrebbe fatto qualunque altro bimbominkia, scompattando il jar e ricompattandolo solo dopo aver eliminato tutti i package fuori da “analytics”.
Non ha funzionato, per la cronaca comunque un jar si può decomprimere con qualunque utility di decompressione file o con il comando:
jar xf google-play-services-analytics.jar
e si può ricreare con:
jar cf google-play-services-analytics.jar ./com
.Qui si spiega come funziona il comando per la creazione dei jar, questa cosa mi è comunque servita alla fine.
Android ProGuard
Effettivamente ripulire a mano le classi di una libreria sconosciuta aveva probabilità di successo pari a 0.00001, e io non sono così fortunato. Ho trovato però nel giro di poco la soluzione: ProGuard.
ProGuard è un tool che serve per ottimizzare e offuscare il codice Android. A me interessava eliminare dalla libreria google-play-services.jar inclusa nel modulo non funzionante tutto il superfluo lasciando solo la parte di analytics, e questa cosa si fa abbastanza agevolmente.
-injars google-play-services.jar -outjars google-play-services-analytics.jar -libraryjars /usr/local/Android/sdk/extras/android/support/v4/android-support-v4.jar -libraryjars /usr/local/Android/sdk/platforms/android-21/android.jar -dontoptimize -dontobfuscate -dontwarn com.google.**.R -dontwarn com.google.**.R$* -dontnote -keep public class com.google.android.gms.analytics.** { public protected *; }
Vittoria in (quasi) quattro mosse:
- includere questi comandi in un file di configurazione (chiamato ad esempio
proguard.cfg
) - copiare il file di configurazione appena creato e la libreria “onnicomprensiva” google-play-services.jar all’interno di
/usr/local/Android/sdk/tools/proguard/lib
(o comunque all’interno della directory dove si trova proguard.jar) - installare
android-support-v4.jar
se mancante, con l’Android SDK manager - eseguire il comando
java -jar proguard.jar @proguard.cfg
… e il file salvifico compare magicamente all’interno della stessa directory.
Conclusioni
Ovviamente non è vero che il file risultante funzionava, infatti ho dovuto eliminare a mano tre o quattro classi da quest’ultimo come spiegato nella sezione “Pulizie manuali del jar”.
Il risultato è stato che finalmente quello che doveva funzionare ha funzionato, ma anche che le due librerie “base” e “analytics” pesano insieme meno della metà di quella unica, cosa non di poco conto nel mondo Android.