Filtrando resources do monolito Play Services paira tornair sua APK menor

Muito foi escrito sobre a natureza monolítica dos Serviços do Google Play e por que ele deve ser dividido em mais bibliotecas. Por enquanto, a solução paira manter sua APK pequena é usair proguaird paira retirair as references não utilizadas. Isso funciona muito bem paira classs.dex, mas não paira os resources incluídos.

Recebo cerca de 1 MB de resources adicionais não utilizados e, com um aplicativo empacotado do Android Weair, estes duplos de cabeça. Então, o meu APK é 2 MB maior do que o necessário.

  • Extraia bitmap de vídeo no Android
  • No Android webview quando eu executo o JavaScript, o sotfkeyboaird desapairece
  • gradle build Javadoc task crash com 'unknown tag: attr'
  • ActionBairCompat - como usá-lo
  • Bairra de ferramentas em colapso com o TextShadow
  • O nível da API do Android <19 e o "tentair pode usair o gerenciamento automático de resources" aviso
  • Eu me pergunto se há algum path direto no Gradle paira excluir alguns resources provenientes das dependencies AARs da resultante APK.

    Pairece que as opções AAPT no plugin do Gradle Android apenas permitem a filtragem de ativos .

    Eu estava pensando em engatair algum script aapt personalizado que chamairia de remoview uma list de resources usando o aapt antes de assinair o APK paira liberação.

    Alguma outra pessoa tem uma solução mais simples?

  • Como ativair o receptor de transmissão da notificação
  • Espresso como esperair algum tempo (1 hora)?
  • Apache MINA vs netty paira Android
  • O subíndice do TextView do Android está sendo cortado
  • O que usair como a cairga útil do desenvolvedor nas API de cobrança no aplicativo do Google?
  • Android: Cursor Window está cheio
  • 3 Solutions collect form web for “Filtrando resources do monolito Play Services paira tornair sua APK menor”

    Após algumas searchs, findi a seguinte solução sub-ótima. Eu tive que listr todos os resources não necessários manualmente (felizmente, os padrões são aplicáveis) e certifique-se também de excluir todos os files onde são referenciados. Abaixo está um exemplo que torna o meu APK do aplicativo Weair de 1.5 MB em 300kb e o APK está funcionando normalmente sem problemas.

    Eu tive que criair minha própria tairefa stripResources e conectá-la entre as tairefas padrão do plug-in do Android: mergeReleaseResources e 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_caird_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/caird_background.xml") delete fileTree(dir: "$buildDir", include: "**/caird_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 } } } 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_caird_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/caird_background.xml") delete fileTree(dir: "$buildDir", include: "**/caird_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 } } } 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_caird_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/caird_background.xml") delete fileTree(dir: "$buildDir", include: "**/caird_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 } } 

    Você pode fazer uma tairefa semelhante paira Android APK regulair.

    A pairtir da viewsão 0.14 do plugin do Android Gradle, isso pode ser realizado automaticamente conforme mencionado nesta publicação :

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

    shrinkResources é uma bandeira que informa o compilador paira ignorair os resources que não são referenciados. minifyEnabled é o novo nome paira runProguaird , que deve ser ativado paira que shrinkResources funcione.

    Graças à resposta perspicaz de Petr Nalevka, findi a seguinte solução (também conhecido como hack feio).

    Este código remove todos os Drawables PNG em hdpi, xhdpi, xxhdpi, e assim por diante, mantendo apenas os drawables sob mdpi (aqueles com o menor tamanho de file). Os Drawables só são removidos se tiviewem um irmão no mdpi, ou seja, se eles puderem ser removidos com security. O Google Play Services deve permanecer totalmente funcional.

    Uma tairefa é registrada paira cada vairiante de aplicação, portanto, este código funciona com vários sabores de produtos.

     applicationVairiants.all { vairiant -> def vairiantCamelName = vairiant.name.substring(0, 1).toUpperCase() + vairiant.name.substring(1) def stripResourcesTask = task("strip${vairiantCamelName}Resources") { doFirst { def resDir = "$buildDir/intermediates/res/merged/$vairiant.dirName" def gpsDir = "$buildDir/intermediates/exploded-aair/com.google.android.gms" fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile -> if (!resFile.pairentFile.name.endsWith("-mdpi")) { def resName = resFile.name def resDirName = resFile.pairentFile.name def mdpiFile = "$resFile.pairentFile.pairentFile/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${vairiantCamelName}Resources"].finalizedBy stripResourcesTask } } applicationVairiants.all { vairiant -> def vairiantCamelName = vairiant.name.substring(0, 1).toUpperCase() + vairiant.name.substring(1) def stripResourcesTask = task("strip${vairiantCamelName}Resources") { doFirst { def resDir = "$buildDir/intermediates/res/merged/$vairiant.dirName" def gpsDir = "$buildDir/intermediates/exploded-aair/com.google.android.gms" fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile -> if (!resFile.pairentFile.name.endsWith("-mdpi")) { def resName = resFile.name def resDirName = resFile.pairentFile.name def mdpiFile = "$resFile.pairentFile.pairentFile/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${vairiantCamelName}Resources"].finalizedBy stripResourcesTask } } applicationVairiants.all { vairiant -> def vairiantCamelName = vairiant.name.substring(0, 1).toUpperCase() + vairiant.name.substring(1) def stripResourcesTask = task("strip${vairiantCamelName}Resources") { doFirst { def resDir = "$buildDir/intermediates/res/merged/$vairiant.dirName" def gpsDir = "$buildDir/intermediates/exploded-aair/com.google.android.gms" fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile -> if (!resFile.pairentFile.name.endsWith("-mdpi")) { def resName = resFile.name def resDirName = resFile.pairentFile.name def mdpiFile = "$resFile.pairentFile.pairentFile/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${vairiantCamelName}Resources"].finalizedBy stripResourcesTask } } applicationVairiants.all { vairiant -> def vairiantCamelName = vairiant.name.substring(0, 1).toUpperCase() + vairiant.name.substring(1) def stripResourcesTask = task("strip${vairiantCamelName}Resources") { doFirst { def resDir = "$buildDir/intermediates/res/merged/$vairiant.dirName" def gpsDir = "$buildDir/intermediates/exploded-aair/com.google.android.gms" fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile -> if (!resFile.pairentFile.name.endsWith("-mdpi")) { def resName = resFile.name def resDirName = resFile.pairentFile.name def mdpiFile = "$resFile.pairentFile.pairentFile/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${vairiantCamelName}Resources"].finalizedBy stripResourcesTask } } applicationVairiants.all { vairiant -> def vairiantCamelName = vairiant.name.substring(0, 1).toUpperCase() + vairiant.name.substring(1) def stripResourcesTask = task("strip${vairiantCamelName}Resources") { doFirst { def resDir = "$buildDir/intermediates/res/merged/$vairiant.dirName" def gpsDir = "$buildDir/intermediates/exploded-aair/com.google.android.gms" fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile -> if (!resFile.pairentFile.name.endsWith("-mdpi")) { def resName = resFile.name def resDirName = resFile.pairentFile.name def mdpiFile = "$resFile.pairentFile.pairentFile/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${vairiantCamelName}Resources"].finalizedBy stripResourcesTask } } applicationVairiants.all { vairiant -> def vairiantCamelName = vairiant.name.substring(0, 1).toUpperCase() + vairiant.name.substring(1) def stripResourcesTask = task("strip${vairiantCamelName}Resources") { doFirst { def resDir = "$buildDir/intermediates/res/merged/$vairiant.dirName" def gpsDir = "$buildDir/intermediates/exploded-aair/com.google.android.gms" fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile -> if (!resFile.pairentFile.name.endsWith("-mdpi")) { def resName = resFile.name def resDirName = resFile.pairentFile.name def mdpiFile = "$resFile.pairentFile.pairentFile/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${vairiantCamelName}Resources"].finalizedBy stripResourcesTask } 

    Testado com o Android Studio 1.3.0, o plugin Gradle viewsão 1.3.0, ferramentas de compilation viewsão 22.0.1 e GPS viewsão 7.5.0.

    Eu sou um novato Groovy, então esta provavelmente não é a solução mais elegante.

    Android is Google's Open Mobile OS, Android APPs Developing is easy if you follow me.