深入Android

来源:互联网 发布:亚马逊和淘宝哪个好 编辑:程序博客网 时间:2024/05/22 16:40

URL:

http://kb.cnblogs.com/page/57325/

转自 作者: duguguiyu  来源: 博客园

[1] 序及开篇
[2] 架构和学习
[3] 组件入门
[4] 组件调用
[5] 任务和进程

携来百侣曾游,忆往昔峥嵘岁月稠。 -- 《沁园春·长沙》
对于Android,我也算是老人了,所谓,有文有真想。正由于这段玩票经历,使得我在毕业后,鬼使神差的成为移动平台的一名码工,再次有机会放肆的拥抱Android。
2010开年,手上突然有了一把闲散时间,有机会进一步总结和学习Android。于是想再一次为Android写一系列的东西,这些东西来自于一些开发经验,对源码的学习和对Android的浅薄认识,也算是鞭笞自己学习的一种手段。
其下所有内容,预计有十数篇,抑或更多。基本和技术相关,也许会配有一些其他相关比较闲淡的话题。可能会有一些具象实例,但更多的可能是自己的一些理解和认知。所有一切,源自于妄自挖掘,难免有疏漏或误解,观者淡定。
以此为序,有心者,望共勉。

Android简史

人生若只初识,何事秋风悲画扇。 -- 《木兰辞》
要说当今移动平台的当红辣子鸡,Android说它是第二,也许没有别家敢认这个第一(好吧,iPhone,有意见就说...)。了解Android开发平台的过去和现状,除了往下看,另有便捷的方式就是在WikiPedia中键入Android,在这里,特此鸣谢GFW友情放生。

诞生

早在2005年7月,Google舞动着手中的美刀,收下了由Andy Rubin(传说中的Android之父...)等人创立的一家小公司,他们当时做的就是基于Linux内核的手机操作系统,也就是小时候的Android。经过Google多年打磨,Android在07年末,正二八经的粉墨登场开门接客。
自打一出生,Android便被钉板在富二代的角色上,不仅是因为老爹有钱的令人发指,也是因为其后有一帮金光闪耀的叔伯们保驾护航。这个叔伯群,便是响当当的开放手机联盟OHA(Open Handset Alliance)。这个联盟涵盖了中国移动、T-Mobile、Sprint这样的移动运营商,也包括HTC、Motolora、三星这些的手机制造商,同时还有以Google为代表的手机软件商,以Inter、Nvidia为标志的底层硬件厂商和Astonishing Tribe这样的商业运作公司(这公司是啥我也不晓得...)。作为后援团,他们理论上的任务,是各尽其长,全力捧红Android,实际上的任务是齐心协力,借Android东风赚一个盆钵满响。
当然,Android自所以被万众瞩目一炮走红,不仅仅是丫实在太有背景,同时,它也有这太多的新鲜的概念。Android是一个开源的平台(恩,真正的全面开源,是在发布后很久以后了...),它给那些捂着自家平台源码当宝的竞争对手们一记当头棒喝。Android自行研发了一套Java虚拟机,当时仅提供Java API的支持(NDK是更久以后的事情了...),号称为专为高端智能设计。Android开发环境支持所有主流操作系统平台,包括Windows,Linux,Mac,即便到今天,在手机开发中也是极其罕见的。Android的带头人Google,本身是做网络起家,Android内嵌大量Google网络应用,听上去就显得很酷。这所有的一切,共同造就了Android那鹤立鸡群,不染风尘的少侠形象。

造势

推出伊始,Google还有一个很震撼的推广举动,就是举行所谓的Android程序挑战赛(Android Developer Challenge, ADC)。整个比赛分成两场,第一场(ADC1)比赛,在没有任何真机问世,SDK还是个雏形的状况下,便鸣金开锣了。
比赛套路是无差别的群殴,基本概念是无论你来自何方(还是要满足美国法律要求和避嫌要求的),无论你想做些什么,也不管你是光杆司令还是流氓团伙,只要提交一个能在模拟器上跑起来的程序,就可参赛。而比赛只是对你作品进行参观评比,作品的所有权依然放在开发者的口袋中。
当然,这还不算什么创新,NB的是无比丰厚的奖金,整个ADC的奖金高达1千万美刀,每场各半,基本上首轮入围奖(前50)已经超越了那时候一般程序比赛的头名奖金,这对很多小公司和个人而言,无疑是具有很强吸引力的。于是,各路打酱油好手蜂拥而至,各论坛、博客、网站也七嘴八舌的讨论开来,一时间,满城风雨。
ADC1我也很厚颜无耻的参加了,结果当然可以预想,一毛钱都没摸上。回想整个过程,差距最大的并不是在技术上,而是认知上,我们玩的产品是人家几年前玩剩下的,说创新只是一抹笑谈。
当时觉着,Google太NB了,ADC这种车马未动粮草先行的招太华丽了,就这动静,不论比出来个啥结果,这1千万刀也掏值了。但时过境迁,现在回头来想,也许一切并不是看上去那么美。由于没有扎扎实实的真机摆出来,大家普遍抱着一种玩票的心理,真的敢不顾一切舍下身家性命押宝在Android上的尽在少数,这就锻造了Android平台很长一段时间的只见雷不见雨的局面。而等喧嚣过后,很多人热情消退,Google真端出Android真机的时候,还需要重新热场再来一次,也许,真的有些得不偿失。

困境

所有的东西现在来说,都是事后诸葛亮,只能听个响不能当个真。而真实的状况是ADC1很快进入囧境,由于架构设计上的种种原因,Google花了比预想多的多的时间做Android的优化工作,ADC1比赛被迫不断延期,彻底沦为懒婆娘的裹脚布。各路曾热捧Android的媒体,也不失时机的倒戈,亲手在自己画上的感叹号后面,重重画上了个大大的问号。
祸不单行,同样是由于Android的性能问题,虽然各路高手把Android移植到了不同的手机平台上,但传说中的GPhone一直难产中,使得人们不免有了胎死腹中的猜测。
与此同时,其他对手可一点也没闲着。iPhone很快宣布开放SDK,以此来勾引纯情的开发人员。Symbian被Nokia彻底收购,成为Nokia的自留地,开源计划也很快浮出水面。所有景象,对Android而言都犹如梦魇。

破茧

所有一切困境,都在G1发布后,渐渐消散了。2008年10月,第一款搭配Android平台的真机,搭载着无限光荣与梦想的HTC Dream正式发售,这就是注定要载入史册的G1。虽然比之当时绝代风华的iPhone,粗陋的G1犹如村姑遇上公主一般,但无论如何,G1让人们真真切切的看到了Android。这就犹如你家买的跳票N久的期房,终于见着了个毛胚房,那种感觉,除了踏实,找不到更适合的词汇了。
好事当然也会成双,G1它不是一个人在战斗。ADC1总算是落下帷幕,Android Market的也顺理成章的破茧而出,早期的应用,大都来自于ADC1的贡献。
Android也结束了伪开源的历史旅程,正式开发SDK的源码实现,搭配的是Apache的License,这种坦诚相见的感觉看上去很不错。
忠心耿耿的HTC,更是再接再励,在G1后,陆续发布了Magic(G2)和Hero(G3)。尤其是Hero的现身,惹得一阵小惊艳,HTC为Hero搭配的是基于Android改造UI的Sense系统,以华丽的界面风格赚足了眼球,也创了改造Android的先河。
在HTC高歌猛进的同时,猫在螳螂后的一群黄雀,也敌在动我也动了。摩托罗拉,三星,LG,华为,戴尔,联想等一干手机厂商纷纷跟进,各式各样的Android蜂拥而至。与此同时,其他嵌入式厂商也推陈出新,爱可视(Archos)发布了基于Android的平板设备,明基的Android上网本也是箭在弦上,而基于Android的手持电子书阅读设备也不断的被推出,庞大的Android联盟初现峥嵘。
为了避免同质化,各个厂商纷纷对Android进行的改造,摩托罗拉推出了Cliq,打得是SNS整合牌,三星的新系统也是被广泛期待,而中移动的OMS丑媳妇也要见婆娘了,打着整合移动服务牌@_@的OMS,以丑陋的外貌、低下的SDK版本和雷死人不偿命的宣传文案(绝口不提Android,只说自己做了大量很NB的工作,其实...,哎,咋就那么小家子气呢...)也算是招来大量眼球。
而还是没能耐住寂寞的Google,联手HTC,一同推出了至今只为止最重量级的Android手机:Nexus One。江湖有云:天下武功,无快不破。搭载了全新的Android 2.1,1G的CPU,史上最清晰的手机屏幕的Nexus One,快的是一塌糊涂迅雷不掩耳盗铃小叮当,在单机层面,第一次使得Android手机与iPhone掰手腕的能力(之前与iPhone的比较,都需要依靠集团力量,三英战吕布...)。
在各家厂商努力的同时,Android本身也没有闲着,版本从1.1,一步步进阶到了2.1,SDK的升级,伴随着大量性能、接口的改进,和功能的丰富,Android变得越来越快,越来越省电,越来越丰富,越来越多Google服务被嵌入@_@。由于Android SDK是基于Java的,即便虚拟机做的很是NB,在某些情况下,性能也是无法与原生的C++代码相提并论,于是,从1.5版起,除了SDK,Android还拥有了NDK(Native Develop Kit),它提供了一些C++的库和编译环境(库是真的很少...),开发人员可以基于C++写底层库,用Java写上层逻辑,通过混编的方式,兼得鱼和熊掌。
Android Market的发展也甚为迅猛,虽然和其鼻祖App Store相比,应用的规模和盈利能力还显得比较幼齿,但其涨势凶猛,发展趋势远胜于前辈。国内一些比较著名的手机软件,也纷纷拥有了Android版本的小弟,比如网易有道的出品的有道词典Android版(好吧,这是在插播广告,欢迎大家进行围观并提宝贵意见...)。

起飞

种种迹象表明,2010,也许就是姗姗来迟的Android元年。三星,moto,LG,HTC等多家手机制造厂商,都为2010年将推出的半数以上的手机搭配了Android。在国内,移动的OPhone,丑媳妇要正式揭开盖头了,惨烈是惨烈了一点,但聊胜于无,除了水货,2010毕竟至少多了条购买Android手机的道路。
软件开发方面,大家也从抱着双臂冷眼旁观的状态,进入到了一种伺机而动的战略准备阶段。前不久召开的moto开发者大会,惊现国内各领域的公司,试水开始,可见一斑。国内各个山寨的Market的,也越来越货源充足,下载量稳步上升,升温,就在当下。
而随着G2为首的Android水机价格火速下调,身边路边地铁边,可以看到越来越多的人,把玩着各式各样的Android手机,状况尤为喜人。
所以,2010,如果你有心,就做好准备吧。
 

Android架构和特征

千呼万唤始出来,犹抱琵琶半遮。 -- 《琵琶行》
虽贵为富二代,但Android要是没任何可圈点的地方,开不过70迈,在玲琅满目的手机平台竞争中,充其量也就做几个俯卧撑打一桶酱油,然后被落的远远的。说到底,出来混,靠的还是技术。

架构

从SDK文档中,偷来一幅Android平台的架构图,如上。在整个架构最底层红彤彤的部分,是Linux Kernel在移动平台的一个移植,它隐藏了硬件、网络等相关的细节,为上层提供了一个相对纯洁的统一接口。除非要做的是Android到不同设备的移植工作,否则对于大部分普通开发者而言,基本上是远观而不必亵玩的。Google一直强调,Android的底层实现异常NB,可移植性超强,暂没有功夫研读,实属遗憾。
靠上一层,是一些核心的和扩展的类库,它们都是原生的C++实现。在这一层,你可以看到很多熟悉的面孔,一如SQLite、WebKit、OpenGL,开源的力量与贡献由此可见。如果,该层类库需要被上层函数调用,就必须要通过JNI的导出相应的接口函数,否则就只能在层次内部自个把玩。
也是在这一层次上,还有为上层Java程序服务的运行时。Dalvik虚拟机,是Android的Java虚拟机,之所以不采用J2ME的虚拟机,一方面是因为J2ME的设计是为了低端机器而优化,而Dalvik则是为了高端一些的机器进行优化,提供更好的性能。另一方面,从商业角度来看,必须绕开J2ME虚拟机,Android才能彻底解放,想怎么开源就怎么开源,不再需要考虑License的问题。
再往上,终于有Java出没了。首先是框架层,这里包含所有开发所用的SDK类库,另外还有一些未公开接口的类库和实现,它们是整个Android平台核心机制的体现。
而在最上面,就是应用层了,系统的一些应用和第三方开发的所有应用都是位于这个层次上,也许要纠结两者的差别,就是系统应用会用一些隐藏的类,而第三方的应用,总是基于SDK提供的东西来搞。
一般来说,Android开发,就是在SDK的基础上,吭哧吭哧用Java写应用。但自从有了NDK,一切有了写小变化。NDK的出现意味着,最上面应用层的内容,可以穿越Java部署的框架层,直接和底层暴露出来的,或者自行开发的C++库直接对话,当然在这些库中需要包含JNI的接口。
人说,这就不是Android也可以用C++开发应用么,但其实,这样的说法不够确切,纯C++应用,是无法被接受的。因为在Android中,大量的核心机制部署在框架层,它们都是用Java实现的,比如控件库,Activity的调度之类的。因此,没了界面,没了调度,还是只用C++做类库比较合适,否则一切都乱了套了。

特征

基于这样的架构,Android有很多的设计显得很有意思。纵览整个SDK和核心机制的设计,工整漂亮,是Android给人的第一感觉。为了说明这一点,找一个反面教材是很有必要的,Symbian同学毫无悬念的担当这个伟岸的角色。
写Symbian程序,感觉就像是在玩一个猜谜游戏。哪怕你是一个Symbian老手,当需要用到Symbian中某块陌生功能的时候,你可能依然束手无策。你往往需要猜并反复找寻,在这里我需要使用哪一种奇巧淫技呢,是该臆想某些事件,还是应该用一个神秘的UID寻找某个特定应用,诸如此类。
而做Android应用的时候,就像是做高考模拟试题,题看上去不一样,解答模式摸清楚,就一通百通,一了百了。监听某个系统事件,查一下SDK就好;访问某个应用的数据,看看它有没有提供Content Provider就可以。所有的一切,都是按套路出牌,只要你了解了套路,再陌生的牌也可以看得懂,出的顺。人说武林高手,都应该是无招胜有招,而一个好的应用框架,也应该做到举重若轻,可触类旁通。
而Android框架最文采飞扬的一点,就是引入了Mash-Up的思想。所谓Mash-Up,就是把写应用搞成搭积木,要出效果的时候,东家一块西家一块现场拼起来就好。这里面关键有两点,一个是模块化,另一个就是动态性。所谓模块化,就是一个应用的功能要明确的被封成一个个边界清晰的功能点,每一个功能点都像是一个黑盒,由预先定义的规则描述出其交互方式;而动态性,就是这些独立的模块能够在运行的时候,按照需求描述,连接在一起,共同完成某项更大的功能。在这两点上,Android都做得非常出色。
站在可Mash-Up构造应用这一点去看其他的一些Android中的核心功能设计,就显得很有章可循了。比如为什么要把文件私有化,为什么要让进程被托管,等等(当然也可以站在别的角度看出不同的效果,视角不同,视野自然不同...)。
在UI机制方面,Android也有很不错的表现。它采取xml格式的资源文件,描述所有界面相关的内容。资源文件不是什么新东西了,xml格式也是老调重弹,但可贵的是Android做的更为的丰富和彻底,基本把界面相关的逻辑,全部从代码中剥离到了资源文件中,和Symbian那四不像的资源文件相比,真是强大了不知多少倍了。

Android学习入门

不积跬步,无以至千里。 -- 《劝学》
说,万事开头难。想开始Android的开发,最重要的应该是先把马步扎稳,套路摸清楚,后面的事情就顺当多了。打开怀抱,拥抱Android,也许可以先做下面这些事。

开发环境

辣手摧花成性的GFW,无情的把Android开发者官网关在了墙外。不过没关系,猛击这里,同样可以殊途同归(Shit,一直在用代理,刚试了下,发现竟然也被盾了,如果不行,那就只能翻墙了...)。
如果旅途顺利,你可以在路径sdk/index.html下找到安装说明,成功配置好Android的开发环境(【注】在以后,如果要给开发者页面上的链接,都会给一个像sdk/index.html这样的相对路径,你可以在前面加上官网地址,或者本地SDK的doc地址拼凑成完整的路径,在一个盾牌横行的朝代,只能用这样委屈求全的方法保证能更好的使用...)。
在2.0之前,每一次版本更新,你都要自己去下个全新的SDK,然后按照说明,小心翼翼的一步步修改eclipse的设置,甚是麻烦。在2.0后,这个模式有所改善,你会先下到一个类似于下载器的插件,通过它可以来管理和升级SDK,不仅简化了整个升级模式,还使得你可以更好的在各个不同的SDK版本间游走,利国利民。
Eclipse + ADT(Android Development Tool),是正牌的Android开发环境。你可以在Windows,Linux,Mac下做开发,甚为自由。比之Symbian的开发环境,ADT显得尤为强大,它对SDK提供的一堆优秀的命令行工具进行了UI上的封装,提供了图形界面(命令行控当然同样幸福,具体参见:guide/developing/tools/index.html)。通过ADT,你可以用运行和管理模拟器,使用调试器进行调试,过滤和查看Log,浏览模拟器上的文件信息,模拟拨号、短信等手机才有的事件,等等。

文档

我知道,有很多人在学习一个新平台开发的时候,都习惯去买一些《xxx 21天精通》之类的书籍。但其实,最好的入门学习资料,就是SDK文档。因为只有做平台的自己,才能最了解平台中的各个玄机,各方面的轻重缓急,从而能够更好的对症下药药到病除。
在Android的SDK中,guide/index.html是由浅入深的教学文档,reference/packages.html是标准的API文档。对于教学文档,我的意见是,一字不拉的通读一遍甚至多遍,至少做到能对Android摸着头脑,并且碰到问题的时候,能够快速想起在哪里可以找到,回来深刻阅读。
而API文档方面,Android做的算是还不错了。基本上每个类,每个接口,都有标准而详尽的说明,在一些尤为重要的类中,还具有大量的学习性的内容,不和Symbian似的,有太多的太监类,只有光秃秃的一个函数,一行文档说明都没有。整个文档结构是按照Java包来组织的,本身Java包命名的结构性和可读性很强,找起来也颇为方便。
很多人对SDK文档有抵触情绪,我想,有两方面的原因。一则是SDK文档普遍缺少文学性,麻木不仁的八股文,难以下咽。Android在这方面做得算是乏善可陈,虽然算不上文采华丽,但还是挺适合阅读的。另一则,就会是语言方面的原因了。SDK文档多为英语,偶尔像MSDN这样有中文的,也停留在机器翻译的水平上,阅读起来颇为难受。特地在网上搜了下,找到一些翻译SDK的中文文档,比如这里。虽然是基于1.5 r1版本SDK所著,稍显过时,但翻译的还是有小用心的,作为辅助,也不失为一份好资料,特代表广大看官向这些为人民福利着想的同志致敬。

Tutorials

光说不练假把式,除了读,在入门阶段,写也是一项不能少的运动。同样是在SDK中,Android提供了一组Tutorials和一些列的Samples,详见:resources/index.html
Tutorials很简单,Hello World只是在教你如何在eclipse中,在ADT的帮助下,创建一个Android项目。相比之下,Hello Views复杂了些,它集中展示了几种标准的Android Layout 样式是如何构建的,很多时候,你都是在这些样式下扩展所需的UI。
Hello Localization,是教你如何使用资源的,做完这个,就可以了解Android的资源有多杀~。最后收官的是一个更为完整的Notepad Tutorial,它展示了很多Android的核心机制,比如基于Intent的Activity整合,Activity的生命周期等。迈过这个Tutorial,欢迎你,进入Android的大门。
当然,做完Tutorials,对于Android而言,只是管中窥豹略见一斑。在SDK中,还提供了一系列的Samples。可以根据自己的需求,挑选合适的Sample编译运行和学习。但其中,有一个是不论你做什么,都需要必看必读必熟悉的,就是API Demos。在这个Sample中,集中展示了Android重点功能的API使用,把这个Sample用熟悉,需要做什么的时候过去找一下就可以很快的入手了。

源码

到这里,很多看官一定很不屑,前面所谓的学习入门介绍,只不过是围着SDK打转。其实,事实也是如此,SDK中包含的内容是真的非常重要,我只是期望通过一些简短的介绍,激起一些初学者的重视,如是而已。
当然,SDK每一个平台都有,没什么稀罕的。但Android有另一个非常稀罕而值钱的看家法宝,就是源代码。从Android Source的主站上:http://source.android.com/,你可以获得整个平台的源码以及相关介绍。非常苦口婆心的期望大家都去down一份源码放在机器上,哪怕你不需要进行修改编译,放在机器上当百科全书也是远胜于任何一本Android教学书籍的。本系列文章后续很多内容,都是从源码中学习到的一些浅薄见识。
对于大部分开发者都有学习价值的源码,主要在源码的frameworkspackages目录下。前者包含的是平台核心的一些实现,比如你需要自定义一个控件,也许你就可以翻到一个系统控件的实现中去,看看它是怎么来做的。后者包含一些系统的应用实现,比如你想做个播放器,也许你可以先去参考参考系统自带的是具体怎么做的。这样的实现,即便不算是最华美,至少也是最标准,其价值不容小视。
另外,你也可以把它当个代码库来使,不会使用某个类,grep一把,也许就能获得一份最漂亮的Sample。当然,如果你有时候对某些系统机制表示费解,抑或有一些bug不知道源头在哪,都可以跟着源码顺藤摸瓜的搞清楚。这样的好东西,可不是每个平台都能够享用的。

其他

论坛,其实对于开发和学习都是很重要的资源。毕竟,所有的资料都是死的,只有人是活得,能够最大限度的因地制宜解决问题。
只不过,标准的官方论坛,放在Google Group上,已经惆怅的被盾了。中文论坛方面,没有特别优秀和活跃的,这一方面是由于Android的发展现状还不算很磅礴,另一方面是由于Android的开发相对于Symbian而言,奇技淫巧少了很多,没有那么多好问的。也许你可以去去csdn这样的传统论坛,或者eoe这样专门的论坛。有的时候,还是多少能获得一些帮助的。
书籍方面,真没有什么推荐,豆瓣上搜索一下,你可以看到,目前的书籍,基本上还是集中在SDK使用层面上,很少有解析的很透彻,做的很深入的。而SDK的使用,看SDK的文档就足够了,如果实在对e文不感冒,买一两本评价不太差的中文书籍,放着翻翻也还是挺好。
更进一步,也许可以读读一些经验性的文档,去Google Code上搜索一些代码回来看看。比如,SDK文档中,有个经验性文档的集合:resources/articles/index.html,就可以翻看一下。
最后,更多的一切还需要自己在工程和思考中,慢慢总结。相信,好的代码,会垂青一个勤于动手和思考的人。
 

Android组件

横看成岭侧成峰,远近高低各不同。 -- 《题西林壁》
组件(Component),在谈及所谓架构和重用的时候,是一个重要的事情。很多时候都会说基于组件的软件架构,指的是期望把程序做乐高似的,有一堆接口标准封装完整的组件放在哪里,想用的时候取上几个一搭配,整个程序就构建完成了。
在开篇的时候就在说,Android是一个为组件化而搭建的平台,它引入所谓Mash-Up的概念,这使得你在应用的最上层,想做的不组件化都是很困难的一件事情(底层逻辑,好吧,管不了...)。具体说来,Android有四大组件四喜丸子:Activity、Service、Broadcast Receiver、Content Provider。

Activity

做一个完整的Android程序,不想用到Activity,真的是比较困难的一件事情,除非是想做绿叶想疯了。因为Activity是Android程序与用户交互的窗口,在我看来,从这个层面的视角来看,Android的Activity特像网站的页面
首先,一个网站,如果一张页面都没有,那...,真是一颗奇葩。而一张页面往往都有个独立的主题和功能点,比如登录页面,注册页面,管理页面,如是。
在每个页面里面,会放一些链接,已实现功能点的串联,有的链接点了,刷,跑到同一站点的另一个页面去了;有的链接点了,啾,可能跳到其他网站的页面去;还有的链接点了,恩...,这次没跑,但当前页面的样子可能有所变化了。这些模式,和Activity给人的感觉很像,只不过实现策略不同罢了,毕竟Android这套架构的核心思想,本身就来自源于Web的Mash-Up概念,视为页面的客户端化,也未尝不可。
Activity,在四大组件中,无疑是最复杂的,这年头,一样东西和界面挂上了勾,都简化不了,想一想,独立做一个应用有多少时间沦落在了界面上,就能琢磨清楚了。从视觉效果来看,一个Activity占据当前的窗口,响应所有窗口事件,具备有控件,菜单等界面元素。从内部逻辑来看,Activity需要为了保持各个界面状态,需要做很多持久化的事情,还需要妥善管理生命周期,和一些转跳逻辑。对于开发者而言,就需要派生一个Activity的子类,然后埋头苦干上述事情。对于Activity的更多细节,先可以参见:reference/android/app/Activity.html。后续,会献上更为详尽的剖析。

Service

服务,从最直白的视角来看,就是剥离了界面的Activity,它们在很多Android的概念方面比较接近,都是封装有一个完整的功能逻辑实现,只不过Service不抛头露脸,只是默默无声的做坚实的后盾。
但其实,换个角度来看,Android中的服务,和我们通常说的Windows服务,Web的后台服务又有一些相近,它们通常都是后台长时间运行,接受上层指令,完成相关事务的模块。用运行模式来看,Activity是跳,从一个跳到一个,呃...,这有点像模态对话框(或者还像web页面好了...),给一个输入(抑或没有...),然后不管不顾的让它运行,离开时返回输出(同抑或没有...)。
而Service不是,它是等,等着上层连接上它,然后产生一段持久而缠绵的通信,这就像一个用了Ajax页面,看着没啥变化,偷偷摸摸的和Service不知眉来眼去多少回了。
但和一般的Service还是有所不同,Android的Service和所有四大组件一样,其进程模型都是可以配置的,调用方和发布方都可以有权利来选择是把这个组件运行在同一个进程下,还是不同的进程下。这句话,可以拿把指甲刀刻进脑海中去,它凸显了Android的运行特征。如果一个Service,是有期望运行在于调用方不同进程的时候,就需要利用Android提供的RPC机制,为其部署一套进程间通信的策略。
Android的RPC实现,如上图所示(好吧,也是从SDK中拿来主义的...),无甚稀奇,基于代理模式的一个实现,在调用端和服务端都去生成一个代理类,做一些序列化和反序列化的事情,使得调用端和服务器端都可以像调用一个本地接口一样使用RPC接口。
Android中用来做数据序列化的类是Parcel,参见:/reference/android/os/Parcel.html,封装了序列化的细节,向外提供了足够对象化的访问接口,Android号称实现非常高效。
还有就是AIDL (Android Interface Definition Language) ,一种接口定义的语言,服务的RPC接口,可以用AIDL来描述,这样,ADT就可以帮助你自动生成一整套的代理模式需要用到的类,都是想起来很乏力写起来很苦力的那种。更多内容,可以再看看:guide/developing/tools/aidl.html,如果有兴致,可以找些其他PRC实现的资料lou几眼。
关于Service的实现,还强推参看API Demos这个Sample里面的RemoteService实现。它完整的展示了实现一个Service需要做的事情:那就是定义好需要接受的Intent,提供同步或异步的接口,在上层绑定了它后,通过这些接口(很多时候都是RPC的...)进行通信。在RPC接口中使用的数据、回调接口对象,如果不是标准的系统实现(系统可序列化的),则需要自定义aidl,所有一切,在这个Sample里都有表达,强荐。
Service从实现角度看,最特别的就是这些RPC的实现了,其他内容,都会接近于Activity的一些实现,也许不再会详述了。

Broadcast Receiver

在实际应用中,我们常需要等,等待系统抑或其他应用发出一道指令,为自己的应用擦亮明灯指明方向。而这种等待,在很多的平台上,都会需要付出不小的代价。
比如,在Symbian中,你要等待一个来电消息,显示归属地之类的,必须让自己的应用忍辱负重偷偷摸摸的开机启动,消隐图标隐藏任务项,潜伏在后台,监控着相关事件,等待转瞬即逝的出手机会。这是一件很发指的事情,不但白白耗费了系统资源,还留了个流氓软件的骂名,这真是卖力不讨好的正面典型。
在Android中,充分考虑了广泛的这类需求,于是就有了Broadcast Receiver这样的一个组件。每个Broadcast Receiver都可以接收一种或若干种Intent作为触发事件(有不知道Intent的么,后面会知道了...),当发生这样事件的时候,系统会负责唤醒或传递消息到该Broadcast Receiver,任其处置。在此之前和这以后,Broadcast Receiver是否在运行都变得不重要了,及其绿色环保。
这个实现机制,显然是基于一种注册方式的,Broadcast Receiver将其特征描述并注册在系统中,根据注册时机,可以分为两类,被我冠名为冷热插拔。所谓冷插拔,就是Broadcast Receiver的相关信息写在配置文件中(求配置文件详情?稍安,后续奉上...),系统会负责在相关事件发生的时候及时通知到该Broadcast Receiver,这种模式适合于这样的场景。某事件方式 -> 通知Broadcast -> 启动相关处理应用。比如,监听来电、邮件、短信之类的,都隶属于这种模式。而热插拔,顾名思义,插拔这样的事情,都是由应用自己来处理的,通常是在OnResume事件中通过registerReceiver进行注册,在OnPause等事件中反注册,通过这种方式使其能够在运行期间保持对相关事件的关注。比如,一款优秀的词典软件(比如,有道词典...),可能会有在运行期间关注网络状况变化的需求,使其可以在有廉价网络的时候优先使用网络查询词汇,在其他情况下,首先通过本地词库来查词,从而兼顾腰包和体验,一举两得一石二鸟一箭双雕(注,真实在有道词典中有这样的能力,但不是通过Broadcast Receiver实现的,仅以为例...)。而这样的监听,只需要在其工作状态下保持就好,不运行的时候,管你是天大的网路变化,与我何干。其模式可以归结为:启动应用 -> 监听事件 -> 发生时进行处理
除了接受消息的一方有多种模式,发送者也有很重要的选择权。通常,发送这有两类,一个就是系统本身,我们称之为系统Broadcast消息,在reference/android/content/Intent.htmlStandard Broadcast Actions,可以求到相关消息的详情。除了系统,自定义的应用可以放出Broadcast消息,通过的接口可以是Context.sendBroadcast,抑或是Context.sendOrderedBroadcast。前者发出的称为Normal broadcast,所有关注该消息的Receiver,都有机会获得并进行处理;后者放出的称作Ordered broadcasts,顾名思义,接受者需要按资排辈,排在后面的只能吃前面吃剩下的,前面的心情不好私吞了,后面的只能喝西北风了。
当Broadcast Receiver接收到相关的消息,它们通常做一些简单的处理,然后转化称为一条Notification,一次振铃,一次震动,抑或是启动一个Activity进行进一步的交互和处理。所以,虽然Broadcast整个逻辑不复杂,却是足够有用和好用,它统一了Android的事件广播模型,让很多平台都相形见绌了。更多Broadcast Receiver相关内容,参见:/reference/android/content/BroadcastReceiver.html

Content Provider

Content Provider,听着就和数据相关,没错,这就是Android提供的第三方应用数据的访问方案。在Android中,对数据的保护是很严密的,除了放在SD卡中的数据,一个应用所持有的数据库、文件、等等内容,都是不允许其他直接访问的,但有时候,沟通是必要的,不仅对第三方很重要,对应用自己也很重要。
比如,一个联系人管理的应用。如果不允许第三方的应用对其联系人数据库进行增删该查,整个应用就失去了可扩展力,必将被其他应用抛弃,然后另立门户,自个玩自个的去了。
Andorid当然不会真的把每个应用都做成一座孤岛,它为所有应用都准备了一扇窗,这就是Content Provider。应用想对外提供的数据,可以通过派生ContentProvider类,封装成一枚Content Provider,每个Content Provider都用一个uri作为独立的标识,形如:content://com.xxxxx。所有东西看着像REST的样子,但实际上,它比REST更为灵活。和REST类似,uri也可以有两种类型,一种是带id的,另一种是列表的,但实现者不需要按照这个模式来做,给你id的uri你也可以返回列表类型的数据,只要调用者明白,就无妨,不用苛求所谓的REST。
另外,Content Provider不和REST一样只有uri可用,还可以接受ProjectionSelectionOrderBy等参数,这样,就可以像数据库那样进行投影,选择和排序。查询到的结果,以Cursor(参见:reference/android/database/Cursor.html )的形式进行返回,调用者可以移动Cursor来访问各列的数据。
Content Provider屏蔽了内部数据的存储细节,向外提供了上述统一的接口模型,这样的抽象层次,大大简化了上层应用的书写,也对数据的整合提供了更方便的途径。Content Provider内部,常用数据库来实现,Android提供了强大的Sqlite支持,但很多时候,你也可以封装文件或其他混合的数据。
在Android中,ContentResolver是用来发起Content Provider的定位和访问的。不过它仅提供了同步访问的Content Provider的接口。但通常,Content Provider需要访问的可能是数据库等大数据源,效率上不足够快,会导致调用线程的拥塞。因此Android提供了一个AsyncQueryHandler(参见:reference/android/content/AsyncQueryHandler.html),帮助进行异步访问Content Provider。
在各大组件中,Service和Content Provider都是那种需要持续访问的。Service如果是一个耗时的场景,往往会提供异步访问的接口,而Content Provider不论效率如何,都提供的是约定的同步访问接口。我想这遵循的就是场景导向设计的原则,因为Content Provider仅是提供数据访问的,它不能确信具体的使用场景如何,会怎样使用它的数据;而相比之下,Service包含的逻辑更复杂更完整,可以抉择大部分时候使用某接口的场景,从而确定最贴切的接口是同步还是异步,简化了上层调用的逻辑。

配置

四大组件说完了,四大组件幕后的英雄也该出场了,那就是每个应用都会有一份的配置文件,名称是AndroidManifest.xml,在工程的根目录下。在这个配置文件中,不仅会描述一些应用相关的信息,很重要的,会包含一个应用中所有组件的信息。如果你派生Activity或者Service实现了一个相关的类,这只是把它组件化的第一步,你需要把这个类的相关信息写到配置文件中,它才会作为一个组件被应用到,否则只能默默无闻的黯淡度过余生。

 

摆了一幅图出来,这次不是偷来的,是敝帚自珍原创,所以没有意外的画的很丑,但基本还是可以体现出一些意思。在In Others的部分,这里是一般平台应用之间通信和交互的模型,每个应用都有很强烈的应用边界(往往表现为进程边界...),App 1的还是App 2的,分得很是清楚。每个应用内部,都有自己的逻辑去切分功能组件,这样的切分通常没有什么标准,率性而为。应用间的交互逻辑也比较零散,App 1与App 2交互,往往需要明确知道对方应用的具体信息,比如进程ID,进程名称之类的,这样使得应用和应用之间的联系,变得很生硬。而上层应用和系统应用的通信,往往有很多特定的模式,这种模式,很可能是无法直接应用在普通应用之间的,换而言之,系统应用是有一定特殊性的。
重点,在图的下半部,描述的是Android的应用情形。在Android中,应用的边界,在组件这个层面,是极度模糊,什么进程、什么应用,都可以不必感知到。举个例子,App 1,实现了A和B两个组件,App 2,实现了C这个组件。A和C,都想使用B这个组件,那么它们的使用方式是完全一致的,都需要通过系统核心的组件识别和通信机制,找到和使用组件B。A,虽说和B是一个娘胎里蹦出来的,很不好意思,没有任何特殊的后面和捷径,还是要跑规矩的途径才能用到,一片和谐社会的景象油然而生。
在Android中,所有组件的识别和消息传递逻辑都必须依赖底层核心来进行(通信可以没有底层核心的参与,比如一旦Service找到了,就可以和它产生持久的通信...),没有底层核心的牵线搭桥,任何两个组件都无法产生联系。比如一个Activity,跳到另一个Activity,必须要向底层核心发起一个Intent,有底层解析并认可后,会找到另一个Activity,把相关消息和数据传给它。一个Activity想使用Content Provider中的数据,必须通过底层核心解析相关的uri,定位到这个Content Provider,把参数传递给它,然后返回Activity需要的Cursor。这样的设计,保证了底层核心对所有组件的绝对掌控权和认知权,使得搭积木似的开发变成可能。
为了,使得核心系统能够完整的掌握每个组件的信息,这就需要配置文件了。配置文件,就是将组件插到底层核心上的这个插头。只有通过这个插头插在底层核心的插座上(不要乱想,非十八禁...),组件才能够发光发热,闪耀光芒。
组件的配置信息在我看来主要包含两个方面,一部分是描述如何认知。比如,Activity、Service、Broadcast Receiver都会有名字信息,和希望能够把握的Intent信息(姑且看成消息好了...),Content Provider会有一个描述其身份的uri。当其他组件通过这样的名字或者Intent,就可以找到它。
另一部分是运行相关的信息。这个组件,期望怎么来运行,放在单独的进程,还是和调用者一个进程,还是找相关的其他组件挤在同一个进程里面,这些内容,都可以在配置的时候来决定(调用者在这个约束范围内,有进一步的选择权...)。更多配置项,请参见:guide/topics/manifest/manifest-intro.html
通过前续内容,也许可以帮助大家对Android组件有个初略的了解。但这些了解都还停留在静态层面,程序是个动态的概念,关于各个组件具体是怎么联系在一起的,如何手拉手运行起来完成一项功能的,这便是后话了。
 
 

Intent解析

基于组件的架构体系,除了有定义良好的组件,如何把这些组件组装在一起,也是一门艺术。在Android中,Intent(貌似通常译作:意图...),就是连接各组件的桥梁。
前段时间看同事们做Symbian平台的网易掌上邮(真的是做的用心,NB的一米,热情欢迎所有163邮箱的S60v3用户,猛点击之...),有个功能是为邮件添加附件,比如你想要通过邮件发送一副图片泡mm,可能需要有个很直观的方式从本地选一副珍藏美图,抑或是拿相机来个完美自拍。在Symbian中,这样的功能,都需要你用底层的API,自己一点点写。为了让选图片体验更好,可能需要做一个类似于图片浏览器之类的东西,为了把拍照做的更为顺畅,甚至需要实现从聚焦到调节亮度之类一整套的相机功能。
而其实呢,用户的手机中可能本身就装了其他的专业图片浏览器、相机等应用,这些应用已经非常出色好用,而用户也已然能很纯属使用它们,如果能进行调用,对邮箱的开发者和用户而言,都会是个更好的选择。但在Symbian这样残败的系统里,应用和应用之间的结合能力奇弱无比,想复用,基本比登天还难,作为开发者,只能忍住一次又一次的恶心,为了用户,做这些重复造轮子吃力不讨好的附加工作。
还好还好,在Android中,一切变得美好多了,它将开发者从接口和对象的细节中解救出来,让我们有更多精力投入到核心功能的开发中去。在Android中,如果你需要选个图拍个片,只需要构造一个描述你此项意愿的Intent,发送出去,系统会帮你选择一个能够处理该项业务的组件来满足你的需求,而不再需要纠结在具体的接口和实现上,Perfect World,便应如此。

 

Intent构成

Intent被译作意图,其实还是很能传神的,Intent期望做到的,就是把实现者和调用者完全解耦,调用者专心将以意图描述清晰,发送出去,就可以梦想成真,达到目的。

当然,这么说太虚了,庖丁解牛,什么东西切开来看看,也许就清晰了。Intentreference/android/content/Intent.html),在Android中表现成一个类,发起一个意图,你需要构造这样一个对象,并为下列几项中的一些进行赋值:

 

  1. Action。当日常生活中,描述一个意愿或愿望的时候,总是有一个动词在其中。比如:我想三个俯卧撑;我要一部x片;我要一部血泪史,之类云云。在Intent中,Action就是描述看、做、写等动作的,当你指明了一个Action,执行者就会依照这个动作的指示,接受相关输入,表现对应行为,产生符合的输出。在Intent类中,定义了一批量的动作,比如ACTION_VIEWACTION_PICK,之类的,基本涵盖了常用动作,整一个降龙十八掌全集。当然,你也可以与时俱进,创造新的动作,比如lou这样的。与系统预定义的相比,这些自定义动作的流通范围很是有限,除非做了非常NB的应用,大家都需要follow你,否则通常都是应用内部流通。
  2. Data。当然,光有动作还是不够的,还需要有更确切的对象信息。比如,同样是这个动作,但泡咖啡,和泡妞,就差之千里了。Data的描述,在Android中,表现成为一个URI。用在内部通信中,可能描述是Content Provider用的形如content://xxxx这样的东东,抑或是外部的一个形如tel://xxxx这样的链接。总而言之,是能够清楚准确的描述一个数据地址的uri。
  3. Type。说了Data,就必须要提Type,很多时候,会有人误解,觉着Data和Type的差别,就犹如泡妞泡马子之间的差别一样,微乎其微。但其实不然,Type信息,是用MIME来表示的,比如text/plain,这样的东西。说到这里,两者差别就很清晰了,Data就是门牌号,指明了具体的位置,具体问题具体分析,而type,则是强调物以类聚,解决一批量的问题。实际的例子是这样的,比如,从某个应用拨打一个电话,会发起的是action为ACTION_DIAL且data为tel:xxx这样的Intent,对应的人类语言就是拨打xxx的电话,很具象。而如果使用type,就宽泛了许多,比如浏览器收到一个未知的MIME类型的数据(比如一个视频...),就会放出这样的Intent,求系统的其他应用来帮助,表达成自然语言应该就是:查看pdf类文档,这样的。
  4. Category。通过Action,配合Data或Type,很多时候可以准确的表达出一个完整的意图了,但也会有些时候,还需要加一些约束在里面才能够更精准。比如,如果你虽然很喜欢做俯卧撑,但一次做三个还只是在特殊的时候才会发生,那么你可能表达说:每次吃撑了的时候,我都想做三个俯卧撑。吃撑了,这就对应着Intent的Category的范畴,它给所发生的意图附加一个约束。在Android中,一个实例是,所有应用主Activity(就是单独启动时候,第一个运行的那个Activity...),都需要能够接受一个Category为CATEGORY_LAUNCHER,Action为ACTION_Main的意图。
  5. Component。在此之前,我们企图用Action,Data/Type,Category去描述一个意图,这是Android推荐,并期望大家在大多数时候使用的,这样模式在Android中称做Implicit Intents,通过这种模式,提供一种灵活可扩展的模式,给用户和第三方应用一个选择权。比如,还是一个邮箱软件,他大部分功能都好,就是选择图片的功能做的很土,怎么办?如果它采用的是Implicit Intents,那么它就是一个开放的体系了,手机中没有其他图片选择程序的话,可以继续使用邮箱默认的,如果有,你可以任意选择来替代原有模块完整这功能,一切都自然而然。但这种模式,也不是没有成本,需要付出的是一些性能上的开销,因为毕竟有一个检索过程。于是,Android提供了另一种模式,叫做Explicit Intents,就需要Component的帮助了。Component就是类名,完整的,形如com.xxxxx.xxxx,一旦指明了,一切都清晰了,找的到这个类(当然会是一个特定的子类...),成功,反之,失败。这个好处,自然是速度,适合在你明确知道这就是一个内部模块的时候,使用它。
  6. Extras。通过上面的这些项,识别问题,基本完美解决了,剩下一个重要的问题,就是传参。Extras是用来做这个事情的,它是一个Bundle类的对象,有一组可序列化的key/value对组成。每一个Action,都会有与之对应的key和value类型约定,发起Intent的时候,需要按照要求把Data不能表示的额外参数放入Extras中(当然,如果不需要额外附加参数,就算了...),否则执行者拿到的时候会抓狂的。
  7. Flags。能识别,有输入,整个Intent基本就完整了,但还有一些附件的指令,需要放在Flags中带过去。顾名思义,Flags是一个整形数,有一些列的标志位构成,这些标志,是用来指明运行模式的。比如,你期望这个意图的执行者,和你运行在两个完全不同的任务中(或说进程也无妨吧...),就需要设置FLAG_ACTIVITY_NEW_TASK的标志位。

 

有了上述这些,一个Intent的形象就跃然纸上了,如此丰富的内容,决定了它比传统的模式,都来得强大。

 

Intent匹配

上次在moto dev上,听人做Android的讲座,下面有很多听客都对Intent这个概念表示出了强烈的兴趣,拿出自己熟悉领域的各类概念进行类比,比如事件、消息之类。当时我在想,Intent作为组件间的通信协定,与一般的简单的通信方式不同,首先,从前面部分可以看到,它的描述是针对需求而不是实现者来进行的。其次,它的解析是依托第三方而不是两方直接进行。
这个概念和设计模式中的中介模式(Mediator Pattern)是一脉的,即所有的外围组件,都只和系统的核心模块发生联系,通过它进行中转,组件之间不直接勾搭。

 


如上图所示,要想跑通整个流程,另一个很重要的东西,就是Intent Filters,它是用来描述一个Activity或Serveice等组件,期望能够响应怎么样的Intent。如果一个组件,只希望别的组件通过Explicit Intents(就是指明Component...)的方式来找到它,那么就不需要添加Intent Filters,反之,一定需要一个或若干个Intent Filters。Intent Filter的各个项,犹如Intent照镜子过来的效果,包括Action,Catagory,Data,Type等。

Intent Filters可以写到配置文件中,和那些组件的配置一起(不记得什么是配置文件了,可以看这里...),若干的实例可以在Intent介绍页面上找到(reference/android/content/Intent.html)。同样,Intent Filters可以在代码中,动态插拔,这个是和动态插拔的Broadcast Receiver是配套使用的。
系统核心的模块,会负责收集这些Intent Filters,和它们对应的组件信息。当请求者需要一个组件帮忙,并构造了描述它需求的Intent发送到系统核心,系统核心会将其与已知的各个Intent Filters进行匹配,挑选一个符合需求的组件返回。如果有多个符合的,会尝试看看有没有默认执行的,如果没有默认的,就会构造UI,让用户帮助抉择,如是,整个流程就跑通了。

 

Intent实现




上图,是请求一个Activity组件的简单实现流程图,算是用的最多的Intent解析实例。流程从调用Context.startActivity(Intent)开始,调用者传入构造好的Intent对象,然后流程会让实际的执行者,是Instrumentation对象来完成。它是整个应用激活的Activity管理者,集中负责该应用内所有Activity的起承转合生离死别。它有一个隐藏的方法execStartActivity方法,就是负责根据Intent启动Activity的。去掉一些细节,它做得最重要的事情,就是将此调用,通过RPC的方式,传递到ActivityManagerService
前面一直再说,系统核心层,其实这里所谓的系统核心层,就是负责Android一些关键事务的一组服务。它们同样运行在虚拟机上,和普通的Service实现机理是一致的,只不过它们不抛头露脸只是默默的在下层服务,故谓之核心嘛。AcitivityManagerService,是负责Activity调度的服务,也许日后提及调度细节的时候还会有涉及。

在这里,AcitivityManagerService会分两个步骤完成相关操作,首先把Intent递交给另一个服务PackageManagerService,此服务掌握整个软件包及其各组件的信息,它会将传递过来的Intent,与已知的所有Intent Filters进行匹配(如果带有Component信息,就不用比了...),找到了,就把相关Component的信息通知回AcitivityManagerService,在这里,会完成启动Activity这个很多细节的事情。
由此可知,启动Activity,要经过多个服务的处理,并不是非常轻量的过程,在Android随机文档介绍性能的一节中,对此有一个评估。但这样的操作不是会放在循环里反复折磨的那种,因此整体效果与其付出的性能代价相比,觉得是物超所值的。
 
Intent解析

基于组件的架构体系,除了有定义良好的组件,如何把这些组件组装在一起,也是一门艺术。在Android中,Intent(貌似通常译作:意图...),就是连接各组件的桥梁。
前段时间看同事们做Symbian平台的网易掌上邮(真的是做的用心,NB的一米,热情欢迎所有163邮箱的S60v3用户,猛点击之...),有个功能是为邮件添加附件,比如你想要通过邮件发送一副图片泡mm,可能需要有个很直观的方式从本地选一副珍藏美图,抑或是拿相机来个完美自拍。在Symbian中,这样的功能,都需要你用底层的API,自己一点点写。为了让选图片体验更好,可能需要做一个类似于图片浏览器之类的东西,为了把拍照做的更为顺畅,甚至需要实现从聚焦到调节亮度之类一整套的相机功能。
而其实呢,用户的手机中可能本身就装了其他的专业图片浏览器、相机等应用,这些应用已经非常出色好用,而用户也已然能很纯属使用它们,如果能进行调用,对邮箱的开发者和用户而言,都会是个更好的选择。但在Symbian这样残败的系统里,应用和应用之间的结合能力奇弱无比,想复用,基本比登天还难,作为开发者,只能忍住一次又一次的恶心,为了用户,做这些重复造轮子吃力不讨好的附加工作。
还好还好,在Android中,一切变得美好多了,它将开发者从接口和对象的细节中解救出来,让我们有更多精力投入到核心功能的开发中去。在Android中,如果你需要选个图拍个片,只需要构造一个描述你此项意愿的Intent,发送出去,系统会帮你选择一个能够处理该项业务的组件来满足你的需求,而不再需要纠结在具体的接口和实现上,Perfect World,便应如此。

Intent构成

Intent被译作意图,其实还是很能传神的,Intent期望做到的,就是把实现者和调用者完全解耦,调用者专心将以意图描述清晰,发送出去,就可以梦想成真,达到目的。
当然,这么说太虚了,庖丁解牛,什么东西切开来看看,也许就清晰了。Intentreference/android/content/Intent.html),在Android中表现成一个类,发起一个意图,你需要构造这样一个对象,并为下列几项中的一些进行赋值:

  1. Action。当日常生活中,描述一个意愿或愿望的时候,总是有一个动词在其中。比如:我想三个俯卧撑;我要一部x片;我要一部血泪史,之类云云。在Intent中,Action就是描述看、做、写等动作的,当你指明了一个Action,执行者就会依照这个动作的指示,接受相关输入,表现对应行为,产生符合的输出。在Intent类中,定义了一批量的动作,比如ACTION_VIEWACTION_PICK,之类的,基本涵盖了常用动作,整一个降龙十八掌全集。当然,你也可以与时俱进,创造新的动作,比如lou这样的。与系统预定义的相比,这些自定义动作的流通范围很是有限,除非做了非常NB的应用,大家都需要follow你,否则通常都是应用内部流通。
  2. Data。当然,光有动作还是不够的,还需要有更确切的对象信息。比如,同样是这个动作,但泡咖啡,和泡妞,就差之千里了。Data的描述,在Android中,表现成为一个URI。用在内部通信中,可能描述是Content Provider用的形如content://xxxx这样的东东,抑或是外部的一个形如tel://xxxx这样的链接。总而言之,是能够清楚准确的描述一个数据地址的uri。
  3. Type。说了Data,就必须要提Type,很多时候,会有人误解,觉着Data和Type的差别,就犹如泡妞泡马子之间的差别一样,微乎其微。但其实不然,Type信息,是用MIME来表示的,比如text/plain,这样的东西。说到这里,两者差别就很清晰了,Data就是门牌号,指明了具体的位置,具体问题具体分析,而type,则是强调物以类聚,解决一批量的问题。实际的例子是这样的,比如,从某个应用拨打一个电话,会发起的是action为ACTION_DIAL且data为tel:xxx这样的Intent,对应的人类语言就是拨打xxx的电话,很具象。而如果使用type,就宽泛了许多,比如浏览器收到一个未知的MIME类型的数据(比如一个视频...),就会放出这样的Intent,求系统的其他应用来帮助,表达成自然语言应该就是:查看pdf类文档,这样的。
  4. Category。通过Action,配合Data或Type,很多时候可以准确的表达出一个完整的意图了,但也会有些时候,还需要加一些约束在里面才能够更精准。比如,如果你虽然很喜欢做俯卧撑,但一次做三个还只是在特殊的时候才会发生,那么你可能表达说:每次吃撑了的时候,我都想做三个俯卧撑。吃撑了,这就对应着Intent的Category的范畴,它给所发生的意图附加一个约束。在Android中,一个实例是,所有应用主Activity(就是单独启动时候,第一个运行的那个Activity...),都需要能够接受一个Category为CATEGORY_LAUNCHER,Action为ACTION_Main的意图。
  5. Component。在此之前,我们企图用Action,Data/Type,Category去描述一个意图,这是Android推荐,并期望大家在大多数时候使用的,这样模式在Android中称做Implicit Intents,通过这种模式,提供一种灵活可扩展的模式,给用户和第三方应用一个选择权。比如,还是一个邮箱软件,他大部分功能都好,就是选择图片的功能做的很土,怎么办?如果它采用的是Implicit Intents,那么它就是一个开放的体系了,手机中没有其他图片选择程序的话,可以继续使用邮箱默认的,如果有,你可以任意选择来替代原有模块完整这功能,一切都自然而然。但这种模式,也不是没有成本,需要付出的是一些性能上的开销,因为毕竟有一个检索过程。于是,Android提供了另一种模式,叫做Explicit Intents,就需要Component的帮助了。Component就是类名,完整的,形如com.xxxxx.xxxx,一旦指明了,一切都清晰了,找的到这个类(当然会是一个特定的子类...),成功,反之,失败。这个好处,自然是速度,适合在你明确知道这就是一个内部模块的时候,使用它。
  6. Extras。通过上面的这些项,识别问题,基本完美解决了,剩下一个重要的问题,就是传参。Extras是用来做这个事情的,它是一个Bundle类的对象,有一组可序列化的key/value对组成。每一个Action,都会有与之对应的key和value类型约定,发起Intent的时候,需要按照要求把Data不能表示的额外参数放入Extras中(当然,如果不需要额外附加参数,就算了...),否则执行者拿到的时候会抓狂的。
  7. Flags。能识别,有输入,整个Intent基本就完整了,但还有一些附件的指令,需要放在Flags中带过去。顾名思义,Flags是一个整形数,有一些列的标志位构成,这些标志,是用来指明运行模式的。比如,你期望这个意图的执行者,和你运行在两个完全不同的任务中(或说进程也无妨吧...),就需要设置FLAG_ACTIVITY_NEW_TASK的标志位。

有了上述这些,一个Intent的形象就跃然纸上了,如此丰富的内容,决定了它比传统的模式,都来得强大。

Intent匹配

上次在moto dev上,听人做Android的讲座,下面有很多听客都对Intent这个概念表示出了强烈的兴趣,拿出自己熟悉领域的各类概念进行类比,比如事件、消息之类。当时我在想,Intent作为组件间的通信协定,与一般的简单的通信方式不同,首先,从前面部分可以看到,它的描述是针对需求而不是实现者来进行的。其次,它的解析是依托第三方而不是两方直接进行。
这个概念和设计模式中的中介模式(Mediator Pattern)是一脉的,即所有的外围组件,都只和系统的核心模块发生联系,通过它进行中转,组件之间不直接勾搭。


如上图所示,要想跑通整个流程,另一个很重要的东西,就是Intent Filters,它是用来描述一个Activity或Serveice等组件,期望能够响应怎么样的Intent。如果一个组件,只希望别的组件通过Explicit Intents(就是指明Component...)的方式来找到它,那么就不需要添加Intent Filters,反之,一定需要一个或若干个Intent Filters。Intent Filter的各个项,犹如Intent照镜子过来的效果,包括Action,Catagory,Data,Type等。
Intent Filters可以写到配置文件中,和那些组件的配置一起(不记得什么是配置文件了,可以看这里...),若干的实例可以在Intent介绍页面上找到(reference/android/content/Intent.html)。同样,Intent Filters可以在代码中,动态插拔,这个是和动态插拔的Broadcast Receiver是配套使用的。
系统核心的模块,会负责收集这些Intent Filters,和它们对应的组件信息。当请求者需要一个组件帮忙,并构造了描述它需求的Intent发送到系统核心,系统核心会将其与已知的各个Intent Filters进行匹配,挑选一个符合需求的组件返回。如果有多个符合的,会尝试看看有没有默认执行的,如果没有默认的,就会构造UI,让用户帮助抉择,如是,整个流程就跑通了。

Intent实现




上图,是请求一个Activity组件的简单实现流程图,算是用的最多的Intent解析实例。流程从调用Context.startActivity(Intent)开始,调用者传入构造好的Intent对象,然后流程会让实际的执行者,是Instrumentation对象来完成。它是整个应用激活的Activity管理者,集中负责该应用内所有Activity的起承转合生离死别。它有一个隐藏的方法execStartActivity方法,就是负责根据Intent启动Activity的。去掉一些细节,它做得最重要的事情,就是将此调用,通过RPC的方式,传递到ActivityManagerService
前面一直再说,系统核心层,其实这里所谓的系统核心层,就是负责Android一些关键事务的一组服务。它们同样运行在虚拟机上,和普通的Service实现机理是一致的,只不过它们不抛头露脸只是默默的在下层服务,故谓之核心嘛。AcitivityManagerService,是负责Activity调度的服务,也许日后提及调度细节的时候还会有涉及。
在这里,AcitivityManagerService会分两个步骤完成相关操作,首先把Intent递交给另一个服务PackageManagerService,此服务掌握整个软件包及其各组件的信息,它会将传递过来的Intent,与已知的所有Intent Filters进行匹配(如果带有Component信息,就不用比了...),找到了,就把相关Component的信息通知回AcitivityManagerService,在这里,会完成启动Activity这个很多细节的事情。
由此可知,启动Activity,要经过多个服务的处理,并不是非常轻量的过程,在Android随机文档介绍性能的一节中,对此有一个评估。但这样的操作不是会放在循环里反复折磨的那种,因此整体效果与其付出的性能代价相比,觉得是物超所值的。
原创粉丝点击