zipalign的使用

zipalign


 

是一个存档对齐工具,可为Android应用程序(.apk)文件提供重要的优化。目的是确保所有未压缩数据以相对于文件开头的特定对齐开始。具体来说,它会导致.apk中的所有未压缩数据(如图像或原始文件)在4字节边界上对齐。这允许直接访问所有部分,mmap()即使它们包含具有对齐限制的二进制数据。其好处是能够减少应用程序的RAM内存资源消耗。

在将.apk文件分发给最终用户之前,应始终使用此工具来对齐.apk文件。Android构建工具可以为您处理此问题。将Eclipse与ADT插件一起使用时,导出向导会在您使用私钥对其进行签名后自动为.apk设置zipalign。在使用Ant编译应用程序时使用的构建脚本也将对.apk进行zipalign,只要您提供了密钥库的路径和项目ant.properties文件中的密钥别名,以便构建工具可以首先对包进行签名。

警告:只有在使用您的私钥对.apk文件进行签名才能执行zipalign 。如果在签名之前执行zipalign,则签名过程将撤消对齐。此外,不要对对齐的包进行更改。对存档的更改(例如重命名或删除条目)可能会破坏已修改条目和所有后续条目的对齐。添加到“对齐”存档的任何文件都不会对齐。

通过更改zip本地文件头部分中“额外”字段的大小来进行调整。“额外”字段中的现有数据可以通过该过程改变。

有关如何在构建应用程序时使用zipalign的更多信息,请阅读签署您的应用程序

 


 

1、对infile.apk进行对齐并且保存为outfile.apk

      zipalign [-f] [-v] infile.apk outfile.apk

 

2、检查apk是否进行了对齐

zipalign c v  existing.apk

 


 

infile.apk表示需要存档对齐的apk
outfile.apk表示存档对齐后的apk
alignment:表示指定的对应字节数,是一个整数且必须指定为4。

转载请标明出处:http://77blogs.com/?p=288

原文:google开发文档:http://www.android-doc.com/tools/help/zipalign.html

Gradle Task顺序

原文链接:https://blog.csdn.net/lzyzsd/article/details/46935405

 

我注意到我在使用Gradle的时候遇到的大多数问题都是和task的执行顺序有关的。很明显如果我的构建会工作的更好如果我的task都是在正确的时候执行。下面我们就深入了解一下如何更改task的执行顺序。

dependsOn

我认为最直接的方式来说明的你task的执行时依赖别的task的方法就是使用dependsOn方法。
比如下面的场景,已经存在task A,我们要添加一个task B,它的执行必须要在A执行完之后:
B->A
这是一个很简单的场景,假定A和B的定义如下:

只需要简单的调用B.dependsOn A,就可以了。
这意味着,只要我执行task B,task A都会先执行。

 

另外,你也可以在task的配置区中来声明它的依赖:

如果我们想要在已经存在的task依赖中插入我们的task该怎么做呢? 
insert task 
过程和刚才类似。假定已经存在如下的task依赖:

加入我们的新的task

输出:

 

注意dependsOn把task添加到依赖的集合中,所以依赖多个task是没有问题的。
multi depends

输出:

mustRunAfter

现在假定我又一个task,它依赖于其他两个task。这里我使用一个真实的场景,我有两个task,一个单元测试的task,一个是UI测试的task。另外还有一个task是跑所有的测试的,它依赖于前面的两个task。
all tests

输出:

 

尽管unitest和UI test会子啊test task之前执行,但是unit和ui这两个task的执行顺序是不能保证的。虽然现在来看是按照字母表的顺序执行,但这是依赖于Gradle的实现的,你的代码中绝对不能依赖这种顺序。
由于UI测试时间远比unit test时间长,因此我希望unit test先执行。一个解决办法就是让ui task依赖于unit task。
ui->unit

输出:

 

现在unit test会在ui test之前执行了。
但是这里有个很恶心的问题,我的ui测试其实并不依赖于unit test。我希望能够单独的执行ui test,但是这里每次我执行ui test,都会先执行unit test。
这里就要用到mustRunAfter了。mustRunAfter并不会添加依赖,它只是告诉Gradle执行的优先级如果两个task同时存在。比如我们这里就可以指定ui.mustRunAfter unit,这样如果ui task和unit task同时存在,Gradle会先执行unit test,而如果只执行gradle ui,并不会去执行unit task。

输出:

依赖关系如下图:
mustRunAfter

mustRunAfter在Gradle2.4中目前还是实验性的功能。

finalizedBy

现在我们已经有两个task,unit和ui,假定这两个task都会输出测试报告,现在我想把这两个测试报告合并成一个:
merge

现在如果我想获得ui和unit的测试报告,执行task mergeReports就可以了。

这个task是能工作,但是看起来好笨啊。mergeReports从用户的角度来看感觉不是特别好。我希望执行tests task就可以获得测试报告,而不必知道mergeReports的存在。当然我可以把merge的逻辑挪到tests task中,但我不想把tests task搞的太臃肿,我还是继续把merge的逻辑放在mergeReports task中。
finalizeBy来救场了。顾名思义,finalizeBy就是在task执行完之后要执行的task。修改我们的脚本如下:

现在执行tests task就可以拿到测试报告了:

 

注意,finalizedBy也是Gradle2.4的实验性功能

shrinkResources去除无用资源原理

开启shrinkResources后,打包过程会新增task transformClassesWithShrinkResFor{variant},gradle1.5之后只需要注册一个tranform就会产生一个对应的task,查看源码发现对应的tranfrom在com.android.build.gradle.internal.transforms.ShrinkResourcesTransform,此类中调用com.android.build.gradle.tasks.ResourceUsageAnalyzer的analyze方法进行分析无用资源。

 

ResourceUsageAnalyzer:

该类的开头有这么一段话:

我翻译一下这段:

这个类负责搜索一颗gradle构建树(在合并资源,编译,压缩已经完成之后,最终的apk组装之前),这颗构建树确定了那些资源是未使用的,并将其删除。

它通过检查做到这一点:

  • 合并的清单,用于查找根资源引用(例如启动图标)
  • 合并的R类(查找分配给资源的实际整数常量)
  • 混淆映射文件(找到原始名称到混淆后短名称的一个映射)
  • 合并的资源(用于查找哪些资源引用其他资源,例如包括其他other drawables的drawable state lists,或包括其他布局的布局,或引用其他drawables的styles,或包括布局文件的菜单项等)
  • 缩小的输出类(在代码中查找实际可访问的资源引用)

所有的这些,构建了一个引用图,并基于根引用(例如来自清单与剩余代码),它计算出了在app中哪些资源是实际可达的,然后将任何无法访问的内容标记为删除。

如果文件R.type.name被引用(非最终资源引用,例如在库中),或者在代码中引用相应的int值,我们通过ASM查看输出的代码来检查这一点。有一个复杂的问题是代码可以通过Resources#getIdentifier(String,String,String),通过传递资源的名称去查查找资源,为了处理这种情况,我们使用ClassVisitor来查看是否有对特定Resources#getIdentifier方法的任何调用如果没有,很好,使用分析是完全准确的。如果我们找到一个,我们检查 所有在应用程序中的任何位置找到的字符串常量,并查看是否有看起来有关。例如,如果我们找到字符串“string / foo”或“my.pkg:string / foo”,我们将标记名为foo的字符串资源(如果有)作为可能使用的字符串资源。

 

 一:

 

 SdkConstants.FN_RESOURCE_CLASS = R.java

可以看出它找到的是R.java文件,然后调用parseResourceClass(file);方法,将R.java中的资源都标记为可达,加入表中。

 

recordMapping(mProguardMapping)

解析混淆文件,将现在的把映射存于表中

 

 

 

recordManifestUsages(mMergedManifest);

分析Manifest文件,将找到的引用标记为可达,例如:启动图标

 

AndResGuard的使用

AndResGuard是何物?

AndResGuard是一个帮助你缩小APK大小的工具,他的原理类似Java Proguard,但是只针对资源。他会将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a。一般用于给apk瘦身。

具体请看:

https://github.com/shwenzhang/AndResGuard/blob/master/README.zh-cn.md

我们解压缩apk后可以看到这个文件

resources.arsc

这个文件是编译后的二进制资源文件,里面是id-name-value的一个map,即id与资源名称的一个映射,如:

AndResGuard。它将资源的名称进行了混淆,所以可以用它对resources.arsc进行优化,只是具体优化效果与编码方式、id数量、平均减少命名长度有关。

例如:

表一:

 

表二:

表二是经过优化后的,我们一眼就可以知道表2肯定比表1存储的字符要小,所以整个文件的大小肯定也要小一些,因此就达到了瘦身的效果。

 

总的来说它可以将res/drawable/activity_advanced_setting_for_test变为r/d/a,达到混淆与减少体积的效果,也增加了反编译后阅读代码的难度,安全性高

 

那么怎么使用呢?

第一种方式:AS集成

1、在项目的build.gradle中添加依赖如下:

 

 

2、在app下建立gradle文件and_res_guard.gradle,里面的内容如下:

其中whiteList(白名单)中指定不需要进行混淆的资源路径规则,所有使用getIdentifier访问的资源都需要加入白名单,因为使用这种方式访问的资源,在代码中写死了id,比如:
int mipmapId = getResources().getIdentifier(“ic_launcher”, “mipmap”, getPackageName());,如果不加入白名单的话会报找不到资源的错误。
 
还有一些第三方SDK,因为有些SDK的代码中也用这种方式引用到对应的资源文件,如果对其进行混淆,会导致找不到对应资源文件,出现crash,所以不能对其资源文件进行混淆。像友盟这种喜欢用反射获取资源的SDK就是一个坑(友盟的SDK就是坑王)!对于app启动图标这样的icon可以不做混淆,推荐将其放入白名单里

可以在white_list.md查看更多sdk的白名单配置,也欢迎大家PR自己的白名单

 

compressFilePattern和compressFilePattern中的通配符支持? + *


白名单机制只作用于资源的specsName,不会keep住资源的路径。如果想keep住资源原有的物理路径,可以使用mappingFile。 例如我想keep住icon所有folder,可以在mappingFile指向的文件添加:

 

注意!

  1. 如果不是对APK size有极致的需求,请不要把resource.asrc添加进compressFilePattern. (#84 #233)
  2. 对于发布于Google Play的APP,建议不要使用7Zip压缩,因为这个会导致Google Play的优化Patch算法失效. (#233)

 

3、在app的build.gradle中引入:

编译一下便可以在gradle的task任务列表里看到混淆任务

使用:

双击对应的task可以编译debug包,release包。混淆后的apk生成在build/output/apk/AndResGuard_*目录中,默认会生成4种apk,我们选择签名、压缩、对齐后的apk即可,后缀名是*_signed_7zip_aligned.apk

 

第二种方式:指令集成

 1、下载AndResGuard过程:

https://github.com/shwenzhang/AndResGuard

 

 

2、解压,可以看到这个文件,进入这个文件

 

文件夹里面有这么一些东西:

 

文件作用:

jar包:用来执行命令的时候使用

build_apk.bat :windows执行脚本

build_apk.sh:linux执行脚本,由于我使用的是windows系统,所以就。