用IntelliJ代替Eclipse,调试Android Source codes
本文所说的调试Android Source Code,是指区别于一般的Android App,是对Android系统源代码,包括自带的Apps,比如Mms, Contacts, Dialer等的开发和调试,需要对IDE做特殊的配置。
其实整个配置的目的,是2个方面:
1,在调试的时候,可以看到相关代码的引用,即看到符号表,这样才可以step into进去debug。
2,要能用remote debug的方式,attach到手机上运行的那些app的process上,比如com.android.mms。这就是区别于一般Android app调试的方面。
用Eclipse调试Android Source Code,可以说是标准的Android开发的做法,如何配置,参考:http://source.android.com/source/using-eclipse.html。
但是Eclipse太慢了,在编辑输入的时候,那个一个字符一个字符蹦出来的感觉,真的是无法忍受。所以开始转向IntelliJ。Eclipse和IntelliJ的对比,可以Google下,有很多文章可以参考,而实际使用的体验,差别也是比较明显的。总的来说,Eclipse有点大,对于Android/Java的开发来说,不如IntelliJ专一,性能也是比后者差一截。如果我们想开发Android App,进行Java、或者Scala这样的JVM Lanauge(如何用Scala开发Android App是另一个话题),的开发,用IntelliJ是明智的。
其实在Android的Source Code Tree中,有一些IDE的配置文件,在它的相对路径development\ide下,其中有Eclipse,IntelliJ, emacs 和 xcode的配置。在上面用eclipse的链接中,就提到了这个目录,用到了eclipse目录下的.classpath文件。
而对于IntelliJ来说,则不需要这个.classpath文件,只需要参考。而那个intellij子目录中的内容,我完全不知道如何用,本文的做法没有用到它们。
虽然IntelliJ没有那个.classpath文件可以用,但是实际配置起来,也是不费事的。而且,它对android source code的那个编译输出目录out的依赖,也比eclipse的配置低多了。下面,直接上steps,在步骤中,我会说到和Eclipse的不同,以及一些tips:
1,拿到整个Android Source Code,首先要编译成功,这会产生Android/out这个目录,IntelliJ和Eclipse相比,只需要其中的一个目录out\target\common\R。其实这个目录不是固定的,许多oem厂商,可以根据不同的configration,配置这个输出目录,这样实际的路径可能会是:out\xxxxx\target\common\R,总是,我们需要这个R。实际上IntelliJ可以自动扫描出来,我们到时候要勾选相应的这个路径。对比配置eclipse需要的那个.classpath,intellij降低了对out目录的依赖,这样的好处在于,由于out目录是会多变的,有时甚至需要删除它重新来过,所以还是尽量不依赖为好。
2,启动IntelliJ 11.1.4,我们用Create New Project的方式。有一种情况,即用Open Project的方式,去打开一个已经存在的Eclipse项目。比如我们已经用那个.classpath文件,按照开头的链接,配置了Eclipse的环境,所以我们想用IntelliJ中兼容Eclipse项目的方式打开这个已经配置好的项目。不幸,这种方式是不行的,试验下来,intellij在open的过程最后,实际无法打开已有的eclipse项目。
[Update]在IntelliJ 12中,这个方式变化了:
要用Import Project,然后直接选定android source codes的根目录:
然后是:
选择"Create project from existing sources",然后点Next。
这里不用再选择IntelliJ Project的文件格式,然后Next,下面就接着第5步,开始扫描Sources。后面的截图就不更新了,类似的。
3,好吧,即使之前已经按照eclipse的方式,配置了Android Source Code,没关系,IntelliJ的配置与之不冲突。现在点击Create New Project,开始下一步。
这里,我们仍然不能选择第二项:“Import project form external model”,试图从已有的Eclipse项目中去创建。要选择第三项。next。
4,选择好你的Android Source Code的根目录android所在的路径,已经起一个项目名称。这里好像intellij的project files location是可以和源代码分开放置的,实际上不行,第二项一定要设置为源代码所在的路径,后面会自动扫描其中的source files。第三项是intellij项目的文件格式,默认的就好。喜欢统一管理的,可以换成.idea子目录的形式。next。
5,然后IntelliJ开始searching for soruces,耐心等待吧,可以去做下别的事情了,要等大约半个小时呢(当然这个也要机器的硬件配置)。其实如果熟练了,可以在这一步stop,然后自己选择要加入的module目录。
6,自动扫描完成后,就会跳出来:
默认是Mark All的,其实我们不需要这么多。所以先Unmark All,然后勾选我们需要的。这时,我们就可以参考那个.classpath文件了。
打开这个文件,我们可以看到其中有2大部分, src和lib,其中src:packages/app, packages/providers, frameworks/base, frameworks/ex, frameworks/opt, frameworks/supports, development, libcore, external, 还有out下的那些;lib:主要就是out中的了,以及一个package中的lib。
在这一步,我们只看src的部分,后面还要看lib的部分。
所以根据我们的需要,packages/app中只需要部分勾选,development就全部不要选了,还有out中的内容,除了那个out/target/common/R要勾,其它的都不用,剩下的,我建议都勾上,因为基本都是公共的部分。记得一点,对于intellij的project来说,module越多,运行起来越慢,所以还是按需来选。将来也可以根据需要增减。
请注意这个out/target/common/R,上面已经提到,这个路径有可能会由于oem修改了编译脚本而变化,请根据具体out的路径选择,但是intellij已经扫描出来了,只要辨识出来即可。
另外,这个.classpath文件的内容,是随着android的升级,有可能变化的,请根据实际内容未准。
再有,如果你还需要调试这个.classpath上没有列出的module,也可以在此时额外勾选相应的内容。
比如,packages/apps/下,只选择了Mms和Phone。
勾选好之后,next。
7,开始searing library,又要等待了。
8,自动扫描完成后,自动切换到如下界面:
这里没有UnMark All的勾选,直接点选第一条,然后按shift,再点选最后一条(或者Ctrl +A,但IntelliJ响应很慢,因为条目很多),然后按下空格(这里要等待,IntelliJ的性能还要继续改进),来UnMark All。然后根据.classpath来勾选:
然后next。
9,开始searching for modules。还是需要等待一段时间,不过相比之前的2步,就短多了,因为我们之前勾选的sources不多。
10,自动完成后,确定我们想创建的IntelliJ Modules。
这一步里,就是上面我们勾选的sources,intellij根据source建立module,你也可以去掉某些source,这样也就无法在intelliJ中调试它们了。勾选的含义,intelliJ会在对应的目录下建立module文件.iml。
如上图,common和common1,我们最好进行适当的改名操作。
另外,我们需要去掉Base的勾选,因为我们只需要具体的base中的内容,比如base/core。
把刚才的那个Common修改成R。这是一个Tip。将来如果out被删除,要重新创建一个Module时,同样的路径,IntelliJ会命名为R,不再是Common了,所以这里事先修改为R,方便将来。
另外,注意到这里R的实际路径是有变化的,多了一个caet2ur_qpl就是因为oem修改了编译的脚本所致。
完成后,next。
11,接下来,searching for frameworks。这一步也要等待一段时间。
12,自动完成后,切换界面,intelliJ会显示出检测到的framework,这里都是Android类型的modules。我们只要看下,就可以点finish结束了。
接下来,intelliJ会建立项目的索引,这同样耗时。等索引结束后,还需要做后面的配置修改工作。
13,如果源代码中已经受vcs工具管理,比如git,就会出现这样的界面:
我们就需要修改一些配置,去掉这些烦人的提示。
在等待索引完成的时候,进一步的配置前,我们看看上面的操作对Android的代码树有什么影响:
上面内容不是全部,简单的说,IntelliJ建立了项目文件.ipr和.iws,以及在我们勾选的module下建立了.iml文件。
与eclipse相比,我觉得这是IntelliJ的缺点。eclipse除了使用那个.classpath文件之外,就没有再额外创建多余的文件了,intelliJ创建了多余的module文件,如果哪个module的代码混乱,从而被删除重新获取的话,那在intellij的项目里,就需要通过创建一个新的module的方式,来重新加入这个module。
14,intelliJ索引完成后,我们先去掉刚才vcs工具的提示:
左侧有一个Version Control,然后拖动右侧的滚动条到最下,去掉“Notify about VCS root errors”的勾选,然后点Apply。然后点击OK,关闭这个Settings界面。
15,在左侧的Project Panel中点选任意一个,然后按F4,启动Project Structure界面,开始后面的配置。
首先解决这个循环依赖的问题。鼠标移动过去,会有hints。
1) 点选左侧的luni的main(可能已经被改名),右侧的dependencies中去掉对xml和davik的依赖。
2) 点选左侧的core,右侧的dependencies中去掉对keystore, location, opengl, wifi, telephony, media, graphics, drm等的依赖,只保留libcore和R就行了。
3) 点选左侧的opengl,去掉对graphics的依赖。
4) 点选左侧的Filterfw,去掉对Filterpacks的依赖。
5) 分别点选左侧的Wifi,Voip, Location, Media,去掉对Telephony的依赖(若存在的话)。
经过上面的操作,Project Structure界面上的循环依赖的错误就没有了。
16,修改依赖的顺序。注意到中间那些前面有+号的模块,它们是有AndroidManifest.xml的android模块(实际上,有的带+号的模块确实没有这个文件)。而没有+号的,实际上是Java模块,依赖的是Java的SDK。
比如上图中的Calendar模块,它是一个android模块,所以它默认依赖的对象中,Android Framework是第一个,我们需要用最后侧的向下按钮,把对Android Framework的依赖降低到最后:
对每一个android模块,都要做这个动作。有点繁琐,呵呵。为什么要做这个呢?因为对于Android Source Code来说,它本身就有所有Android Framework的Source,所以对于已近安装好的Android SDK的依赖要降低优先级,这样Android Source Codes中会首先去找本身就带的实现,而不是Android SDK中的实现了。
额外说一点,Core这个android模块,是核心的基础模块,也应该把它的顺序升高,放到最上面。而R模块应该仅次于Core模块。Core依赖的libcore,可以放到比Core还高的位置上。
17,修改android模块的gen目录。IntelliJ会在每个android模块下创建一个多余的gen目录,这会导致Android Source Code的编译会有问题,我们可以把它们统一设置到out目录中。
先看下多出来的gen目录,跟每个android模块的.iml是放在同一个目录下的。
上面的内容不是全部。那在Project Structure界面中要做的修改(以上面的Calendar为例):
去掉默认的勾选,选择下面那个“Use module compile output path”:
然后点击Apply,再拷贝"Output path:"后的路径,打开Android的设置,切换到Compiler标签页:
将AIDL Compiler和AAPT Compiler的Destination directory的路径,除了最后的gen,前面的路径改为上面Output path中的路径,如下:
然后点击Apply。若有提示不存在AndroidManifest.xml的警告,直接OK。
后面对每一个Android模块都做同样的操作。
全部做完后,关掉Project Structure界面,然后使用Linux的Shell命令清理掉已经存在的那些与.iml同目录下的gen目录。同时,查看out下的目录,会发现新设置的gen目录。
18,现在,所有的配置都完成了,我们首先看看某一个模块中的代码能够是否能够找到相应的reference code。
比如我们打开SmsManager.java这个文件,右侧都是蓝色或者黄色的颜色标示,这说明IntelliJ找到了这支源代码文件中调用了的其它API的实现,否则就会是红色标示,鼠标可以点击这些颜色标示,跳转到相应的地方。
说明一点,Android Source Code中有一些只有lib的库,可能是看不到源代码的,所以无论你如何添加module,也是找不到的。
19,下面,我们配置remote debug,然后,我们就可以调试Android System App或者Frameworks了。
如上图,intellij已经自动建立好了2个debug的配置项,但是对于Android System App来说,是不需要的,可以删除它们。点击Edit Configurations进入debug的设置界面。右上那个按钮,就是attach到手机里的process进行debug时用的,前提是我们要先配置好debug的设置。
在这个界面,首先删除IntelliJ自动建立的那几个配置项,然后点击+按钮,选择Remote:
如上图,修改2个地方,1个是name,是一个port。为什么是8700?请看本文开头的链接中的内容。然后OK,退出。
这时toolbar上的debug项也随着有了变化。接下来,打开android手机的usb调试,连接上pc,点击那个attach debug的按钮。首先是出现一个waiting for adb的进度条对话框,然后出现Choose Process的界面:
上图表现的是手机没有连接上pc时的情况,连接上之后会自动刷新,如下:
这里选择com.android.mms,表示想要调试android的系统自带的app Mms。点击Mms上的创建新消息的按钮,会挺在设置在onCreate的断点:
OK,接下来,开始尝试step over, step into等调试操作。
经过几步F8(step over),到达initResourceRefs()处,接下来,step into:
intellij正常跳转到initResourceRefs()的实现处的第一行。
OK,经过这个简单的示例,说明已经完全建立成功了在intellij上调试Android Source Code的环境。
本文到此结束,最后来一些总结:
- IntelliJ比Eclipse的性能要高一个层次。在Eclipse上输入,以及Code Compelete都很痛苦。
- IntelliJ比Eclipse的配置要繁琐很多。
- IntelliJ比Eclipse要“智能” 不少,用用就知道了。
- IntelliJ打开那些已有git管理的代码,自动支持git在ide里的操作,而Eclipse的git管理方式大不一样,比较繁琐。
- 稍微分析下IntelliJ建立项目的后的动作,它使用一个indexing的功能,可能是它的性能比Eclipse好的一个原因。