make a little difference

[置顶]Micolog部落——一个Micologer的聚集地

日期:2010.11.09
标签: micolog 个人项目
分类: what

Android打包成apk流程简析

最近研究了一下Android打包apk的流程,简要描述一下。

我们可以跟随android的sdk目录下的tools/ant/build.xml文件的描述来一窥打包apk流程究竟。

首先这是用ant打包的过程,eclipse打包流程应该大抵一样。用ant打包前,如果project不是用ant创建的,先需要执行android update project -p /path/to/project。该命令会在project目录下生成相应的配置文件。如build.xml(与sdk下的那个不同),project.properties,local.properties等。生成的几个文件都是项目相关的配置参数等。ant打包需要build.xml配置文件,project下的配置文件包含sdk/tools/ant/build.xml文件。

project下的build.xml相对简单了,设定一些project属性,引用了project.properties,local.properties等文件。build.xml设定了ant的默认行为(target)是help。显示相应帮助。

sdk/tools/ant/build.xml相对复杂很多,一共有1300+行。乍看很多,粗粗分析开来,一块一块也相对较为明朗。即便我对ant配置一知半解,也看个大概明白。<property>就是设定一些key-value的属性,<target>就是ant对应的目标,类似于Makefile的那个目标。

这个build.xml相应地方做了良好的注释。这个文件内容分布大概如下:

1.定义一些覆盖默认设置的属性。还是有一些挺有意思的属性值得一看的,比如android.package.excludes,设定该属性可以排除编译一部分代码。再比如version.code,version.name可以替换AndroidManifest.xml中的相关版本内容。

2.自定义了一些任务。凭借我的揣测,这是ant提供的一些扩展,这些任务是通过${sdk.dir}/tools/lib/anttasks.jar这个jar包来导入定义的。定义了很多,列举一二,如com.android.ant.NewSetupTask,com.android.ant.AaptExecTask,com.android.ant.IfElseTask等等。

3.其它属性。主要是编译流程用到的一些变量属性,包含输入目录,输出目录,工具位置等。这里可以看见一些经常看到用到的目录和工具。比如src自不用多说,还有gen目录,libs目录等。此处加一段故事,很久之前,我是用Eclipse好的project在带我的bear那里用ant编译有问题,问题在于我将外部的jar包放在了lib目录下,ant编译的时候会找不到这个jar包了,但是放在libs里就可以了,到了这就可以解释了,因为build.xml暗暗的定义了这个默认的文件夹的名字。再插一句最新的adt也支持放在libs中而不需要做多余路径设定了。

4.宏定义。定义了一些多次用的流程为宏。比如do-only-if-not-library,如果不是android library project就怎样做。package-helper等等。

5.Build过程的一些Target。第一个是nodeps,很简单是设定了一个属性,来设定targets间是否存在依赖关系。然后有-per-clean,clean清空bin,gen目录做了这么些事。(前面加-的target,从某种程度来说是private的target,因为输入ant -xxx的时候,-xxx会当作ant的参数,而不是build的目标)

然后就涉及打包流程了。-setup,做一些初始化工作,创建bin目录等,设定一些属性等。-build-setup,也是做一些build的初始化工作。然后是-per-build这个是空的,这个是用来给用户做一些自定义设定或者实现预留的,后面类似这样预留的无内容的部分我就不讲了。然后是-code-gen,这个就有意思一些了,最为人熟知的android中的R文件就要生成了,生成R文件在这个过程做了定义,通过aapt程序(这个程序很有意思,这里略去不讲,没准下回会讲)生成R文件。-code-gen过程还有一些重量级的东西。比如生成用于进程通信的aidl文件相应类的生成,还有renderscript的生成。还有BuildConfig文件的生成。然后就是激动人心的compile编译过程了,就是javac做编译,生成class文件。这里有些令人激动的事情。就是Android为了代码复用,提出了library project的概念,某种程度上还不错,但是扩展能力有限,还是有些坑爹的,而且有一些限制。此处再插故事一则,我有3个project,分别标记为a,b,c,a,b分别是library project,然后依赖是c=>b=>a(c依赖于b,b依赖于a),我想对b做代码混淆,但是出现一些依赖问题。因为android的现在的这个build过程并不把在前面-code-gen生成的依赖的子library的R文件编译到这个library自己的这里,所以在混淆b的时候,会出现a中的R无法找到的问题,因为a中的R会被便已到c中,很混乱吧。事情是这样,但是错误在自己,因为对android的library和整个project的打包流程不清楚,所以造成了上面的窘境。其实根本不用混淆b工程,因为b工程的class会最终放到c中,最后在c中混淆就行了。晕了。。。

继续说打包流程,编译完了,然后就是-obfuscate混淆过程了,混淆默认只在release模式下才进行,内部有一些判断。关于混淆的一些介绍,可以参见我的上一篇博文《ProGuard总结和混淆Android代码中遇到的问题的解决方法以及寻找getSomething游戏》。然后是-dex,转.class到.dex过程。开发Android的都知道,Android没有用标准的jvm,而是自己专门为移动设备优化实现的dalvik vm。这个过程会把所有的.class文件打包到一个.dex文件中。

至此,代码build过程就结束了。全都结束了吗?还没有,Android还有编译资源,再说还没打成apk包呢。

跟资源处理有关的过程先是-crunch这个过程就是对png图片做一些压缩处理。具体怎么处理的咱就不细究了,想知道的话可以研究源代码去。然后又到了激动人心的资源打包过程-package-resources,这个过程就是用牛x的aapt,将所有res目录下的资源文件manifest等文件打包编译处理。这其中有一个对于.9.png的说明。我们知道.9.png的图片是带一圈透明画有黑点的图片。这个过程aapt会将.9.png处理,将透明带黑点写入二进制。和原来的图是不一样了的,逆向工程的时候对.9.png有很多限制。apktool的wiki上有相关说明。xda论坛上也有相关讨论。还有一点是将普通.png改为.9.png的后,ant全程打包是不会有问题的。但是aapt确实对这种情况做了抛出异常处理,所以我猜想是在crunch时做了什么处理,还未证实。

资源文件也build好了,然后对于debug或release。就是打包成apk。然后给apk签名,签名方式上两种模式有些不同处理。debug会进行debug签名,release需要你提供你的签名文件等。然后或进行zipalign操作,字节对齐。

至此,整个过程就结束了。

还有一些关于测试和安装卸载的target就不写了。

重点过程,加粗标识了,看一遍加粗部分,基本就能知道个大概了

日期:2012.05.17
标签: Android
分类: how
0 Comments

ProGuard总结和混淆Android代码中遇到的问题的解决方法以及寻找getSomething游戏

先说点题外的,前一秒我心想干点什么呢,想到写篇博文吧,最近写的频率有点低了。另外标题有点长了。

进正文。

前些日子几次遇到ProGuard的问题,想偷个懒,没好好RTFM,后来通读了一下ProGuard的Manual,有点收获,总结一下。

主要是读了Usage部分,http://proguard.sourceforge.net/#manual/usage.html

命令:java -jar proguard.jar options ... 或 java -jar proguard.jar @myconfig.pro(myconfig.pro是配置文件)。Android提供的ant脚本把这个包含了进去,ADT也相应做了处理,所以基本不会直接用到这个。proguard包含在android sdk的tools目录下。

options或者配置文件设定了proguard的参数,分为Input/Output Options,Input/Output Options,Shrinking Options,Optimization Options,Obfuscation Options,Preverification Options,General Options 这些可选的参数。

从配置选项中其实可以看出,proguard有几部分的功能,Shrinking,Optimization,Obfuscation,Preverification。

1. Shrinking

就是缩减代码,他的工作是把代码中没被引用或者依赖的类、类成员删掉。可以通过Keep参数来设定保存。我之前就遇到这么一个问题,这里简称为“寻找getSomething游戏”(这个问题几乎会贯穿全文),我写了这么一段代码,大意如下:

public class JavascriptInterface {
    void getSomething(String something) {}
}

webview.addJavascriptObject(new JavascriptInterface(), "jsi");
webview.loadUrl("javascript:window.jsi.getSomething("hello")");

就是通过js给java传个值。然后打包运行正常,再然后使用proguard处理打包,结果不正常,提示大意为object没有getSomething方法。

出了什么问题呢,因为android的proguard默认配置是开启Shrinking的,所以结果是getSomething(String)方法被删掉了,我通过反编译打出来的apk包也证实了这一点。

解决方法是,在配置文件中加入keep

-keep public class yourpackagename.JavascriptInterface

对于keep有几个类似的选项,下面的表格抄自:http://proguard.sourceforge.net/#manual/usage.html

Keep From being removed or renamed From being renamed
Classes and class members -keep -keepnames
Class members only -keepclassmembers -keepclassmembernames
Classes and class members, if class members present -keepclasseswithmembers -keepclasseswithmembernames

我就不解释了。

2. Optimization

代码优化,说是bytecode层级的优化,具体怎么优化的我就不知道,而且Android默认配置也没开起这个。

3. Obfuscation

这个就是传说中的混淆了,可以通过-dontobfuscate关闭(关闭为什么还用proguard呢?)什么是混淆呢,就是把类和类成员 (包括变量和函数)的名字替换成相应的随机字符,大大增加别人解包破解你代码的难度。混淆过程生成mapping文件,记录每个类和成员被替换称什么随机字符了,也可以自己提供一些生成随机字符的规则,这里提供了很多选项,很有意思。

前面说道“寻找getSomething游戏”的例子,其实象刚才那样做并没有搞定这个问题,这里一个重要的概念是,单写一行keep并不能让proguard不做成员变量的混淆处理,而只是不被删掉。所以getSomething作为一个类成员方法依然会被混淆,变成了一个随机字符,比如a,那么这句代码:webview.loadUrl("javascript:window.jsi.getSomething("hello")"); 显然不再能正常使用了,因为它已经找不到getSomething了。

过程中我反复解包,由于认为keep就可以防止混淆,觉得无解,因我解包发现每次getSomething都变成了a,所以进行了一种很狗屎的方法的尝试,将webview.loadUrl("javascript:window.jsi.getSomething("hello")");换成webview.loadUrl("javascript:window.jsi.a("hello")");,把getSomething直接写成了a。。。但是由于我的实际JavascriptInterface类里面还有一些别的东西,有些东西也会被混淆成a,所以居然连狗屎运都没有。

后来我发现-useuniqueclassmembernames这个参数,顾名思义,可以让类成员使用唯一的名字,于是我给getSomething改成了后来混淆后的唯一的名字,终于在寻找getSomething的游戏中,找到了它,虽然它已经不叫getSomething了。。。

还是觉得这个方法太屎了,胜之不武。

关于keep有个复杂的语法,下面依然抄自ProGuard文档。

[@annotationtype] [[!]public|final|abstract|@ ...] [!]interface|class|enum classname
    [extends|implements [@annotationtype] classname]
[{
    [@annotationtype] [[!]public|private|protected|static|volatile|transient ...] <fields> |
                                                                      (fieldtype fieldname);
    [@annotationtype] [[!]public|private|protected|static|synchronized|native|abstract|strictfp ...] <methods> |
                                                                                           <init>(argumenttype,...) |
                                                                                           classname(argumenttype,...) |
                                                                                           (returntype methodname(argumenttype,...));
    [@annotationtype] [[!]public|private|protected|static ... ] *;
    ...
}]

。。。

我之前看了这段就跳过了。后来耐心看下还是很容易看懂的,而且这个部分后面有个说明。

这些符号其实很常见了,“|” 表示或关系,“!”表示非,“[]”表示可选,“...”代表等等,黑色的部分是关键字。

之前-keep public class yourpackagename.JavascriptInterface 这样写,注意语法有个花括号,里面是用来说明成员变量是否keep的,可以写一个范围,比如写<fields>就是所有字段不被混淆,<methods>就是所有方法不被混淆,*就是所有都不被混淆。还可以单独指出哪个函数不被混淆,比如对于“寻找getSomething游戏”,可以这样写:

-keep public class yourpackagename.JavascriptInterface {
  void getSomething(java.lang.String)
}

这样,getSomething函数就不会被混淆了。这里一个值得注意的问题是,所有类都要写全称,就是包名.类名,String要写成java.lang.String,我最开始就只写了String,结果是还别混淆了,郁闷了很久。

好了,“寻找getSomething游戏”完胜了。。。它再也不会找不到getSomething了。

4. Preverification

预验证,在载入类之前的验证,《Android前向兼容的几个问题》里面说的大概是这个,Android的ProGuard配置也没有开启这个,我也不是很清楚,就不说了。

 

还有很多选项,不在赘述,还是RTFM吧! 

 

日期:2012.04.27
标签: Android ProGuard
分类: summary
0 Comments

使用openvswitch配置xen的虚拟化网络,实现网络隔离的故事

这事本没想难易,结果是花掉了我好几个周末的时间才搞通。

最开始对网络配置各种不懂,我偶有网络课程白学的想法。而且这次又玩虚幻,各种虚拟网络,各种配置,搞得头儿晕晕。实现的目的其实很简单:就是两台物理主机A、B,每台物理主机上各有采用Xen虚拟化的两个VM,可分别标记为A1,A2,B1,B2。可以让这4个虚拟机之间实现网络联通与隔离,消除掉物理主机的概念,好像所有虚拟机都运行在一台物理主机上一样。

说一下我的实验环境,一台物理主机,然后采用Vmware虚拟了两个主机,然后每个主机再通过Xen虚拟两个虚拟机。有如兔子生娃,一生俩,俩生四。这样我就有了4个虚拟主机。

(话说当初我的实验环境更为复杂,我搞到一台远程Windows主机,然后远程上去,在上面用Vmware建了个Centos的虚拟机,然后又用Xen虚拟了两个VM,搞得尤为复杂,后来遇到一些问题,遂搞到一台机器上了,这个稍后再说)

有了实验环境后,就是安装一些软件,首先我使用的是Centos5.x系列的操作系统,然后使用了Xen3.x版本的Xen。(话说这玩应当初我尝试了N多版本,各种问题,各种装操作系统,和配置Xen环境,各种等待的过程)

然后是Xen的虚拟化网络,Xen的虚拟化网络有三种方式,默认的bridge,还有route和nat(但是名字这我看了几个文档  ,各自介绍又有点区别)。

基础知识我这就不说了(前面已经跑偏不少了),几种网络方式可以参看如下几个网址:

  • http://wiki.kartbuilding.net/index.php/Xen_Networking#Overview_of_Networks_with_Xen
  • http://wiki.xen.org/wiki/Xen_Networking
  • http://wiki.xen.org/wiki/HostConfiguration/Networking

而且Xen的虚拟化网络搞出一堆设备、接口出来,如:eth0,peth0,vifx.x,veth,xenbr0等等。

这些东西其实主要是通过xen的几个网络脚本搞出来的,在/etc/xen/scripts目录下,有一些网络脚本,例如network-bridge,桥接方式,主机用到的脚本,仔细研读这个脚本还是很有好处的,能了解xen初始化虚拟网的过程。

有几篇博文不错,可以帮助理解,如下:

  • http://blog.csdn.net/sahusoft/article/details/5996453
  • https://sites.google.com/site/gnawux/Home/%E6%9C%AC%E4%BA%BA%E8%AF%91%E4%BD%9C/xen%E7%BB%84%E7%BD%91#network-bridge_%E8%84%9A%E6%9C%AC

好了,现在说openvswitch,这个是在查找网络隔离解决方案中,找到的,这个东西可以说是虚拟的交换机。

这篇博文介绍了一下openvswitch:http://bengo.blog.51cto.com/4504843/791213,可以大体了解一下。openvswitch有个http://openvswitch.org/support/config-cookbooks/vlan-configuration-cookbook/这么个文档,于是我一下相中了它,这个功能整合我意,于是开始鼓捣openvswitch,又是各种配置,但是怎么配置都不行,虽说openvswitch官方声明支持xen,但是缺乏良好的文档来说明怎么和xen结合起来,或者说根本没有文档,我翻看了一些邮件列表,找到一两个相关的讨论,但是也都是没有结果。

配置openvswitch虚拟网络,经常容易把xen的虚拟网络搞毁,终于偶有一次惊奇的发现使用openvswitch的虚拟网络,替换了linux bridge的网桥网络,一阵窃喜,不幸的是到隔了一周的时间,到下一个周末的时候,忘记上次是怎么配置的了,所以又是乱搞。搞了又搞,终于鼓捣成了,然后下一周又忘了,这过程中我几次想写个文,记录一下配置的过程,但是都没写,侥幸想这次应该不会忘了,于是就又忘了。但其实所谓忘了,不过是还没有了解其中的原理。后来驾轻就熟后,往往怎么搞都没有问题了。

后来又遇到一个问题,前面提到了,就是交换机truck口的问题,之前是两台vmware虚拟机,而且一台还是远程的,网段还小有区别,虽然是在一个网段里,后来问老师,老师说这样会有问题,于是搞到一台机器上,终于成功成仁。(话说最开始卡在网络上的时候,我一度狂乱,一天给老师发了10封左右邮件,老师当时1封没回,我就1封接1封的发,有什么想法就发过去)

写到这里我已经不想写什么解决方案,我发现这故事本身往往很精彩,而最后不过是几句话而已。

于是,最后我无耻的将标题最后加了“的故事”三个字。

未完,待续。。。

日期:2012.04.06
标签: xen 虚拟化 openvswitch
分类: how
0 Comments

加速Android开发,玩转Eclipse

有用的快捷键

  • CTRL+SHIFT+O——整理Import,发现有一堆类没有导入吗,那么试试这个
  • CTRL+1——快速修正,发现有个红叉叉吗,那么试试这个
  • CTRL+SHIFT+T——想打开一个类文件吗,那么试试这个(你还在去你的project树中去一个一个查找吗?)
  • CTRL+E——你打开了一堆文件,想切换到某个打开的文件吗,那么试试这个(你还在去你的tabs栏中一个一个查找吗?)
  • CTRL + F11——你不想每次费力的去点击运行按钮吗,那么试试这个
  • CTRL + SHIFT + F——乱哄哄的代码对齐,空行,你不能忍受了吗?那么试试这个
  • Ctrl+O——你还在用Ctrl+F查找定位函数吗?那么试试这个吧
  • Ctrl+T——想快速看看这个类的继承关系吗?那么试试这个
  • Ctrl+/(或者Ctrl+Shift+C)——你还在输入//来吧暂时不用的代码注释掉吗?那么试试这个
  • Alt+/——智能提示,你不知道吗?
  • Alt+Shift+R——名字起的不好,想改改?试试这个
  • Alt+Shift+T——与上一个类似的重构功能还有什么,试试这个
  • Alt+←,Alt+→——刚才编辑哪了?试试这个

以上都是我常用的快捷键,包好用,应该还有一些,现在想不起来了。

代码模版

当你输入if或者for这种句子的时候,是否会自动补全,选择后一段代码就出来了呢,然后可以在预定位置输入替换词。这个就是代码模板了。

比如我们常常打日志,然后要输入Log.d(TAG, "xxxxx");其中xxxxx是要替换的内容,那么你还在一个字一个字的输入吗?添加一个模版,比如定义名字为logd,那么当你输入完logd,然后自动补全,看,Log.d(TAG, "");已经出来了哦,而且光标不是定位在末尾哦,而是在两个冒号中间哦,直接输入要输出的日志就好了哦,超级方便的吧。

如何添加:打开Preferences/Java/Editor/Templates,然后就可以添加了相应的模版就好了,比如logd的模版是:Log.d(TAG, "${cursor}");

抛砖引玉了。

快速的移动、操作——Vrapper

你用Vim吗,那么你应该尝试一下Vrapper。Vrapper提供了基本的Vim编辑器特性,光标的快速移动,迅速定位到你想要的位置,区块。行首,句首,句中,按字,按字符,按行,F,迅速移动,定位到所需代码处,然后进行相应的操作。如果是Vim党,那就不用我多说了(尽管不是所有features都支持,但是基本够用,详见下面网址的官方介绍)

我一直在寻找Eclipse与Vim的结合品,目前Vrapper是基于Eclipse插件机制相对较为稳定、好用、轻量的一个。我已经使用了10个月左右,倾情推荐。

网址:http://vrapper.sourceforge.net/home/

正则

正则无处不在,如果你精通它,那么查找、替换的速度将提升一个数量级。

条例清晰,如流水一般

显然,这是王道,混乱的代码是侵蚀时间的毒瘤,修复bug是最浪费时间,也是减慢开发速度的毒虫。当脑袋想清楚一切的逻辑后,这有助于减少bug,便于增加新的feature。如流水一般,清晰,透明,顺畅。总结一个字,爽。

日期:2012.03.05
标签: Android Eclipse
分类: how
1 Comments

我为什么写博客

很多时候没有为什么,写就是了。写与不写的区别就是:写就留下了,不写就忘却了。

日期:2012.02.28
标签: 为什么 博客
分类: why
0 Comments

如何花费1分钟写一篇博文

动手写,然后少写点,就像这样。注:这是一片正经的How文,你仔细反复读一句。
日期:2012.02.26
标签:
分类: how
1 Comments