2014-07-12 9 views
16

Wiele napisano o monolitycznym charakterze Usług Google Play i dlaczego należy je podzielić na więcej bibliotek. Na razie obejście tego problemu ma na celu użycie proguard do usunięcia niewykorzystanych odniesień. Działa to całkiem dobrze w klasach.dex, ale nie w przypadku dołączonych zasobów.Filtrowanie zasobów z monolitów Usług odtwarzania, aby zmniejszyć rozmiar pakietu APK.

Dostaję około 1 MB dodatkowych nieużywanych zasobów, a wraz z dołączoną aplikacją Android Wear zwiększa się dwukrotnie. Więc mój APK jest o 2 MB większy niż potrzeba.

Zastanawiam się, czy istnieje jakiś prosty sposób w Gradle, aby wykluczyć niektóre zasoby pochodzące z AARs zależności od wynikowego pakietu APK.

Wygląda na to, że the AAPT options in the Gradle Android plugin only allow filtering assets.

Zastanawiam się nad zahaczeniem o niestandardowy skrypt, który wywołałby usunięcie listy zasobów przy pomocy aapt przed podpisaniem pliku APK do wydania.

Czy ktoś inny ma prostsze rozwiązanie?

+0

Niestała alternatywa dla odpowiedzi Petra: http: // stackoverflow.com/questions/22967839/how-to-remove-unused-google-icons-import-from-google-play-services-library – pmont

+0

Rozwiązanie gradle powinno być bardziej przenośne. Nie próbowałem tego jeszcze, ale podoba mi się podejście. – pmont

Odpowiedz

14

Po kilku badaniach znalazłem następujące nieoptymalne rozwiązanie. Musiałem ręcznie wymieniać wszystkie niepotrzebne zasoby (na szczęście obowiązują wzorce) i upewnić się, że wszystkie pliki, do których są przywoływane, zostały usunięte. Poniżej znajduje się przykład, który sprawia, że ​​APK aplikacji Wear od 1,5 MB do 300kb, a plik APK działa normalnie bez problemów.

Musiałem stworzyć własne zadanie stripResources i podłączyć je między standardowymi wtyczkami do Androida: mergeReleaseResources i processReleaseResources.

task stripResources << { 
    println "Custom resource stripping in: $buildDir" 
    delete fileTree(dir: "$buildDir", include: "**/layout/confirmation_activity_layout.xml") 
    delete fileTree(dir: "$buildDir", include: "**/layout/watch_card_content.xml") 
    delete fileTree(dir: "$buildDir", include: "**/common_signin*.png") 
    delete fileTree(dir: "$buildDir", include: "**/drawable/common_signin*.xml") 
    delete fileTree(dir: "$buildDir", include: "**/generic_confirmation*.png") 
    delete fileTree(dir: "$buildDir", include: "**/drawable/confirmation_*.xml") 
    delete fileTree(dir: "$buildDir", include: "**/drawable/card_background.xml") 
    delete fileTree(dir: "$buildDir", include: "**/card_frame*.png") 
    delete fileTree(dir: "$buildDir", include: "**/go_to*.png") 
    delete fileTree(dir: "$buildDir", include: "**/drawable/go_to_*.xml") 
    delete fileTree(dir: "$buildDir", include: "**/ic_plusone*.png") 
    delete fileTree(dir: "$buildDir", include: "**/powered_by_google*.png") 
    // if you only have English you can teh following to filter out some GPS texts wich also take few hundreds of kb 
    // delete fileTree(dir: "$buildDir", include: "**/values-*/values.xml") 
} 

    tasks.whenTaskAdded { task -> 
     if (task.name == 'processReleaseManifest') { 
      task.dependsOn stripResources 
     } 
    } 

Możesz zrobić podobne zadanie dla zwykłego pakietu APK systemu Android.

+1

Czy ten fragment usunie zasoby naszego własnego modułu (jeśli przypadkowo mają taki sam wzorzec nazwy)? – akhyar

1

Począwszy od wersji 0.14 wtyczki Android Gradle, może to być osiągnięte automatycznie, jak wspomniano w this post:

android { 
    buildTypes { 
     release { 
      minifyEnabled true 
      shrinkResources true 
     } 
    } 
} 

shrinkResources jest flaga, która informuje kompilator, aby pominąć wszelkie zasoby, które nie odwołują. minifyEnabled to nowa nazwa dla runProguard, która musi być włączona dla pracy shrinkResources.

+1

W rzeczywistości mam 'minifyEnabled' ustawiony na' true', a te zasoby nadal są zawarte w aplikacji. – markshiz

+0

Potrzebujesz zarówno 'minifyEnabled' oraz' shrinkResources'. Możliwe, że reguły programu Proguard uniemożliwią programowi Proguard usunięcie nieużywanych klas z biblioteki, co z kolei uniemożliwi skasowaniu obrazów '' shrinkResources'. –

+0

Mam również ustawienie 'shrinkResources' ustawione na" true ", i brak wyraźnych reguł, które uniemożliwiałyby optymalizację Google Play. – markshiz

1

Dzięki wnikliwej odpowiedzi Petra Nalevki wymyśliłem następujące rozwiązanie (a.k.a. brzydkie włamanie).

Ten kod usuwa wszystkie rysunki PNG pod pozycjami hdpi, xhdpi, xxhdpi i tak dalej, zachowując tylko rysunki pod mdpi (te z najmniejszym rozmiarem pliku). Szuflady są usuwane tylko wtedy, gdy mają rodzeństwo poniżej mdpi, czyli jeśli można je bezpiecznie usunąć. Usługi Google Play powinny być w pełni funkcjonalne.

Zadanie zostaje zarejestrowane dla każdego wariantu aplikacji, więc ten kod działa z wieloma smakami produktów.

applicationVariants.all { variant -> 
    def variantCamelName = variant.name.substring(0, 1).toUpperCase() + variant.name.substring(1) 
    def stripResourcesTask = task("strip${variantCamelName}Resources") { 
     doFirst { 
      def resDir = "$buildDir/intermediates/res/merged/$variant.dirName" 
      def gpsDir = "$buildDir/intermediates/exploded-aar/com.google.android.gms" 
      fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile -> 
       if (!resFile.parentFile.name.endsWith("-mdpi")) { 
        def resName = resFile.name 
        def resDirName = resFile.parentFile.name 
        def mdpiFile = "$resFile.parentFile.parentFile/drawable-mdpi/$resName" 
        if (file(mdpiFile).file) { 
         def files = fileTree(dir: "$resDir", include: "**/${resDirName}/${resName}") 
         // files.each { f -> println "Deleting $f..." } 
         delete files 
        } 
       } 
      } 
     } 
    } 
    tasks["merge${variantCamelName}Resources"].finalizedBy stripResourcesTask 
} 

Testowane z Androidem Studio 1.3.0, wersja 1.3.0 Gradle wtyczki, budować narzędzia w wersji 22.0.1 i wersję GPS 7.5.0.

Jestem nieco Groovy noob, więc prawdopodobnie nie jest to najbardziej eleganckie rozwiązanie.

+0

Czy 'shrinkResources' nie działa dla ciebie? – JJD