关闭

android gradle build resources.arsc文件未压缩问题解决

778人阅读 评论(0) 收藏 举报
分类:

随着google如火如荼的推出android studio IDE和gradle buld工具,很多公司要对build系统做从ant到gradle的升级,不幸的是我被分配负责该任务。


发现一个问题,gralde编译后的apk比ant要大大约1M左右,对于老大之前严格要求控制apk在10M左右的需求,只能说造化弄人。压缩包打开发现gradle build之后的apk resources.arsc没有压缩,导致apk增大很多。

于是乎,设置resource shrink 为true,结果如下:


图一:ant打包之后的详单:


图二:gradle shrinkResources false的详单


图三:gradle shrinkResources true的详单


结果发现:ant打包后的resources.arsc,从1.1M压缩到267K,还是比较狠的。

至于是否shrinkResources,没有什么效果

aapt l -v Client-release-unsigned-ant.apk ,查看apk明细:

发现ant包和gradle包的差异:

 Length   Method    Size  Ratio   Offset      Date  Time  CRC-32    Name
1158688  Deflate  273481  76%   9835381  11-17-15 15:18  04aecb3e  resources.arsc -------- <span style="color:#ff0000;">ant build apk</span>
1158508  Stored  1158508   0%   2899860  11-17-15 15:21  9222ccf9  resources.arsc   -------- <span style="color:#ff0000;">gradle build apk</span>

ant build resources.arsc是deflate method,而gradle build 的是stored method,而且size相差很大。

于是乎,寻找gradle设置deflate模式的配置开关,未果,似乎是硬编码方式,不支持dsl配置,只能从最后的apk包下手,对resources做二次压缩。下面代码:

apply plugin: 'android'


dependencies {
    compile fileTree(dir: 'libs', include: '*.jar')
    compile project(':MyProj')
}


android {
    compileSdkVersion 19
    buildToolsVersion '21.1.2'


    lintOptions {
        abortOnError false
    }


    sourceSets {
        main {
            manifest.srcFile 'AndroidManifest.xml'
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
            jniLibs.srcDirs = ['libs']
        }


        // Move the tests to tests/java, tests/res, etc...
        instrumentTest.setRoot('tests')


        // Move the build types to build-types/<type>
        // For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
        // This moves them out of them default location under src/<type>/... which would
        // conflict with src/ being used by the main source set.
        // Adding new build types or product flavors should be accompanied
        // by a similar customization.
        debug.setRoot('build-types/debug')
        release.setRoot('build-types/release')
    }


    defaultConfig {
        multiDexEnabled true
    }


    dexOptions {
        javaMaxHeapSize "2g"
        jumboMode true
    }


    buildTypes {
        debug {
            minifyEnabled false
            proguardFile('proguard.cfg')
        }
        release {
            minifyEnabled true
            proguardFile('proguard.cfg')
        }
    }


    packagingOptions {
        exclude('META-INF/DEPENDENCIES')
        exclude('META-INF/NOTICE')
        exclude('META-INF/LICENSE')
    }


    lintOptions {
        // Or, if you prefer, you can continue to check for errors in release builds,
        checkReleaseBuilds false
        // but continue the build even when errors are found:
        abortOnError false
    }
}


import java.util.zip.ZipEntry
import java.util.zip.ZipFile
import java.util.zip.ZipOutputStream


// 打包过程中很多手工zip过程:
// 1,为了压缩resources.arsc文件而对标准产出包重新压缩
// 2,以及各子apk的纯手打apk包
// 但对于音频等文件,压缩会导致资源加载报异常
// 重新打包方法,使用STORED过滤掉不应该压缩的文件们
// 后缀名列表来自于android源码
def repackApk(originApk, targetApk){
    def noCompressExt = [".jpg", ".jpeg", ".png", ".gif",
                         ".wav", ".mp2", ".mp3", ".ogg", ".aac",
                         ".mpg", ".mpeg", ".mid", ".midi", ".smf", ".jet",
                         ".rtttl", ".imy", ".xmf", ".mp4", ".m4a",
                         ".m4v", ".3gp", ".3gpp", ".3g2", ".3gpp2",
                         ".amr", ".awb", ".wma", ".wmv"]


    ZipFile zipFile = new ZipFile(originApk)
    ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(targetApk)))
    zipFile.entries().each{ entryIn ->
        if(entryIn.directory){
            println "${entryIn.name} is a directory"
        }
        else{
            def entryOut = new ZipEntry(entryIn.name)
            def dotPos = entryIn.name.lastIndexOf('.')
            def ext = (dotPos >= 0) ? entryIn.name.substring(dotPos) : ""
            def isRes = entryIn.name.startsWith('res/')
            if(isRes && ext in noCompressExt){
                entryOut.method = ZipEntry.STORED
                entryOut.size = entryIn.size
                entryOut.compressedSize = entryIn.size
                entryOut.crc = entryIn.crc
            }
            else{
                entryOut.method = ZipEntry.DEFLATED
            }
            zos.putNextEntry(entryOut)
            zos << zipFile.getInputStream(entryIn)
            zos.closeEntry()
        }
    }
    zos.finish()
    zos.close()
    zipFile.close()
}


task repack(){


	doLast{
		print "root dir is : $rootDir"
		println "release打包之后,重新压缩一遍,以压缩resources.arsc"


		def oldApkFile = file("$rootDir/projectPath/build/outputs/apk/Client-release-unsigned.apk")


		assert oldApkFile != null : "没有找到release包!"


		def newApkFile = new File("$rootDir/projectPath/build/outputs/apk/Client-release-unsigned-repack.apk")
		print oldApkFile.absolutePath
		print newApkFile.absolutePath
		//重新打包
		repackApk(oldApkFile.absolutePath, newApkFile.absolutePath)


		assert newApkFile.exists() : "没有找到重新压缩的release包!"
	}
}

其实也可以通过java或者python程序终结对apk进行二次处理达到效果,但是不如gradle处理的自然。


done, perfect !

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:44028次
    • 积分:964
    • 等级:
    • 排名:千里之外
    • 原创:43篇
    • 转载:51篇
    • 译文:3篇
    • 评论:2条
    最新评论