﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>博客园-Venus神庙</title><link>http://www.cnblogs.com/duguguiyu/</link><description>The flying fly is flying in the high and big sky...</description><language>zh-cn</language><lastBuildDate>Wed, 10 Feb 2010 05:48:01 GMT</lastBuildDate><pubDate>Wed, 10 Feb 2010 05:48:01 GMT</pubDate><ttl>60</ttl><item><title>深入Android 【四】 —— 组件调用</title><link>http://www.cnblogs.com/duguguiyu/archive/2010/02/07/1665544.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Sun, 07 Feb 2010 14:09:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2010/02/07/1665544.html</guid><description><![CDATA[<p>阅读: 844 评论: 4 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2010-02-07 22:09 <a href="http://www.cnblogs.com/duguguiyu/archive/2010/02/07/1665544.html" target="_blank">原文链接</a></p><span class="Apple-style-span" style="font-family: Verdana; line-height: normal; font-size: 13px; "><br /><span class="Apple-style-span" style="font-family: Georgia; font-size: xx-large; font-weight: bold; ">Intent解析</span><br /><br /><div style="margin-top: 0px; margin-bottom: 0px; ">基于组件的架构体系，除了有定义良好的组件，如何把这些组件组装在一起，也是一门艺术。在Android中，<strong>Intent</strong>（貌似通常译作：<strong>意图</strong>...），就是连接各组件的桥梁。</div><div style="margin-top: 0px; margin-bottom: 0px; ">前段时间看同事们做Symbian平台的<a href="http://zsy.163.com/help" id="ds0." title="网易掌上邮" target="_blank"><strong>网易掌上邮</strong></a>（真的是做的用心，NB的一米，热情欢迎所有163邮箱的S60v3用户，猛点击之...），有个功能是为邮件添加附件，比如你想要通过邮件发送一副图片泡mm，可能需要有个很直观的方式从本地选一副珍藏美图，抑或是拿相机来个完美自拍。在Symbian中，这样的功能，都需要你用底层的API，自己一点点写。为了让选图片体验更好，可能需要做一个类似于图片浏览器之类的东西，为了把拍照做的更为顺畅，甚至需要实现从聚焦到调节亮度之类一整套的相机功能。</div><div style="margin-top: 0px; margin-bottom: 0px; ">而其实呢，用户的手机中可能本身就装了其他的专业图片浏览器、相机等应用，这些应用已经非常出色好用，而用户也已然能很纯属使用它们，如果能进行调用，对邮箱的开发者和用户而言，都会是个更好的选择。但在Symbian这样残败的系统里，应用和应用之间的结合能力奇弱无比，想复用，基本比登天还难，作为开发者，只能忍住一次又一次的恶心，为了用户，做这些重复造轮子吃力不讨好的附加工作。</div><div style="margin-top: 0px; margin-bottom: 0px; ">还好还好，在Android中，一切变得美好多了，它将开发者从接口和对象的细节中解救出来，让我们有更多精力投入到核心功能的开发中去。在Android中，如果你需要选个图拍个片，只需要构造一个描述你此项意愿的Intent，发送出去，系统会帮你选择一个能够处理该项业务的组件来满足你的需求，而不再需要纠结在具体的接口和实现上，Perfect World，便应如此。</div><br /><h2 style="font-size: 14pt; ">Intent构成</h2>Intent被译作意图，其实还是很能传神的，Intent期望做到的，就是把实现者和调用者完全解耦，调用者专心将以意图描述清晰，发送出去，就可以梦想成真，达到目的。<div style="margin-top: 0px; margin-bottom: 0px; ">当然，这么说太虚了，庖丁解牛，什么东西切开来看看，也许就清晰了。<strong><a href="http://androidappdocs.appspot.com/reference/android/content/Intent.html" id="z.s2" title="Intent" style="color: #551a8b; " target="_blank">Intent</a></strong>（<strong>reference/android/content/Intent.html</strong>），在Android中表现成一个类，发起一个意图，你需要构造这样一个对象，并为下列几项中的一些进行赋值：</div><br /><ol style="margin-top: 0px; margin-bottom: 0px; "><li style="margin-top: 0px; margin-bottom: 0px; "><strong>Action</strong>。当日常生活中，描述一个意愿或愿望的时候，总是有一个动词在其中。比如：我想<strong>做</strong>三个俯卧撑；我要<strong>看</strong>一部x片；我要<strong>写</strong>一部血泪史，之类云云。在Intent中，Action就是描述看、做、写等动作的，当你指明了一个Action，执行者就会依照这个动作的指示，接受相关输入，表现对应行为，产生符合的输出。在Intent类中，定义了一批量的动作，比如<strong>ACTION_VIEW</strong>，<strong>ACTION_PICK</strong>，之类的，基本涵盖了常用动作，整一个降龙十八掌全集。当然，你也可以与时俱进，创造新的动作，比如lou这样的。与系统预定义的相比，这些自定义动作的流通范围很是有限，除非做了非常NB的应用，大家都需要follow你，否则通常都是应用内部流通。</li><li style="margin-top: 0px; margin-bottom: 0px; "><strong>Data</strong>。当然，光有动作还是不够的，还需要有更确切的对象信息。比如，同样是<strong>泡</strong>这个动作，但<strong>泡咖啡</strong>，和<strong>泡妞</strong>，就差之千里了。Data的描述，在Android中，表现成为一个<strong>URI</strong>。用在内部通信中，可能描述是Content Provider用的形如<strong>content://xxxx</strong>这样的东东，抑或是外部的一个形如<strong>tel://xxxx</strong>这样的链接。总而言之，是能够清楚准确的描述一个数据地址的uri。</li><li style="margin-top: 0px; margin-bottom: 0px; "><strong>Type</strong>。说了Data，就必须要提Type，很多时候，会有人误解，觉着Data和Type的差别，就犹如<strong>泡妞</strong>和<strong>泡马子</strong>之间的差别一样，微乎其微。但其实不然，Type信息，是用<strong>MIME</strong>来表示的，比如<strong>text/plain</strong>，这样的东西。说到这里，两者差别就很清晰了，Data就是门牌号，指明了具体的位置，具体问题具体分析，而type，则是强调物以类聚，解决一批量的问题。实际的例子是这样的，比如，从某个应用拨打一个电话，会发起的是action为ACTION_DIAL且data为tel:xxx这样的Intent，对应的人类语言就是<strong>拨打xxx的电话</strong>，很具象。而如果使用type，就宽泛了许多，比如浏览器收到一个未知的MIME类型的数据（比如一个视频...），就会放出这样的Intent，求系统的其他应用来帮助，表达成自然语言应该就是：<strong>查看pdf类文档</strong>，这样的。</li><li style="margin-top: 0px; margin-bottom: 0px; "><strong>Category</strong>。通过Action，配合Data或Type，很多时候可以准确的表达出一个完整的意图了，但也会有些时候，还需要加一些约束在里面才能够更精准。比如，如果你虽然很喜欢做俯卧撑，但一次做三个还只是在特殊的时候才会发生，那么你可能表达说：<strong>每次吃撑了的时候</strong>，我都想做三个俯卧撑。吃撑了，这就对应着Intent的Category的范畴，它给所发生的意图附加一个约束。在Android中，一个实例是，所有应用主Activity（就是单独启动时候，第一个运行的那个Activity...），都需要能够接受一个Category为CATEGORY_LAUNCHER，Action为ACTION_Main的意图。</li><li style="margin-top: 0px; margin-bottom: 0px; "><strong>Component</strong>。在此之前，我们企图用Action，Data/Type，Category去描述一个意图，这是Android推荐，并期望大家在大多数时候使用的，这样模式在Android中称做<strong>Implicit Intents</strong>，通过这种模式，提供一种灵活可扩展的模式，给用户和第三方应用一个选择权。比如，还是一个邮箱软件，他大部分功能都好，就是选择图片的功能做的很土，怎么办？如果它采用的是Implicit Intents，那么它就是一个开放的体系了，手机中没有其他图片选择程序的话，可以继续使用邮箱默认的，如果有，你可以任意选择来替代原有模块完整这功能，一切都自然而然。但这种模式，也不是没有成本，需要付出的是一些性能上的开销，因为毕竟有一个检索过程。于是，Android提供了另一种模式，叫做<strong>Explicit Intents</strong>，就需要Component的帮助了。Component就是<strong>类名</strong>，完整的，形如<strong>com.xxxxx.xxxx</strong>，一旦指明了，一切都清晰了，找的到这个类（当然会是一个特定的子类...），成功，反之，失败。这个好处，自然是速度，适合在你明确知道这就是一个内部模块的时候，使用它。</li><li style="margin-top: 0px; margin-bottom: 0px; "><strong>Extras</strong>。通过上面的这些项，识别问题，基本完美解决了，剩下一个重要的问题，就是传参。Extras是用来做这个事情的，它是一个<a href="http://androidappdocs.appspot.com/reference/android/os/Bundle.html" id="ldre" title="Bundle" target="_blank"><strong>Bundle</strong></a>类的对象，有一组可序列化的<strong>key/value对</strong>组成。每一个Action，都会有与之对应的key和value类型约定，发起Intent的时候，需要按照要求把Data不能表示的额外参数放入Extras中（当然，如果不需要额外附加参数，就算了...），否则执行者拿到的时候会抓狂的。</li><li style="margin-top: 0px; margin-bottom: 0px; "><strong>Flags</strong>。能识别，有输入，整个Intent基本就完整了，但还有一些附件的指令，需要放在Flags中带过去。顾名思义，Flags是一个整形数，有一些列的标志位构成，这些标志，是用来指明运行模式的。比如，你期望这个意图的执行者，和你运行在两个完全不同的任务中（或说进程也无妨吧...），就需要设置<strong>FLAG_ACTIVITY_NEW_TASK</strong>的标志位。</li></ol><br /><div style="margin-top: 0px; margin-bottom: 0px; ">有了上述这些，一个Intent的形象就跃然纸上了，如此丰富的内容，决定了它比传统的模式，都来得强大。</div><br /><h2 style="font-size: 14pt; ">Intent匹配</h2><div style="margin-top: 0px; margin-bottom: 0px; ">上次在moto dev上，听人做Android的讲座，下面有很多听客都对Intent这个概念表示出了强烈的兴趣，拿出自己熟悉领域的各类概念进行类比，比如事件、消息之类。当时我在想，Intent作为组件间的通信协定，与一般的简单的通信方式不同，首先，从前面部分可以看到，它的描述是针对需求而不是实现者来进行的。其次，它的解析是依托第三方而不是两方直接进行。</div><div style="margin-top: 0px; margin-bottom: 0px; ">这个概念和设计模式中的中介模式（<a href="http://en.wikipedia.org/wiki/Mediator_pattern" id="il70" title="Mediator Pattern" style="color: #551a8b; " target="_blank">Mediator Pattern</a>）是一脉的，即所有的外围组件，都只和系统的核心模块发生联系，通过它进行中转，组件之间不直接勾搭。</div><br /><div style="margin-top: 0px; margin-bottom: 0px; "><img src="http://images.cnblogs.com/cnblogs_com/duguguiyu/2.png" width="400" height="211" alt="" /><br /></div><br />如上图所示，要想跑通整个流程，另一个很重要的东西，就是<a href="http://androidappdocs.appspot.com/reference/android/content/IntentFilter.html" id="e6lb" title="Intent Filters" style="color: #551a8b; " target="_blank"><strong>Intent Filters</strong></a>，它是用来描述一个Activity或Serveice等组件，期望能够响应怎么样的Intent。如果一个组件，只希望别的组件通过Explicit Intents（就是指明Component...）的方式来找到它，那么就不需要添加Intent Filters，反之，一定需要一个或若干个Intent Filters。Intent Filter的各个项，犹如Intent照镜子过来的效果，包括Action，Catagory，Data，Type等。<div style="margin-top: 0px; margin-bottom: 0px; ">Intent Filters可以写到配置文件中，和那些组件的配置一起（不记得什么是配置文件了，可以看<a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/30/1659980.html" id="vrpk" title="这里" style="color: #551a8b; " target="_blank">这里</a>...），若干的实例可以在Intent介绍页面上找到（<a href="http://androidappdocs.appspot.com/reference/android/content/Intent.html" style="color: #551a8b; " target="_blank"><strong>reference/android/content/Intent.html</strong></a>）。同样，Intent Filters可以在代码中，动态插拔，这个是和动态插拔的<strong>Broadcast Receiver</strong>是配套使用的。</div><div style="margin-top: 0px; margin-bottom: 0px; ">系统核心的模块，会负责收集这些Intent Filters，和它们对应的组件信息。当请求者需要一个组件帮忙，并构造了描述它需求的Intent发送到系统核心，系统核心会将其与已知的各个Intent Filters进行匹配，挑选一个符合需求的组件返回。如果有多个符合的，会尝试看看有没有默认执行的，如果没有默认的，就会构造UI，让用户帮助抉择，如是，整个流程就跑通了。</div><br /><h2 style="font-size: 14pt; ">Intent实现</h2><img src="http://images.cnblogs.com/cnblogs_com/duguguiyu/3.png" width="600" height="240" alt="" /><br /><br /><br />上图，是请求一个Activity组件的简单实现流程图，算是用的最多的Intent解析实例。流程从调用<a href="http://androidappdocs.appspot.com/reference/android/content/Context.html#startActivity(android.content.Intent)" id="s0hr" title="Context.startActivity(Intent)" style="color: #551a8b; " target="_blank"><strong>Context.startActivity(Intent)</strong></a>开始，调用者传入构造好的Intent对象，然后流程会让实际的执行者，是<a href="http://androidappdocs.appspot.com/reference/android/app/Instrumentation.html" id="capl" title="Instrumentation" style="color: #551a8b; " target="_blank"><strong>Instrumentation</strong></a>对象来完成。它是整个应用激活的Activity管理者，集中负责该应用内所有Activity的起承转合生离死别。它有一个隐藏的方法<strong>execStartActivit</strong>y方法，就是负责根据Intent启动Activity的。去掉一些细节，它做得最重要的事情，就是将此调用，通过RPC的方式，传递到<strong>ActivityManagerService</strong>。<br />前面一直再说，<strong>系统核心层</strong>，其实这里所谓的系统核心层，就是负责Android一些关键事务的<strong>一组服务</strong>。它们同样运行在虚拟机上，和普通的Service实现机理是一致的，只不过它们不抛头露脸只是默默的在下层服务，故谓之核心嘛。AcitivityManagerService，是负责Activity调度的服务，也许日后提及调度细节的时候还会有涉及。<div style="margin-top: 0px; margin-bottom: 0px; ">在这里，AcitivityManagerService会分两个步骤完成相关操作，首先把Intent递交给另一个服务<strong>PackageManagerService</strong>，此服务掌握整个软件包及其各组件的信息，它会将传递过来的Intent，与已知的所有Intent Filters进行匹配（如果带有Component信息，就不用比了...），找到了，就把相关Component的信息通知回AcitivityManagerService，在这里，会完成启动Activity这个很多细节的事情。</div><div style="margin-top: 0px; margin-bottom: 0px; ">由此可知，启动Activity，要经过多个服务的处理，并不是非常轻量的过程，在Android随机文档<a href="http://androidappdocs.appspot.com/guide/practices/design/performance.html#samples" id="sytu" title="介绍性能" style="color: #551a8b; " target="_blank">介绍性能</a>的一节中，对此有一个评估。但这样的操作不是会放在循环里反复折磨的那种，因此整体效果与其付出的性能代价相比，觉得是物超所值的。</div></span><img src="http://www.cnblogs.com/duguguiyu/aggbug/1665544.html?type=1" width="1" height="1" alt=""/><p>评论: 4　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/02/07/1665544.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/02/07/1665544.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>深入Android 【三】 —— 组件入门</title><link>http://www.cnblogs.com/duguguiyu/archive/2010/01/30/1659980.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Sat, 30 Jan 2010 05:09:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2010/01/30/1659980.html</guid><description><![CDATA[<p>阅读: 1403 评论: 3 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2010-01-30 13:09 <a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/30/1659980.html" target="_blank">原文链接</a></p><div><span class="Apple-style-span" style="font-family: Verdana; line-height: normal; font-size: 13px; "><div style="margin-top: 0px; margin-bottom: 0px; "><h1 style="font-size: 18pt; "><font class="Apple-style-span" face="Georgia"><font size="6">Android组件</font></font></h1></div><blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; "><font class="Apple-style-span" face="Georgia">横看成岭侧成峰，远近高低各不同。 -- 《题西林壁》</font></blockquote><div style="margin-top: 0px; margin-bottom: 0px; ">组件（<strong>Component</strong>），在谈及所谓架构和重用的时候，是一个重要的事情。很多时候都会说基于组件的软件架构，指的是期望把程序做乐高似的，有一堆接口标准封装完整的组件放在哪里，想用的时候取上几个一搭配，整个程序就构建完成了。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在<a id="p6q9" href="http://www.cnblogs.com/duguguiyu/archive/2010/01/21/1652868.html" title="开篇" style="color: #551a8b; " target="_blank">开篇</a>的时候就在说，Android是一个为组件化而搭建的平台，它引入所谓Mash-Up的概念，这使得你在应用的最上层，想做的不组件化都是很困难的一件事情（底层逻辑，好吧，管不了...）。具体说来，Android有四大组件四喜丸子：Activity、Service、Broadcast Receiver、Content Provider。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">Activity</h2><div style="margin-top: 0px; margin-bottom: 0px; ">做一个完整的Android程序，不想用到Activity，真的是比较困难的一件事情，除非是想做绿叶想疯了。因为Activity是Android程序与用户交互的窗口，在我看来，从这个层面的视角来看，Android的Activity特像网站的<strong>页面</strong>。</div><div style="margin-top: 0px; margin-bottom: 0px; ">首先，一个网站，如果一张页面都没有，那...，真是一颗奇葩。而一张页面往往都有个<strong>独立的主题和功能点</strong>，比如登录页面，注册页面，管理页面，如是。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在每个页面里面，会放一些链接，已实现功能点的串联，有的链接点了，刷，跑到同一站点的另一个页面去了；有的链接点了，啾，可能跳到其他网站的页面去；还有的链接点了，恩...，这次没跑，但当前页面的样子可能有所变化了。这些模式，和Activity给人的感觉很像，只不过实现策略不同罢了，毕竟Android这套架构的核心思想，本身就来自源于Web的Mash-Up概念，视为页面的客户端化，也未尝不可。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Activity，在四大组件中，无疑是最复杂的，这年头，一样东西和界面挂上了勾，都简化不了，想一想，独立做一个应用有多少时间沦落在了界面上，就能琢磨清楚了。从视觉效果来看，一个Activity占据当前的窗口，响应所有窗口事件，具备有控件，菜单等界面元素。从内部逻辑来看，Activity需要为了保持各个界面状态，需要做很多持久化的事情，还需要妥善管理生命周期，和一些转跳逻辑。对于开发者而言，就需要派生一个Activity的子类，然后埋头苦干上述事情。对于Activity的更多细节，先可以参见：<strong><a id="aq.:" href="http://androidappdocs.appspot.com/reference/android/app/Activity.html" title="reference/android/app/Activity.html" style="color: #551a8b; " target="_blank">reference/android/app/Activity.html</a></strong>。后续，会献上更为详尽的剖析。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">Service</h2><div style="margin-top: 0px; margin-bottom: 0px; ">服务，从最直白的视角来看，就是剥离了界面的Activity，它们在很多Android的概念方面比较接近，都是封装有一个<strong>完整的功能逻辑实现</strong>，只不过Service不抛头露脸，只是默默无声的做坚实的后盾。</div><div style="margin-top: 0px; margin-bottom: 0px; ">但其实，换个角度来看，Android中的服务，和我们通常说的Windows服务，Web的后台服务又有一些相近，它们通常都是<strong>后台长时间运行，接受上层指令，完成相关事务的模块</strong>。用运行模式来看，Activity是跳，从一个跳到一个，呃...，这有点像模态对话框（或者还像web页面好了...），给一个输入（抑或没有...），然后不管不顾的让它运行，离开时返回输出（同抑或没有...）。</div><div style="margin-top: 0px; margin-bottom: 0px; ">而Service不是，它是等，等着上层连接上它，然后产生一段持久而缠绵的通信，这就像一个用了Ajax页面，看着没啥变化，偷偷摸摸的和Service不知眉来眼去多少回了。</div><div style="margin-top: 0px; margin-bottom: 0px; ">但和一般的Service还是有所不同，Android的Service和所有四大组件一样，其<strong>进程模型都是可以配置的</strong>，调用方和发布方都可以有权利来选择是把这个组件运行在同一个进程下，还是不同的进程下。这句话，可以拿把指甲刀刻进脑海中去，它凸显了Android的运行特征。如果一个Service，是有期望运行在于调用方不同进程的时候，就需要利用Android提供的<strong>RPC</strong>机制，为其部署一套进程间通信的策略。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><div style="margin-top: 0px; margin-bottom: 0px; "><div id="x4oh" style="margin-top: 0px; margin-bottom: 0px; text-align: left; "><img src="http://docs.google.com/File?id=ddwgxw9r_980hc5x4cgf_b" style="width: 472px; height: 365px; " alt="" /></div><div id="n5jz" style="margin-top: 0px; margin-bottom: 0px; text-align: left; "><br /></div>Android的RPC实现，如上图所示（好吧，也是从SDK中拿来主义的...），无甚稀奇，基于代理模式的一个实现，在调用端和服务端都去生成一个代理类，做一些序列化和反序列化的事情，使得调用端和服务器端都可以像调用一个本地接口一样使用RPC接口。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Android中用来做数据序列化的类是<strong>Parcel</strong>，参见：<strong><a id="faxe" href="http://androidappdocs.appspot.com/reference/android/os/Parcel.html" title="/reference/android/os/Parcel.html" style="color: #551a8b; " target="_blank">/reference/android/os/Parcel.html</a></strong>，封装了序列化的细节，向外提供了足够对象化的访问接口，Android号称实现非常高效。</div><div style="margin-top: 0px; margin-bottom: 0px; ">还有就是<strong>AIDL (Android Interface Definition Language)</strong>&nbsp;，一种接口定义的语言，服务的RPC接口，可以用AIDL来描述，这样，ADT就可以帮助你自动生成一整套的代理模式需要用到的类，都是想起来很乏力写起来很苦力的那种。更多内容，可以再看看：<strong><a id="a9m0" href="http://androidappdocs.appspot.com/guide/developing/tools/aidl.html" title="guide/developing/tools/aidl.html" style="color: #551a8b; " target="_blank">guide/developing/tools/aidl.html</a></strong>，如果有兴致，可以找些其他PRC实现的资料lou几眼。</div><div style="margin-top: 0px; margin-bottom: 0px; ">关于Service的实现，还强推参看<strong>API Demos</strong>这个Sample里面的<strong><a id="b1.y" href="http://androidappdocs.appspot.com/resources/samples/ApiDemos/src/com/example/android/apis/app/RemoteService.html" title="RemoteService" style="color: #551a8b; " target="_blank">RemoteService</a></strong>实现。它完整的展示了实现一个Service需要做的事情：那就是定义好需要接受的Intent，提供<strong>同步或异步的接口</strong>，在上层绑定了它后，通过这些接口（很多时候都是RPC的...）进行通信。在RPC接口中使用的数据、回调接口对象，如果不是标准的系统实现（系统可序列化的），则需要自定义aidl，所有一切，在这个Sample里都有表达，强荐。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Service从实现角度看，最特别的就是这些RPC的实现了，其他内容，都会接近于Activity的一些实现，也许不再会详述了。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">Broadcast Receiver</h2><div style="margin-top: 0px; margin-bottom: 0px; ">在实际应用中，我们常需要等，等待系统抑或其他应用发出一道指令，为自己的应用擦亮明灯指明方向。而这种等待，在很多的平台上，都会需要付出不小的代价。</div><div style="margin-top: 0px; margin-bottom: 0px; ">比如，在Symbian中，你要等待一个来电消息，显示归属地之类的，必须让自己的应用忍辱负重偷偷摸摸的开机启动，消隐图标隐藏任务项，潜伏在后台，监控着相关事件，等待转瞬即逝的出手机会。这是一件很发指的事情，不但白白耗费了系统资源，还留了个流氓软件的骂名，这真是卖力不讨好的正面典型。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在Android中，充分考虑了广泛的这类需求，于是就有了<strong>Broadcast Receiver</strong>这样的一个组件。每个Broadcast Receiver都可以接收一种或若干种Intent作为触发事件（有不知道Intent的么，后面会知道了...），当发生这样事件的时候，系统会负责唤醒或传递消息到该Broadcast Receiver，任其处置。在此之前和这以后，Broadcast Receiver是否在运行都变得不重要了，及其绿色环保。</div><div style="margin-top: 0px; margin-bottom: 0px; ">这个实现机制，显然是基于一种<strong>注册</strong>方式的，Broadcast Receiver将其特征描述并注册在系统中，根据注册时机，可以分为两类，被我冠名为<strong>冷热插拔</strong>。所谓<strong>冷插拔</strong>，就是Broadcast Receiver的相关信息写在配置文件中（求配置文件详情？稍安，后续奉上...），系统会负责在相关事件发生的时候及时通知到该Broadcast Receiver，这种模式适合于这样的场景。<strong>某事件方式 -&gt; 通知Broadcast -&gt; 启动相关处理应用</strong>。比如，监听来电、邮件、短信之类的，都隶属于这种模式。而<strong>热插拔</strong>，顾名思义，插拔这样的事情，都是由应用自己来处理的，通常是在OnResume事件中通过<strong>registerReceiver</strong>进行注册，在OnPause等事件中反注册，通过这种方式使其能够在运行期间保持对相关事件的关注。比如，一款优秀的词典软件（比如，<a id="xlrf" href="http://m.youdao.com/help/cidian&amp;keyfrom=duguguiyu" title="有道词典" target="_blank">有道词典</a>...），可能会有在运行期间关注网络状况变化的需求，使其可以在有廉价网络的时候优先使用网络查询词汇，在其他情况下，首先通过本地词库来查词，从而兼顾腰包和体验，一举两得一石二鸟一箭双雕（注，真实在有道词典中有这样的能力，但不是通过Broadcast Receiver实现的，仅以为例...）。而这样的监听，只需要在其工作状态下保持就好，不运行的时候，管你是天大的网路变化，与我何干。其模式可以归结为：<strong>启动应用 -&gt; 监听事件 -&gt; 发生时进行处理</strong>。</div><div style="margin-top: 0px; margin-bottom: 0px; ">除了接受消息的一方有多种模式，发送者也有很重要的选择权。通常，发送这有两类，一个就是系统本身，我们称之为系统Broadcast消息，在<strong><a id="gxxw" href="http://androidappdocs.appspot.com/reference/android/content/Intent.html" title="reference/android/content/Intent.html" target="_blank">reference/android/content/Intent.html</a></strong>的<strong>Standard Broadcast Actions</strong>，可以求到相关消息的详情。除了系统，自定义的应用可以放出Broadcast消息，通过的接口可以是<strong>Context.sendBroadcast</strong>，抑或是<strong>Context.sendOrderedBroadcast</strong>。前者发出的称为<strong>Normal broadcast</strong>，所有关注该消息的Receiver，都有机会获得并进行处理；后者放出的称作<strong>Ordered broadcasts</strong>，顾名思义，接受者需要按资排辈，排在后面的只能吃前面吃剩下的，前面的心情不好私吞了，后面的只能喝西北风了。</div><div style="margin-top: 0px; margin-bottom: 0px; ">当Broadcast Receiver接收到相关的消息，它们通常做一些简单的处理，然后转化称为一条Notification，一次振铃，一次震动，抑或是启动一个Activity进行进一步的交互和处理。所以，虽然Broadcast整个逻辑不复杂，却是足够有用和好用，它统一了Android的事件广播模型，让很多平台都相形见绌了。更多Broadcast Receiver相关内容，参见：<strong><a id="mnjr" href="http://androidappdocs.appspot.com/reference/android/content/BroadcastReceiver.html" title="/reference/android/content/BroadcastReceiver.html" target="_blank">/reference/android/content/BroadcastReceiver.html</a></strong>。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">Content Provider</h2><div style="margin-top: 0px; margin-bottom: 0px; ">Content Provider，听着就和数据相关，没错，这就是Android提供的第三方应用数据的访问方案。在Android中，对数据的保护是很严密的，除了放在SD卡中的数据，一个应用所持有的数据库、文件、等等内容，都是不允许其他直接访问的，但有时候，沟通是必要的，不仅对第三方很重要，对应用自己也很重要。</div><div style="margin-top: 0px; margin-bottom: 0px; ">比如，一个联系人管理的应用。如果不允许第三方的应用对其联系人数据库进行增删该查，整个应用就失去了可扩展力，必将被其他应用抛弃，然后另立门户，自个玩自个的去了。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Andorid当然不会真的把每个应用都做成一座孤岛，它为所有应用都准备了一扇窗，这就是<strong>Content Provider</strong>。应用想对外提供的数据，可以通过派生<a id="i3v3" href="http://androidappdocs.appspot.com/reference/android/content/ContentProvider.html" title="ContentProvider" style="color: #551a8b; " target="_blank">ContentProvider</a>类，封装成一枚Content Provider，每个Content Provider都用一个<strong>uri</strong>作为独立的标识，形如：<strong>content://com.xxxxx</strong>。所有东西看着像REST的样子，但实际上，它比REST更为灵活。和REST类似，uri也可以有两种类型，一种是带id的，另一种是列表的，但实现者不需要按照这个模式来做，给你id的uri你也可以返回列表类型的数据，只要调用者明白，就无妨，不用苛求所谓的REST。</div><div style="margin-top: 0px; margin-bottom: 0px; ">另外，Content Provider不和REST一样只有uri可用，还可以接受<strong>Projection</strong>，<strong>Selection</strong>，<strong>OrderBy</strong>等参数，这样，就可以像数据库那样进行投影，选择和排序。查询到的结果，以<strong>Cursor</strong>（参见：<strong><a id="ke6j" href="http://androidappdocs.appspot.com/reference/android/database/Cursor.html" title="reference/android/database/Cursor.html" target="_blank">reference/android/database/Cursor.html</a></strong>&nbsp;）的形式进行返回，调用者可以移动Cursor来访问各列的数据。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Content Provider屏蔽了内部数据的存储细节，向外提供了上述统一的接口模型，这样的抽象层次，大大简化了上层应用的书写，也对数据的整合提供了更方便的途径。Content Provider内部，常用数据库来实现，Android提供了强大的<strong><a id="pai2" href="http://androidappdocs.appspot.com/reference/android/database/sqlite/package-summary.html" title="Sqlite" style="color: #551a8b; " target="_blank">Sqlite</a></strong>支持，但很多时候，你也可以封装文件或其他混合的数据。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在Android中，<strong><a id="zq4m" href="http://androidappdocs.appspot.com/reference/android/content/ContentResolver.html" title="ContentResolver" style="color: #551a8b; " target="_blank">ContentResolver</a></strong>是用来发起Content Provider的定位和访问的。不过它仅提供了同步访问的Content Provider的接口。但通常，Content Provider需要访问的可能是数据库等大数据源，效率上不足够快，会导致调用线程的拥塞。因此Android提供了一个<strong>AsyncQueryHandler</strong>（参见：<strong><a id="p:1b" href="http://androidappdocs.appspot.com/reference/android/content/AsyncQueryHandler.html" title="reference/android/content/AsyncQueryHandler.html" style="color: #551a8b; " target="_blank">reference/android/content/AsyncQueryHandler.html</a></strong>），帮助进行异步访问Content Provider。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在各大组件中，Service和Content Provider都是那种需要持续访问的。Service如果是一个耗时的场景，往往会提供<strong>异步访问</strong>的接口，而Content Provider不论效率如何，都提供的是约定的<strong>同步访问</strong>接口。我想这遵循的就是<strong>场景导向设计</strong>的原则，因为Content Provider仅是提供数据访问的，它不能确信具体的使用场景如何，会怎样使用它的数据；而相比之下，Service包含的逻辑更复杂更完整，可以抉择大部分时候使用某接口的场景，从而确定最贴切的接口是同步还是异步，简化了上层调用的逻辑。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">配置</h2><div style="margin-top: 0px; margin-bottom: 0px; ">四大组件说完了，四大组件幕后的英雄也该出场了，那就是每个应用都会有一份的配置文件，名称是<strong><a id="bqcp" href="http://androidappdocs.appspot.com/guide/topics/manifest/manifest-intro.html" title="AndroidManifest.xml" style="color: #551a8b; " target="_blank">AndroidManifest.xml</a></strong>，在工程的根目录下。在这个配置文件中，不仅会描述一些应用相关的信息，很重要的，会包含一个应用中所有组件的信息。如果你派生Activity或者Service实现了一个相关的类，这只是把它组件化的第一步，你需要把这个类的相关信息写到配置文件中，它才会作为一个组件被应用到，否则只能默默无闻的黯淡度过余生。</div><p>&nbsp;</p><p>&nbsp;<img src="http://images.cnblogs.com/cnblogs_com/duguguiyu/1.png" width="600" height="445" alt="" /></p><p>&nbsp;</p><div style="margin-top: 0px; margin-bottom: 0px; ">摆了一幅图出来，这次不是偷来的，是敝帚自珍原创，所以没有意外的画的很丑，但基本还是可以体现出一些意思。在<strong>In Others</strong>的部分，这里是一般平台应用之间通信和交互的模型，每个应用都有很强烈的应用边界（往往表现为进程边界...），App 1的还是App 2的，分得很是清楚。每个应用内部，都有自己的逻辑去切分功能组件，这样的切分通常没有什么标准，率性而为。应用间的交互逻辑也比较零散，App 1与App 2交互，往往需要明确知道对方应用的具体信息，比如进程ID，进程名称之类的，这样使得应用和应用之间的联系，变得很生硬。而上层应用和系统应用的通信，往往有很多特定的模式，这种模式，很可能是无法直接应用在普通应用之间的，换而言之，系统应用是有一定特殊性的。</div><div style="margin-top: 0px; margin-bottom: 0px; ">重点，在图的下半部，描述的是<strong>Android</strong>的应用情形。在Android中，<strong>应用的边界</strong>，在组件这个层面，是极度模糊，什么进程、什么应用，都可以不必感知到。举个例子，App 1，实现了A和B两个组件，App 2，实现了C这个组件。A和C，都想使用B这个组件，那么它们的使用方式是<strong>完全一致</strong>的，都需要通过系统核心的组件识别和通信机制，找到和使用组件B。A，虽说和B是一个娘胎里蹦出来的，很不好意思，没有任何特殊的后面和捷径，还是要跑规矩的途径才能用到，一片和谐社会的景象油然而生。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在Android中，<strong>所有组件的识别和消息传递逻辑</strong>都必须依赖底层核心来进行（通信可以没有底层核心的参与，比如一旦Service找到了，就可以和它产生持久的通信...），没有底层核心的牵线搭桥，任何两个组件都无法产生联系。比如一个Activity，跳到另一个Activity，必须要向底层核心发起一个Intent，有底层解析并认可后，会找到另一个Activity，把相关消息和数据传给它。一个Activity想使用Content Provider中的数据，必须通过底层核心解析相关的uri，定位到这个Content Provider，把参数传递给它，然后返回Activity需要的Cursor。这样的设计，保证了底层核心对所有组件的绝对掌控权和认知权，使得搭积木似的开发变成可能。</div><div style="margin-top: 0px; margin-bottom: 0px; ">为了，使得核心系统能够完整的掌握每个组件的信息，这就需要配置文件了。配置文件，就是将组件插到底层核心上的这个<strong>插头</strong>。只有通过这个插头插在底层核心的插座上（不要乱想，非十八禁...），组件才能够发光发热，闪耀光芒。</div><div style="margin-top: 0px; margin-bottom: 0px; ">组件的配置信息在我看来主要包含两个方面，一部分是描述<strong>如何认知</strong>。比如，Activity、Service、Broadcast Receiver都会有名字信息，和希望能够把握的Intent信息（姑且看成消息好了...），Content Provider会有一个描述其身份的uri。当其他组件通过这样的名字或者Intent，就可以找到它。</div><div style="margin-top: 0px; margin-bottom: 0px; ">另一部分是<strong>运行相关</strong>的信息。这个组件，期望怎么来运行，放在单独的进程，还是和调用者一个进程，还是找相关的其他组件挤在同一个进程里面，这些内容，都可以在配置的时候来决定（调用者在这个约束范围内，有进一步的选择权...）。更多配置项，请参见：<strong><a id="vwiq" href="http://androidappdocs.appspot.com/guide/topics/manifest/manifest-intro.html" title="guide/topics/manifest/manifest-intro.html" style="color: #551a8b; " target="_blank">guide/topics/manifest/manifest-intro.html</a></strong>。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><div style="margin-top: 0px; margin-bottom: 0px; ">通过前续内容，也许可以帮助大家对Android组件有个初略的了解。但这些了解都还停留在静态层面，程序是个动态的概念，关于各个组件具体是怎么联系在一起的，如何手拉手运行起来完成一项功能的，这便是后话了。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div></span></div><img src="http://www.cnblogs.com/duguguiyu/aggbug/1659980.html?type=1" width="1" height="1" alt=""/><p>评论: 3　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/30/1659980.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/30/1659980.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>深入Android 【二】 —— 架构和学习</title><link>http://www.cnblogs.com/duguguiyu/archive/2010/01/23/1654559.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Fri, 22 Jan 2010 18:26:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2010/01/23/1654559.html</guid><description><![CDATA[<p>阅读: 2388 评论: 10 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2010-01-23 02:26 <a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/23/1654559.html" target="_blank">原文链接</a></p><div><span class="Apple-style-span" style="font-family: Verdana; line-height: normal; font-size: 13px; "><h1 style="font-size: 18pt; "><font class="Apple-style-span" face="Georgia"><font size="6">Android架构和特征</font></font></h1><blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; "><font class="Apple-style-span" face="Georgia">千呼万唤始出来，犹抱琵琶半遮。 -- 《琵琶行》</font></blockquote><div style="margin-top: 0px; margin-bottom: 0px; ">虽贵为富二代，但Android要是没任何可圈点的地方，开不过70迈，在玲琅满目的手机平台竞争中，充其量也就做几个俯卧撑打一桶酱油，然后被落的远远的。说到底，出来混，靠的还是技术。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">架构</h2><div style="margin-top: 0px; margin-bottom: 0px; "><div style="margin-top: 0px; margin-bottom: 0px; "><img src="http://docs.google.com/File?id=ddwgxw9r_869c6pstfgr_b" style="width: 648px; height: 465.323983px; "  alt="" /></div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div></div><div style="margin-top: 0px; margin-bottom: 0px; ">从SDK文档中，偷来一幅Android平台的<strong>架构图</strong>，如上。在整个架构最底层红彤彤的部分，是<strong>Linux Kernel</strong>在移动平台的一个移植，它隐藏了硬件、网络等相关的细节，为上层提供了一个相对纯洁的统一接口。除非要做的是Android到不同设备的移植工作，否则对于大部分普通开发者而言，基本上是远观而不必亵玩的。Google一直强调，Android的底层实现异常NB，可移植性超强，暂没有功夫研读，实属遗憾。</div><div style="margin-top: 0px; margin-bottom: 0px; ">靠上一层，是一些<strong>核心的和扩展的类库</strong>，它们都是原生的C++实现。在这一层，你可以看到很多熟悉的面孔，一如SQLite、WebKit、OpenGL，开源的力量与贡献由此可见。如果，该层类库需要被上层函数调用，就必须要通过<strong>JNI</strong>的导出相应的接口函数，否则就只能在层次内部自个把玩。</div><div style="margin-top: 0px; margin-bottom: 0px; ">也是在这一层次上，还有为上层Java程序服务的<strong>运行时</strong>。<a id="i2fy" href="http://en.wikipedia.org/wiki/Dalvik_virtual_machine" title="Dalvik虚拟机" target="_blank">Dalvik虚拟机</a>，是Android的Java虚拟机，之所以不采用J2ME的虚拟机，一方面是因为J2ME的设计是为了低端机器而优化，而Dalvik则是为了高端一些的机器进行优化，提供更好的性能。另一方面，从商业角度来看，必须绕开J2ME虚拟机，Android才能彻底解放，想怎么开源就怎么开源，不再需要考虑License的问题。</div><div style="margin-top: 0px; margin-bottom: 0px; ">再往上，终于有Java出没了。首先是<strong>框架层</strong>，这里包含所有开发所用的SDK类库，另外还有一些未公开接口的类库和实现，它们是整个Android平台核心机制的体现。</div><div style="margin-top: 0px; margin-bottom: 0px; ">而在最上面，就是<strong>应用层</strong>了，系统的一些应用和第三方开发的所有应用都是位于这个层次上，也许要纠结两者的差别，就是系统应用会用一些隐藏的类，而第三方的应用，总是基于SDK提供的东西来搞。</div><div style="margin-top: 0px; margin-bottom: 0px; ">一般来说，Android开发，就是在SDK的基础上，吭哧吭哧用Java写应用。但自从有了NDK，一切有了写小变化。NDK的出现意味着，最上面应用层的内容，可以穿越Java部署的框架层，直接和底层暴露出来的，或者自行开发的C++库直接对话，当然在这些库中需要包含JNI的接口。</div><div style="margin-top: 0px; margin-bottom: 0px; ">人说，这就不是Android也可以用C++开发应用么，但其实，这样的说法不够确切，纯C++应用，是无法被接受的。因为在Android中，大量的核心机制部署在框架层，它们都是用Java实现的，比如控件库，Activity的调度之类的。因此，没了界面，没了调度，还是只用C++做类库比较合适，否则一切都乱了套了。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">特征</h2><div style="margin-top: 0px; margin-bottom: 0px; "><div style="margin-top: 0px; margin-bottom: 0px; "></div><div style="margin-top: 0px; margin-bottom: 0px; ">基于这样的架构，Android有很多的设计显得很有意思。纵览整个SDK和核心机制的设计，工整漂亮，是Android给人的第一感觉。为了说明这一点，找一个反面教材是很有必要的，Symbian同学毫无悬念的担当这个伟岸的角色。</div><div style="margin-top: 0px; margin-bottom: 0px; ">写Symbian程序，感觉就像是在玩一个猜谜游戏。哪怕你是一个Symbian老手，当需要用到Symbian中某块陌生功能的时候，你可能依然束手无策。你往往需要猜并反复找寻，在这里我需要使用哪一种奇巧淫技呢，是该臆想某些事件，还是应该用一个神秘的UID寻找某个特定应用，诸如此类。</div><div style="margin-top: 0px; margin-bottom: 0px; ">而做Android应用的时候，就像是做高考模拟试题，题看上去不一样，解答模式摸清楚，就一通百通，一了百了。监听某个系统事件，查一下SDK就好；访问某个应用的数据，看看它有没有提供Content Provider就可以。所有的一切，都是按套路出牌，只要你了解了套路，再陌生的牌也可以看得懂，出的顺。人说武林高手，都应该是无招胜有招，而一个好的应用框架，也应该做到举重若轻，可触类旁通。</div></div><div style="margin-top: 0px; margin-bottom: 0px; "><div style="margin-top: 0px; margin-bottom: 0px; "></div><div style="margin-top: 0px; margin-bottom: 0px; "><div style="margin-top: 0px; margin-bottom: 0px; ">而Android框架最文采飞扬的一点，就是引入了<a id="u-ip" href="http://en.wikipedia.org/wiki/Mashup_(web_application_hybrid)" title="Mash-Up" target="_blank">Mash-Up</a>的思想。所谓Mash-Up，就是把写应用搞成搭积木，要出效果的时候，东家一块西家一块现场拼起来就好。这里面关键有两点，一个是<strong>模块化</strong>，另一个就是<strong>动态性</strong>。所谓模块化，就是一个应用的功能要明确的被封成一个个边界清晰的功能点，每一个功能点都像是一个黑盒，由预先定义的规则描述出其交互方式；而动态性，就是这些独立的模块能够在运行的时候，按照需求描述，连接在一起，共同完成某项更大的功能。在这两点上，Android都做得非常出色。</div><div style="margin-top: 0px; margin-bottom: 0px; ">站在可Mash-Up构造应用这一点去看其他的一些Android中的核心功能设计，就显得很有章可循了。比如为什么要把文件私有化，为什么要让进程被托管，等等（当然也可以站在别的角度看出不同的效果，视角不同，视野自然不同...）。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在UI机制方面，Android也有很不错的表现。它采取xml格式的资源文件，描述所有界面相关的内容。资源文件不是什么新东西了，xml格式也是老调重弹，但可贵的是Android做的更为的丰富和彻底，基本把界面相关的逻辑，全部从代码中剥离到了资源文件中，和Symbian那四不像的资源文件相比，真是强大了不知多少倍了。</div></div></div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h1 style="font-size: 18pt; "><font class="Apple-style-span" face="Georgia"><font size="6">Android学习入门</font></font></h1><blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; "><font class="Apple-style-span" face="Georgia">不积跬步，无以至千里。 -- 《劝学》</font></blockquote><div style="margin-top: 0px; margin-bottom: 0px; ">说，万事开头难。想开始Android的开发，最重要的应该是先把马步扎稳，套路摸清楚，后面的事情就顺当多了。打开怀抱，拥抱Android，也许可以先做下面这些事。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">开发环境</h2><div style="margin-top: 0px; margin-bottom: 0px; ">辣手摧花成性的GFW，无情的把Android开发者官网关在了墙外。不过没关系，猛击<a id="mj1t" href="http://androidappdocs.appspot.com/index.html" title="这里" style="color: #551a8b; " target="_blank">这里</a>，同样可以殊途同归（Shit，一直在用代理，刚试了下，发现竟然也被盾了，如果不行，那就只能翻墙了...）。</div><div style="margin-top: 0px; margin-bottom: 0px; ">如果旅途顺利，你可以在路径<strong>sdk/index.html</strong>下找到安装说明，成功配置好Android的开发环境（<strong><font class="Apple-style-span" color="#FF0000">【注】</font></strong>：<u>在以后，如果要给开发者页面上的链接，都会给一个像sdk/index.html这样的相对路径，你可以在前面加上官网地址，或者本地SDK的doc地址拼凑成完整的路径，在一个盾牌横行的朝代，只能用这样委屈求全的方法保证能更好的使用...</u>）。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在2.0之前，每一次版本更新，你都要自己去下个全新的SDK，然后按照说明，小心翼翼的一步步修改eclipse的设置，甚是麻烦。在2.0后，这个模式有所改善，你会先下到一个类似于下载器的插件，通过它可以来管理和升级SDK，不仅简化了整个升级模式，还使得你可以更好的在各个不同的SDK版本间游走，利国利民。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Eclipse + ADT（Android Development Tool），是正牌的Android开发环境。你可以在Windows，Linux，Mac下做开发，甚为自由。比之Symbian的开发环境，ADT显得尤为强大，它对SDK提供的一堆优秀的命令行工具进行了UI上的封装，提供了图形界面（命令行控当然同样幸福，具体参见：<strong>guide/developing/tools/index.html</strong>）。通过ADT，你可以用运行和管理模拟器，使用调试器进行调试，过滤和查看Log，浏览模拟器上的文件信息，模拟拨号、短信等手机才有的事件，等等。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">文档</h2><div style="margin-top: 0px; margin-bottom: 0px; ">我知道，有很多人在学习一个新平台开发的时候，都习惯去买一些《xxx 21天精通》之类的书籍。但其实，最好的入门学习资料，就是<strong>SDK文档</strong>。因为只有做平台的自己，才能最了解平台中的各个玄机，各方面的轻重缓急，从而能够更好的对症下药药到病除。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在Android的SDK中，<strong>guide/index.html</strong>是由浅入深的教学文档，<strong>reference/packages.html</strong>是标准的API文档。对于教学文档，我的意见是，一字不拉的通读一遍甚至多遍，至少做到能对Android摸着头脑，并且碰到问题的时候，能够快速想起在哪里可以找到，回来深刻阅读。</div><div style="margin-top: 0px; margin-bottom: 0px; ">而API文档方面，Android做的算是还不错了。基本上每个类，每个接口，都有标准而详尽的说明，在一些尤为重要的类中，还具有大量的学习性的内容，不和Symbian似的，有太多的太监类，只有光秃秃的一个函数，一行文档说明都没有。整个文档结构是按照Java包来组织的，本身Java包命名的结构性和可读性很强，找起来也颇为方便。</div><div style="margin-top: 0px; margin-bottom: 0px; ">很多人对SDK文档有抵触情绪，我想，有两方面的原因。一则是SDK文档普遍缺少文学性，麻木不仁的八股文，难以下咽。Android在这方面做得算是乏善可陈，虽然算不上文采华丽，但还是挺适合阅读的。另一则，就会是语言方面的原因了。SDK文档多为英语，偶尔像MSDN这样有中文的，也停留在机器翻译的水平上，阅读起来颇为难受。特地在网上搜了下，找到一些翻译SDK的中文文档，比如<a id="uo54" href="http://www.oschina.net/uploads/doc/android_manual_cn.rar" title="这里" target="_blank">这里</a>。虽然是基于1.5 r1版本SDK所著，稍显过时，但翻译的还是有小用心的，作为辅助，也不失为一份好资料，特代表广大看官向这些为人民福利着想的同志致敬。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">Tutorials</h2><div style="margin-top: 0px; margin-bottom: 0px; ">光说不练假把式，除了读，在入门阶段，写也是一项不能少的运动。同样是在SDK中，Android提供了一组Tutorials和一些列的Samples，详见：<strong>resources/index.html</strong>。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Tutorials很简单，<strong>Hello World</strong>只是在教你如何在eclipse中，在ADT的帮助下，创建一个Android项目。相比之下，<strong>Hello Views</strong>复杂了些，它集中展示了几种标准的Android Layout 样式是如何构建的，很多时候，你都是在这些样式下扩展所需的UI。</div><div style="margin-top: 0px; margin-bottom: 0px; "><strong>Hello Localization</strong>，是教你如何使用资源的，做完这个，就可以了解Android的资源有多杀～。最后收官的是一个更为完整的<strong>Notepad Tutorial</strong>，它展示了很多Android的核心机制，比如基于Intent的Activity整合，Activity的生命周期等。迈过这个Tutorial，欢迎你，进入Android的大门。</div><div style="margin-top: 0px; margin-bottom: 0px; ">当然，做完Tutorials，对于Android而言，只是管中窥豹略见一斑。在SDK中，还提供了一系列的Samples。可以根据自己的需求，挑选合适的Sample编译运行和学习。但其中，有一个是不论你做什么，都需要必看必读必熟悉的，就是<strong>API Demos</strong>。在这个Sample中，集中展示了Android重点功能的API使用，把这个Sample用熟悉，需要做什么的时候过去找一下就可以很快的入手了。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">源码</h2><div style="margin-top: 0px; margin-bottom: 0px; ">到这里，很多看官一定很不屑，前面所谓的学习入门介绍，只不过是围着SDK打转。其实，事实也是如此，SDK中包含的内容是真的非常重要，我只是期望通过一些简短的介绍，激起一些初学者的重视，如是而已。</div><div style="margin-top: 0px; margin-bottom: 0px; ">当然，SDK每一个平台都有，没什么稀罕的。但Android有另一个非常稀罕而值钱的看家法宝，就是源代码。从Android Source的主站上：<a href="http://source.android.com/" style="color: #551a8b; " target="_blank">http://source.android.com/</a>，你可以获得整个平台的源码以及相关介绍。非常苦口婆心的期望大家都去down一份源码放在机器上，哪怕你不需要进行修改编译，放在机器上当百科全书也是远胜于任何一本Android教学书籍的。本系列文章后续很多内容，都是从源码中学习到的一些浅薄见识。</div><div style="margin-top: 0px; margin-bottom: 0px; ">对于大部分开发者都有学习价值的源码，主要在源码的<strong>frameworks</strong>和<strong>packages</strong>目录下。前者包含的是平台核心的一些实现，比如你需要自定义一个控件，也许你就可以翻到一个系统控件的实现中去，看看它是怎么来做的。后者包含一些系统的应用实现，比如你想做个播放器，也许你可以先去参考参考系统自带的是具体怎么做的。这样的实现，即便不算是最华美，至少也是最标准，其价值不容小视。</div><div style="margin-top: 0px; margin-bottom: 0px; ">另外，你也可以把它当个<strong>代码库</strong>来使，不会使用某个类，grep一把，也许就能获得一份最漂亮的Sample。当然，如果你有时候对某些系统机制表示费解，抑或有一些bug不知道源头在哪，都可以跟着源码顺藤摸瓜的搞清楚。这样的好东西，可不是每个平台都能够享用的。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">其他</h2><div style="margin-top: 0px; margin-bottom: 0px; ">论坛，其实对于开发和学习都是很重要的资源。毕竟，所有的资料都是死的，只有人是活得，能够最大限度的因地制宜解决问题。</div><div style="margin-top: 0px; margin-bottom: 0px; ">只不过，标准的官方论坛，放在Google Group上，已经惆怅的被盾了。中文论坛方面，没有特别优秀和活跃的，这一方面是由于Android的发展现状还不算很磅礴，另一方面是由于Android的开发相对于Symbian而言，奇技淫巧少了很多，没有那么多好问的。也许你可以去去csdn这样的传统论坛，或者<a id="m4by" href="http://www.eoeandroid.com/" title="eoe" style="color: #551a8b; " target="_blank">eoe</a>这样专门的论坛。有的时候，还是多少能获得一些帮助的。</div><div style="margin-top: 0px; margin-bottom: 0px; ">书籍方面，真没有什么推荐，豆瓣上<a id="xxjt" href="http://www.douban.com/subject_search?search_text=Android&amp;cat=1001" title="搜索一下" style="color: #551a8b; " target="_blank">搜索一下</a>，你可以看到，目前的书籍，基本上还是集中在SDK使用层面上，很少有解析的很透彻，做的很深入的。而SDK的使用，看SDK的文档就足够了，如果实在对e文不感冒，买一两本评价不太差的中文书籍，放着翻翻也还是挺好。</div><div style="margin-top: 0px; margin-bottom: 0px; ">更进一步，也许可以读读一些经验性的文档，去Google Code上搜索一些代码回来看看。比如，SDK文档中，有个经验性文档的集合：<strong>resources/articles/index.html</strong>，就可以翻看一下。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><div style="margin-top: 0px; margin-bottom: 0px; ">最后，更多的一切还需要自己在工程和思考中，慢慢总结。相信，好的代码，会垂青一个勤于动手和思考的人。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div></span></div><img src="http://www.cnblogs.com/duguguiyu/aggbug/1654559.html?type=1" width="1" height="1" alt=""/><p>评论: 10　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/23/1654559.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/23/1654559.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>深入Android 【一】 —— 序及开篇</title><link>http://www.cnblogs.com/duguguiyu/archive/2010/01/21/1652868.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Wed, 20 Jan 2010 16:56:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2010/01/21/1652868.html</guid><description><![CDATA[<p>阅读: 2302 评论: 24 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2010-01-21 00:56 <a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/21/1652868.html" target="_blank">原文链接</a></p><br /> <div><span class="Apple-style-span" style="font-family: Verdana; line-height: normal; font-size: 13px; "><h1 style="font-size: 18pt; "><font class="Apple-style-span" face="Georgia"><font size="6">序</font></font></h1><blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; ">携来百侣曾游，忆往昔峥嵘岁月稠。 -- 《沁园春&#183;长沙》</blockquote><div style="margin-top: 0px; margin-bottom: 0px; ">对于Android，我也算是老人了，所谓，<a id="tde7" href="http://www.cnblogs.com/duguguiyu/category/112850.html" title="有文有真想" style="color: #551a8b; " target="_blank">有文有真想</a>。正由于这段玩票经历，使得我在毕业后，鬼使神差的成为移动平台的一名码工，再次有机会放肆的拥抱Android。</div><div style="margin-top: 0px; margin-bottom: 0px; ">2010开年，手上突然有了一把闲散时间，有机会进一步总结和学习Android。于是想再一次为Android写一系列的东西，这些东西来自于一些开发经验，对源码的学习和对Android的浅薄认识，也算是鞭笞自己学习的一种手段。</div><div style="margin-top: 0px; margin-bottom: 0px; ">其下所有内容，预计有十数篇，抑或更多。基本和技术相关，也许会配有一些其他相关比较闲淡的话题。可能会有一些具象实例，但更多的可能是自己的一些理解和认知。所有一切，源自于妄自挖掘，难免有疏漏或误解，观者淡定。</div><div style="margin-top: 0px; margin-bottom: 0px; ">以此为序，有心者，望共勉。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h1 style="font-size: 18pt; "><font class="Apple-style-span" face="Georgia"><font size="6">Android简史</font></font></h1><blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; "><font class="Apple-style-span" face="Georgia">人生若只初识，何事秋风悲画扇。 -- 《木兰辞》</font></blockquote><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><div style="margin-top: 0px; margin-bottom: 0px; ">要说当今移动平台的当红辣子鸡，Android说它是第二，也许没有别家敢认这个第一（好吧，iPhone，有意见就说...）。了解Android开发平台的过去和现状，除了往下看，另有便捷的方式就是在WikiPedia中键入<a id="djnh" href="http://en.wikipedia.org/wiki/Android_(operating_system)" title="Android" style="color: #551a8b; " target="_blank">Android</a>，在这里，特此鸣谢GFW友情放生。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">诞生</h2><div style="margin-top: 0px; margin-bottom: 0px; ">早在2005年7月，Google舞动着手中的美刀，收下了由<a id="v536" href="http://en.wikipedia.org/wiki/Andy_Rubin" title="Andy Rubin" target="_blank">Andy Rubin</a>（传说中的Android之父...）等人创立的一家小公司，他们当时做的就是基于Linux内核的手机操作系统，也就是小时候的Android。经过Google多年打磨，Android在07年末，正二八经的粉墨登场开门接客。</div><div style="margin-top: 0px; margin-bottom: 0px; ">自打一出生，Android便被钉板在富二代的角色上，不仅是因为老爹有钱的令人发指，也是因为其后有一帮金光闪耀的叔伯们保驾护航。这个叔伯群，便是响当当的开放手机联盟<a id="zvwk" href="http://en.wikipedia.org/wiki/Open_Handset_Alliance" title="OHA" target="_blank">OHA</a>（Open Handset Alliance）。这个联盟涵盖了中国移动、T-Mobile、Sprint这样的移动运营商，也包括HTC、Motolora、三星这些的手机制造商，同时还有以Google为代表的手机软件商，以Inter、Nvidia为标志的底层硬件厂商和<a id="xkte" href="http://en.wikipedia.org/wiki/The_Astonishing_Tribe" title="Astonishing Tribe" target="_blank">Astonishing Tribe</a>这样的商业运作公司（这公司是啥我也不晓得...）。作为后援团，他们理论上的任务，是各尽其长，全力捧红Android，实际上的任务是齐心协力，借Android东风赚一个盆钵满响。</div><div style="margin-top: 0px; margin-bottom: 0px; ">当然，Android自所以被万众瞩目一炮走红，不仅仅是丫实在太有背景，同时，它也有这太多的新鲜的概念。Android是一个开源的平台（恩，真正的全面开源，是在发布后很久以后了...），它给那些捂着自家平台源码当宝的竞争对手们一记当头棒喝。Android自行研发了一套Java虚拟机，当时仅提供Java API的支持（NDK是更久以后的事情了...），号称为专为高端智能设计。Android开发环境支持所有主流操作系统平台，包括Windows，Linux，Mac，即便到今天，在手机开发中也是极其罕见的。Android的带头人Google，本身是做网络起家，Android内嵌大量Google网络应用，听上去就显得很酷。这所有的一切，共同造就了Android那鹤立鸡群，不染风尘的少侠形象。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">造势</h2><div style="margin-top: 0px; margin-bottom: 0px; "></div><div style="margin-top: 0px; margin-bottom: 0px; ">推出伊始，Google还有一个很震撼的推广举动，就是举行所谓的Android程序挑战赛（Android Developer Challenge, ADC）。整个比赛分成两场，第一场（ADC1）比赛，在没有任何真机问世，SDK还是个雏形的状况下，便鸣金开锣了。</div><div style="margin-top: 0px; margin-bottom: 0px; ">比赛套路是无差别的群殴，基本概念是无论你来自何方（还是要满足美国法律要求和避嫌要求的），无论你想做些什么，也不管你是光杆司令还是流氓团伙，只要提交一个能在模拟器上跑起来的程序，就可参赛。而比赛只是对你作品进行参观评比，作品的所有权依然放在开发者的口袋中。</div><div style="margin-top: 0px; margin-bottom: 0px; ">当然，这还不算什么创新，NB的是无比丰厚的奖金，整个ADC的奖金高达1千万美刀，每场各半，基本上首轮入围奖（前50）已经超越了那时候一般程序比赛的头名奖金，这对很多小公司和个人而言，无疑是具有很强吸引力的。于是，各路打酱油好手蜂拥而至，各论坛、博客、网站也七嘴八舌的讨论开来，一时间，满城风雨。</div><div style="margin-top: 0px; margin-bottom: 0px; ">ADC1我也很厚颜无耻的参加了，结果当然可以预想，一毛钱都没摸上。回想整个过程，差距最大的并不是在技术上，而是认知上，我们玩的产品是人家几年前玩剩下的，说创新只是一抹笑谈。</div><div style="margin-top: 0px; margin-bottom: 0px; "></div><div style="margin-top: 0px; margin-bottom: 0px; ">当时觉着，Google太NB了，ADC这种车马未动粮草先行的招太华丽了，就这动静，不论比出来个啥结果，这1千万刀也掏值了。但时过境迁，现在回头来想，也许一切并不是看上去那么美。由于没有扎扎实实的真机摆出来，大家普遍抱着一种玩票的心理，真的敢不顾一切舍下身家性命押宝在Android上的尽在少数，这就锻造了Android平台很长一段时间的只见雷不见雨的局面。而等喧嚣过后，很多人热情消退，Google真端出Android真机的时候，还需要重新热场再来一次，也许，真的有些得不偿失。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">困境</h2><div style="margin-top: 0px; margin-bottom: 0px; ">所有的东西现在来说，都是事后诸葛亮，只能听个响不能当个真。而真实的状况是ADC1很快进入囧境，由于架构设计上的种种原因，Google花了比预想多的多的时间做Android的优化工作，ADC1比赛被迫不断延期，彻底沦为懒婆娘的裹脚布。各路曾热捧Android的媒体，也不失时机的倒戈，亲手在自己画上的感叹号后面，重重画上了个大大的问号。</div><div style="margin-top: 0px; margin-bottom: 0px; ">祸不单行，同样是由于Android的性能问题，虽然各路高手把Android移植到了不同的手机平台上，但传说中的GPhone一直难产中，使得人们不免有了胎死腹中的猜测。</div><div style="margin-top: 0px; margin-bottom: 0px; ">与此同时，其他对手可一点也没闲着。iPhone很快宣布开放SDK，以此来勾引纯情的开发人员。Symbian被Nokia彻底收购，成为Nokia的自留地，开源计划也很快浮出水面。所有景象，对Android而言都犹如梦魇。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">破茧</h2><div style="margin-top: 0px; margin-bottom: 0px; ">所有一切困境，都在G1发布后，渐渐消散了。2008年10月，第一款搭配Android平台的真机，搭载着无限光荣与梦想的HTC Dream正式发售，这就是注定要载入史册的G1。虽然比之当时绝代风华的iPhone，粗陋的G1犹如村姑遇上公主一般，但无论如何，G1让人们真真切切的看到了Android。这就犹如你家买的跳票N久的期房，终于见着了个毛胚房，那种感觉，除了踏实，找不到更适合的词汇了。</div><div style="margin-top: 0px; margin-bottom: 0px; ">好事当然也会成双，G1它不是一个人在战斗。ADC1总算是落下帷幕，Android Market的也顺理成章的破茧而出，早期的应用，大都来自于ADC1的贡献。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Android也结束了伪开源的历史旅程，正式开发SDK的源码实现，搭配的是Apache的License，这种坦诚相见的感觉看上去很不错。</div><div style="margin-top: 0px; margin-bottom: 0px; ">忠心耿耿的HTC，更是再接再励，在G1后，陆续发布了Magic（G2）和Hero（G3）。尤其是Hero的现身，惹得一阵小惊艳，HTC为Hero搭配的是基于Android改造UI的Sense系统，以华丽的界面风格赚足了眼球，也创了改造Android的先河。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在HTC高歌猛进的同时，猫在螳螂后的一群黄雀，也敌在动我也动了。摩托罗拉，三星，LG，华为，戴尔，联想等一干手机厂商纷纷跟进，各式各样的Android蜂拥而至。与此同时，其他嵌入式厂商也推陈出新，爱可视（Archos）发布了基于Android的平板设备，明基的Android上网本也是箭在弦上，而基于Android的手持电子书阅读设备也不断的被推出，庞大的Android联盟初现峥嵘。</div><div style="margin-top: 0px; margin-bottom: 0px; ">为了避免同质化，各个厂商纷纷对Android进行的改造，摩托罗拉推出了Cliq，打得是SNS整合牌，三星的新系统也是被广泛期待，而中移动的OMS丑媳妇也要见婆娘了，打着整合移动服务牌@_@的OMS，以丑陋的外貌、低下的SDK版本和雷死人不偿命的宣传文案（绝口不提Android，只说自己做了大量很NB的工作，其实...，哎，咋就那么小家子气呢...）也算是招来大量眼球。</div><div style="margin-top: 0px; margin-bottom: 0px; ">而还是没能耐住寂寞的Google，联手HTC，一同推出了至今只为止最重量级的Android手机：Nexus One。江湖有云：天下武功，无快不破。搭载了全新的Android 2.1，1G的CPU，史上最清晰的手机屏幕的Nexus One，快的是一塌糊涂迅雷不掩耳盗铃小叮当，在单机层面，第一次使得Android手机与iPhone掰手腕的能力（之前与iPhone的比较，都需要依靠集团力量，三英战吕布...）。</div><div style="margin-top: 0px; margin-bottom: 0px; ">在各家厂商努力的同时，Android本身也没有闲着，版本从1.1，一步步进阶到了2.1，SDK的升级，伴随着大量性能、接口的改进，和功能的丰富，Android变得越来越快，越来越省电，越来越丰富，越来越多Google服务被嵌入@_@。由于Android SDK是基于Java的，即便虚拟机做的很是NB，在某些情况下，性能也是无法与原生的C++代码相提并论，于是，从1.5版起，除了SDK，Android还拥有了NDK（Native Develop Kit），它提供了一些C++的库和编译环境（库是真的很少...），开发人员可以基于C++写底层库，用Java写上层逻辑，通过混编的方式，兼得鱼和熊掌。</div><div style="margin-top: 0px; margin-bottom: 0px; ">Android Market的发展也甚为迅猛，虽然和其鼻祖App Store相比，应用的规模和盈利能力还显得比较幼齿，但其涨势凶猛，发展趋势远胜于前辈。国内一些比较著名的手机软件，也纷纷拥有了Android版本的小弟，比如<a id="mk-j" href="http://www.youdao.com/" title="网易有道" style="color: #551a8b; " target="_blank">网易有道</a>的出品的<a id="u:vq" href="http://m.youdao.com/help/cidian?keyfrom=duguguiyu" title="有道词典Android版" target="_blank">有道词典Android版</a>（好吧，这是在插播广告，欢迎大家进行围观并提宝贵意见...）。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><h2 style="font-size: 14pt; ">起飞</h2><div style="margin-top: 0px; margin-bottom: 0px; ">种种迹象表明，2010，也许就是姗姗来迟的Android元年。三星，moto，LG，HTC等多家手机制造厂商，都为2010年将推出的半数以上的手机搭配了Android。在国内，移动的OPhone，丑媳妇要正式揭开盖头了，惨烈是惨烈了一点，但聊胜于无，除了水货，2010毕竟至少多了条购买Android手机的道路。</div><div style="margin-top: 0px; margin-bottom: 0px; ">软件开发方面，大家也从抱着双臂冷眼旁观的状态，进入到了一种伺机而动的战略准备阶段。前不久召开的moto开发者大会，惊现国内各领域的公司，试水开始，可见一斑。国内各个山寨的Market的，也越来越货源充足，下载量稳步上升，升温，就在当下。</div><div style="margin-top: 0px; margin-bottom: 0px; ">而随着G2为首的Android水机价格火速下调，身边路边地铁边，可以看到越来越多的人，把玩着各式各样的Android手机，状况尤为喜人。</div><div style="margin-top: 0px; margin-bottom: 0px; "><br /></div><div style="margin-top: 0px; margin-bottom: 0px; "><font size="2">所以，2010，如果你有心，就做好准备吧。</font></div><div><font size="2"><br /></font></div><div style="margin-top: 0px; margin-bottom: 0px; "></div></span></div><img src="http://www.cnblogs.com/duguguiyu/aggbug/1652868.html?type=1" width="1" height="1" alt=""/><p>评论: 24　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/21/1652868.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2010/01/21/1652868.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Symbian手记【五】 —— Symbian的异步框架</title><link>http://www.cnblogs.com/duguguiyu/archive/2009/06/17/1504701.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Tue, 16 Jun 2009 17:13:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2009/06/17/1504701.html</guid><description><![CDATA[<p>阅读: 2027 评论: 3 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2009-06-17 01:13 <a href="http://www.cnblogs.com/duguguiyu/archive/2009/06/17/1504701.html" target="_blank">原文链接</a></p><span  style="font-family: Verdana; font-size: 13px; line-height: normal; ">
<div style="margin-top: 0px; margin-bottom: 0px; ">永远活在同步的流程里，无疑是我等码工最大的奢望之一。为了不阻塞UI，为了读写一陀陀数据，为了含辛茹苦的演算复杂的逻辑，为了大家和睦相处共同劳动，总是需要异步处理，你一下我一下共同完成任务。在Symbian中，做了一套机制来做这件事情，这就是Active Objects。</div>
<h2 style="font-size: 14pt; ">Active Objects</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">Active Object是一套事件驱动的多任务模型。在Symbian的标准线程中（除掉一些Java构造线程、原生C构造线程，等），都包含有一个消息循环，在循环中，会不停的查询注册在该线程所属的CActiveScheduler各个ActiveObject的TRequestStatus状态，一旦发现可执行的任务，立马激活并执行。经典的循环伪码如下：</div>
<blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; ">// *** in the loop ***<br />
<br />
User::WaitForAnyRequest();<br />
<br />
FOREVER<br />
&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;if(activeObject-&gt;IsActive() &nbsp;&amp;&amp; activeObject-&gt;iStatus != KRequestPending)<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;activeObject-&gt;iActive = EFalse;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;TRAPD(r, activeObject-&gt;RunL());<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if( r != KErrNone )<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;{<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;r = activeObject-&gt;RunError();<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;if( r != KErrNone )<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;Error(r);<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;break;<br />
&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;}<br />
&nbsp;&nbsp; &nbsp;}</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px; ">一码解千语。在消息循环中，会等待事件激活，接着，会遍历查询注册各个Active Object的状态。当然，简单的遍历是不够体面的，每个Active Object都是带着优先级来的，毫无疑问，优先级高的会被优先考虑执行，低的永远也超越不了。默认，大家都使用EPriorityStandard，一切太平。如果，一个Active Object是需要长时间被执行的，可以考虑使用低优先级，EPriorityIdle、EPriorityLow，这样执行的频率会低一些。而如果任务是具有一些实时要求的，就需要使用高的优先级，亦如，EPriorityUserInput、EPriorityHigh。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">但这个模型，是不足够满足实时要求的，因为它是非抢占式的。一个低优先级的任务，被调度了，在那里磨叽磨叽，你优先级再高也奈何不了。在这种模式下，要保持良好的响应能力，需要自律。首先，如果一个任务的实时性高，那么就需要调高它执行线程或进程的优先级，让它优先被CPU调度，不给低优先级任务执行的机会。还有，就是不要在Active Object的RunL中放置执行效率低下的代码（如果是非主线程，那就看菜下饭了...），它会使得该线程失去响应其他请求的能力。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">每个异步的任务，都需要派生自CActive类。如果用系统向导，可以发现，Symbian希望每个CActive的子类，做以下几件事情：</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">
<ul style="margin-top: 0px; margin-bottom: 0px; ">
    <li style="margin-top: 0px; margin-bottom: 0px; ">实现RunL，放置等待回调一方的执行代码；</li>
    <li style="margin-top: 0px; margin-bottom: 0px; ">实现DoCancel方法，把未有机会到达目的地的任务妥善安置，这是一个模板方法模式，它会被放在Cancel，仅在状态为执行时，会被调用；</li>
    <li style="margin-top: 0px; margin-bottom: 0px; ">实现RunError，在执行出错后，给该CActive子类一次自我救赎的机会；</li>
    <li style="margin-top: 0px; margin-bottom: 0px; ">尝试告诉使用者，这世界没有免费的午餐，需要用SetActive将请求发送给那个执行者，让它帮你搞定问题。</li>
</ul>
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">任务驱动的控制核心在于TRequestStatus，执行一方在接到任务后，会将TRequestStatus设置成为KRequestPending，当这个状态被再次改变了，才会触发CActiveScheduler调度执行回调。整个流程如下图所示（依然是盗窃来的...）：</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">
<div id="m_5t" style="margin-top: 0px; margin-bottom: 0px; text-align: left; "><img src="http://docs.google.com/File?id=ddwgxw9r_852cwhwdvfx_b" style="width: 648px; height: 639.751px; "  alt="" /></div>
</div>
<h2 style="font-size: 14pt; ">Client-Server框架</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">Client-Server框架，是Symbian的重要机制。在Symbian的内核层面，大量使用该模式，将文件管理，界面管理等功能都剥离到了各个服务中，呈微内核态势。所谓Client-Server框架，就是功能调用者Client，和功能执行者Server，各据一方，处于不同的线程或进程。它们搭建在Active Object上，通常通过异步模式进行调用。不过，Client-Server并没有对执行模式进行约束，比如文件服务，就具有同步和异步两种调用模式，当然，本质上换汤不换药，所谓同步只是阻塞异步来实现的。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">Client-Server分同进程和跨进程两种，不用说，通信模式完全不一样。在跨进程模式下，需要通信，就需要在Client和Server之间建立一个Session。Session是对Symbian IPC的封装，用协定方式进行通信，只不过，大数据的传输还需要各行其道各显神通。而同进程下，传递信息就简单多了，很多时候，同用一个指针就好。在跨进程模式下，适合集中控制，统一管理资源，特适合想文件服务这样的东东。而同进程，则适合大数据的传输，和逻辑的运算，各开各的线程，各跑各的逻辑。</div>
<h2 style="font-size: 14pt; ">结语</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">和同步相比，异步无疑是残酷的，但有了这些框架，比吭哧吭哧动手搞线程，搞通信来的开心多了。Symbian有这点能有的东西，还真不容易*_*。</div>
</span><img src="http://www.cnblogs.com/duguguiyu/aggbug/1504701.html?type=1" width="1" height="1" alt=""/><p>评论: 3　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/06/17/1504701.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/06/17/1504701.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Symbian手记【四】 —— Symbian的容器</title><link>http://www.cnblogs.com/duguguiyu/archive/2009/06/13/1502416.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Fri, 12 Jun 2009 16:55:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2009/06/13/1502416.html</guid><description><![CDATA[<p>阅读: 1550 评论: 5 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2009-06-13 00:55 <a href="http://www.cnblogs.com/duguguiyu/archive/2009/06/13/1502416.html" target="_blank">原文链接</a></p><span style="font-family: Verdana; font-size: 13px; line-height: normal; ">
<h1 style="font-size: 18pt; ">【四】 Symbian的容器</h1>
<div style="margin-top: 0px; margin-bottom: 0px; ">Symbian在设计之初，没有拥抱STL，这就要求，它需要重新制作一些轮子，容器便是其中的一个。</div>
<h2 style="font-size: 14pt; ">CArray系列容器</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">Symbian的设计者，非常喜欢复杂的继承结构和保罗万象的类，CArray系列的容器，就是在这种理念下的产物。CArray是顺序容器，相当于STL的vector + list，以及更多。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">CArray系列容器，在继承的最底端，也就是可实例化使用的类，都采用CArrayXxxYxx的命名方式，即：CArray + 对象单元存储方式 + 对象段存储方式。所谓对象单元存储方式，就是表征容器中每一个单元数据，是如何存放的，在CArray中，主要有四种：</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">
<ul style="margin-top: 0px; margin-bottom: 0px; ">
    <li style="margin-top: 0px; margin-bottom: 0px; ">Flat，容器中的每个数据，都是等长同类的；</li>
    <li style="margin-top: 0px; margin-bottom: 0px; ">Var，容器中的每个数据长度，可以是不同的；</li>
    <li style="margin-top: 0px; margin-bottom: 0px; ">Pak，容器中的数据分成若干部分，每一部分都有一个leading-byte表示这一段的长度，形如描述符；</li>
    <li style="margin-top: 0px; margin-bottom: 0px; ">Ptr，容器存放CBase子类对象的指针数据。</li>
</ul>
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">每个容器，都有一个重要的参数，它是一个整数，称为Granularity，即，每一组元素的个数。组是CArray容器分配内存的单位，在Granularity范畴内，元素都按照上述四种模式进行存储，但是，Granularity总是一个有限的数，当容器中元素填满Granularity大小，就需要新增空间来存储。每次新增空间，都是Granularity个单元，每一组单元之间，有两种连接模式，一种是Fix，一种是Seg。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">
<div id="ctob" style="margin-top: 0px; margin-bottom: 0px; text-align: left; ">
<div id="tmyy" style="margin-top: 0px; margin-bottom: 0px; text-align: left; ">
<div id="ok6m" style="margin-top: 0px; margin-bottom: 0px; text-align: left; "><img src="http://docs.google.com/File?id=ddwgxw9r_851gfxgq5p3_b" style="width: 648px; height: 482.806px; " alt="" /></div>
</div>
</div>
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">在Symbian OS Explained中有一幅经典图片，各种Array的存储模式，一目了然，盗窃过来，如上所示。从存储上来看，Flat方式就有如数组，一个挨一个存在一起，Ptr看上去和Var一致，指针一个挨一个存在一起，指向堆中对象，但从本质上来看，Ptr的实现与Flat的底层类似，而Var则是转为指针定制。Pak的存储有些怪异，每一坨元素有一个leading-byte的个数参数，让人不由怀疑，这玩意整个就是为描述符处心积虑准备的。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">而段与段之间，Fix的犹如vector，每次扩大存储容量，都需要进行内存的重新分配，适合用在定长的场景，而不是变长。而Seg，类似于List，段之间链表方式连接，如果Granularity的一个Seg对象，就彻底沦为了链表，毫无疑问，如果你需要不是的增加存储容量，Seg方式Array应该是你的最爱。。。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">在接口层面，CArray支持AppendL，InsertL，Delete等数据写入的接口，也支持At，operator [] 之类的数据读取接口，还支持Sort，Find，Compress等数据查找和处理的接口。具体实现和底层的存储相关联，具体细节没看过相关资料，只能无条件信任它的实现没有如此废柴。。。</div>
<h2 style="font-size: 14pt; ">RArray系列容器</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">RArray家族有两个成员，RArray和RPointerArray，从Symbian的命名风格很容易想到，前者存放普通栈对象，后者存放指针。RArray系列的实现，与CArray相比，清晰纯洁多了，它们其实就是基于X *或X **的C数组的封装，接近于STL的vector模式，只不过，和STL相比，它的模板行为还是有点伪而已。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">所有的Symbian书上，都会说当你需要接近vector这样的数组的时候，用RArray而不要使用CArrayFixFlat。原因是有很多的，本质上，CArray是被Symbian大而全思想脱了后腿，它的继承结构很深，每一层都要做一些额外的Check工作，降低了效率，而RArray基本就是为这个量身打造的，没有太多额外的开销，在Int，UInt等基本数据类型上，还不辞辛劳的做了模板特化，进一步提高了效率。因此，术业有专攻，性能强与CArrayFixFlat，也不是什么稀奇事情了。</div>
<h2 style="font-size: 14pt; ">其他容器</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">在Symbian中，还有一些容器，比如CDesCArray系列的Array，它们和CArray系列容器本是同根生，只不过换了个皮脸，针对描述符提供了额外的一些接口，可以更好的进行字符串比较等操作。而Symbian中，我并没有发现一些非线性的容器，比如map，比如hash，比如tree。如果有，那么就是我土鳖了，如果没有，其实也是我土鳖了，因为我实在琢磨不透Symbian怎么做这么个决定的。</div>
<h2 style="font-size: 14pt; ">Thin Templates</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">强行插播一些广告，关于Symbian的模板。众所周知，C++模板有两个特别优秀的特征：</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">
<ul style="margin-top: 0px; margin-bottom: 0px; ">
    <li style="margin-top: 0px; margin-bottom: 0px; ">高效。编译期决定类型，保证运行期的运行效率。</li>
    <li style="margin-top: 0px; margin-bottom: 0px; ">安全。编译期决定类型，保证运行期的安全。</li>
</ul>
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">但为了达到这样的效果，也是付出了成本的，那就是类型的膨胀，与运行期技术相比，程序体积增加了不少。作为一个手机操作系统，Symbian在模板的使用上，加了个pattern，企图鱼和熊掌兼得。具体来说，就是不利用模板高效的优点，仅利用模板安全的特征，用运行期的技术，减少程序体积，这层模板的皮，它们称之为Thin Templates。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">从实现上来看，标准的一个模板容器的实现，可能就是：</div>
<blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; ">template &lt;class T&gt;<br />
class Array&lt;T&gt;<br />
{<br />
public:<br />
&nbsp;&nbsp; &nbsp;T GetData(int index);<br />
<br />
private:<br />
&nbsp;&nbsp; &nbsp;T * data;<br />
};</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px; ">这样的话，如果有十个不同类型使用CArray，那么就会编译出十个不同的类，使得体积膨胀。于是Symbian的Thin Templates模式这么来做：</div>
<blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; ">template &lt;class T&gt;<br />
class Array&lt;T&gt; : private BaseArray<br />
{<br />
public:<br />
&nbsp;&nbsp; &nbsp;T GetData(int index);<br />
};<br />
<br />
class CBaseArray<br />
{<br />
protected:<br />
&nbsp;&nbsp; &nbsp;void * data;<br />
};</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px; ">表面上，还是一个模板类，但实际上，存储上并没有按照类型，而是统一转成了void *。用private继承，派生基类的存储，而摒弃基类的接口，重新构造基于模板参数的接口。这样一来，安全性保证了，很多错误在编译期就可以暴露出来，而由于底层实现没有用模板，节约了一部分体积开销，这种实现，类似于早期的Java模板，只是牺牲了性能优势，对于底层来说也许真的利大于弊，但对于自己的应用来说，就不一定了。因此，如果是自己做模板，考量一下应用场景，不需要拘泥所谓的Tiny Templates。。。</div>
<p>&nbsp;</p>
<p>============================</p>
<p>下面是真的插播广告*_*，Symbian上的，<strong><a href="http://m.youdao.com/help/zhushou?keyfrom=duguguiyu">有道购物助手</a></strong>， 现在已经发布。第一版功能还不算很丰富，有一些bug还在处理中。欢迎有S60 v3版的童鞋下载使用，努力拍砖。。。</p>
</span><img src="http://www.cnblogs.com/duguguiyu/aggbug/1502416.html?type=1" width="1" height="1" alt=""/><p>评论: 5　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/06/13/1502416.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/06/13/1502416.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Symbian手记【三】 —— Symbian的描述符</title><link>http://www.cnblogs.com/duguguiyu/archive/2009/05/29/1491634.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Fri, 29 May 2009 04:07:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2009/05/29/1491634.html</guid><description><![CDATA[<p>阅读: 1636 评论: 3 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2009-05-29 12:07 <a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/29/1491634.html" target="_blank">原文链接</a></p><span  style="font-family: Verdana; font-size: 13px; line-height: normal; ">
<h1 style="font-size: 18pt; ">【三】 Symbian的描述符</h1>
<div style="margin-top: 0px; margin-bottom: 0px; ">所谓描述符，一定程度上等同于字符串。只不过与C++的字符串不一样，Symbian中的描述符都是用一个附加的整数描述其长度，而不是以'\0'做终结符。因此，描述符可以表达任意数据，字符串或者二进制串。</div>
<h2 style="font-size: 14pt; ">描述符体系</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">打开任何一本关于Symbian介绍的书，都可以看到Symbian描述符那复杂的继承体系。它的基类是TDesC，顾名思义，T是代表它是T类，后缀C表示它是一个常量，其中数据无法修改。因此，它只是定义了一些字符处理的方法，包括查找、匹配、取子串等，而不包括任何修改其中数据的接口。可修改的描述符类，都是派生自TDes，它是TDesC的子类，额外提供了拷贝、清零、追加之类的接口。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">当你需要在栈上分配一个描述符，你可能需要用到TBuf或者TBufC类。它们都是模板类，接受一个int型参数作为长度信息。从名字可以一目了然的看出其中区别，带C的自然是常量，它一次性在栈中分配好所需的资源，并且同时完成赋值和初始化工作，一经分配，则不能再次修改。而不带C的TBuf，在构造时仅是在栈中预留好所需空间，此后可以在此空间范围内，任意的修改所需内容。从内存分布来看，TBufC对象在真实的字串信息前，还放了一个32位整数，它的前4bits存放类型信息，后28bits存放长度信息，也就是说一个TBufC对象最多包含256M长度的字符串，这已经绝对足够了。而TBuf对象，除了TBuf所包含的内容外，还额外加入了一个max-length的整数在长度信息后，它表示预分配了的内存长度，而length则用于表示真实有效的数据长度。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">除了栈，更多的描述符长度不是在编译期能够确定的，需要在堆上动态的进行分配，这项任务，就交由了HBufC来完成。HBufC也包含三项数据，前两项与TBuf一致，4bits类型 + 28bits真实长度 + 1整形的分配长度。但最后一项是一个指针，它指向堆中的某个位置，在这个位置，开辟了预分配长度的字符串空间。但HBufC的基类不和TBuf一致，而是于TBufC相同，这和它C的后缀表里如一，代表它只具有一些非数据修改性质的接口。这样的设计，一定会引发一系列的疑问，为什么明明又有max-length信息，又具有length信息，却是一个不可变的描述符对象？如果需要动态改变堆中描述符的内容，该使用什么样的类？</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">所有这些疑问，都可以通过TPtr这个类来解答。单纯的从内存数据来看，TPtr与HBufC完全一致，但从实际逻辑来看，HBufC中的指针，仅仅可能指向堆中的数据，而TPtr中的指针可以指向一个堆数据，也可以指向一个栈数据，这完全取决于你用什么对象来初始化它。如果用一个TBuf来初始化，那么就指向栈中，用HBufC来初始化，就指向了堆中，整个一墙头草。但不论是指向堆还是指向栈，TPtr对所指向的数据都仅拥有使用权，而不具有控制其生死的权利，该数据需要通过其原始的控制者，TBuf或者HBufC等来负责管理。很多时候，TPtr都是作为HBufC的一个帮手而存在，当你需要修改HBufC中的字符数据时，调用Des()接口，从HBufC华丽的转身为TPtr，TPtr没有C的后缀，这意味着它秉承了TDes的能力，可以修改其中的数据。我一直不理解Symbian为什么要设计一个HBufC类，而不是HBuf类，唯一可以想到的解释就是，由于TPtr的存在，可以解决修改堆描述符数据这件事情，而不需要再多实现一些接口，虽然有点牵强，但我还是一直用这解释自欺欺人。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">TPtr还有一个孪生兄弟TPtrC。和TBufC与TBuf的关系类似，TPtrC去掉了max-length这个域，分配长度即使用长度。TPtrC所有的设计逻辑，都与TPtr一致，指向堆或栈对象，只使用不管理资源，等等。它应用的最广泛的场合，就是用于表达子串。比如TBuf对象希望取出其中前10个字符给调用者使用，它就会返回一个TPtrC对象，它指向HBufC的字符位置，但仅具有10个长度，既可以控制长度，又可以保证其中数据不被修改，一举两得一石二鸟。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">到此为止，描述符的整个构架算是完整了，既有栈的，又有堆的；既有可修改的，有包含不可变的；既有表达整体的，又有表征局部的。但Symbian本着买一送一，挥泪大馈赠的态度，还提供了一个RBuf类。这是一个R类，并且没有C后缀。它通过Create接口在堆上分配数据，用Release或Close析构所掌握资源，从本质上来看，它就是HBufC的一个R版。但RBuf的基类是TDes，因此直接提供了更为丰富的数据修改接口，不需要转身成TPtr来处理。并且，RBuf屏蔽了字符串为NULL和为空的区别，有的时候，在使用HBufC需要不停的判定是否为NULL或者为空，而用RBuf则不需要，NULL即空，空即NULL。但RBuf的继承体系更深，并且可以想象，它的一些操作会再次封装一些额外的检测操作，可能效率上会有一丁点的降低（只是猜测，有兴趣可以做实验证实...）。从RBuf和HBufC的区别，你也可以从中推断出两者最适合的使用场景。HBufC其实最合适的就是应该本着其C的本质来做，适合于分配了不再修改的场合，比如从一个已有的描述符拷贝出新的描述符，此时返回的往往就是HBufC。而RBuf更适合反复修改的场合（不然白瞎了叫它一声Buf...），在这样的场景下，其接口使用起来更为的简单和明了。。。</div>
<h2 style="font-size: 14pt; ">编码</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">前面提到的所有描述符，其实都不是真实的类，而是一个typedef。在非内核模式的时候，所有的描述符，如TDesC，其真实的实现是TDesC16，在内核模式的时候，则是TDesC8。还是看名取义，带8的是单字符1个字节的描述符，带16的是宽字符2字节的描述符。在非内核态的时候，统一使用16位的描述符作为默认值，是为了兼容unicode编码，帮助在不同语言下进行开发。大部分的系统API，提供的都是接受TDesC这样typedef的接口，其实也就是unicode-16的16位描述符。但在一些io相关的接口，都是接受8结尾的单字符描述符，以兼容不同的数据格式。单字符描述符通常不会对编码有任何约束，可以是二进制流，可以是utf-8，可以是一般的ascii码。具体是什么，逻辑需要调用者自己来维护。为了将io读入的数据传递给一些系统API，往往就需要将8位描述符转成16位描述符。这种转换和编码有密切联系，如果只是一般的ascii串（或者其他编码的ascii部分...），可以使用TDes的Copy接口，从8位拷贝到16位，或者从16位拷贝到8位。从8位拷贝的16位，第一个字节填充对应的8位字符的内容，第二个字节填充的是'\0'，就是全部为0。而从16位到8位，可想而知，后一个字节的内容被截取抛弃。但如果是一些复杂的编码转换，比如utf-8的字符流转成系统所需的unicode-16，那么就需要用到CnvUtfConverter，它负责在不同的字符集中做转换。</div>
<h2 style="font-size: 14pt; ">其他字符类型类</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">看到Symbian的描述符，最疑惑的不仅仅是那套复杂的继承体系，还有_L和_LIT这样的宏。打开_LIT这个宏，你可以看到，它其实就是定义一个TLitC类型的const static常量字符串。从意义上来说，通过_LIT，可以将一些常用的字符串作为常量存在，使其不会反复构造和析构，是空间换时间的策略；从接口上看，它重载了很多转型运算符operator ()，可以转身成为TDesC的各个版本，与该继承体系兼容；而从内存实现上来看，它存放的是：C++字符串的长度（除\0） + 原汁原味的C++字符串（就是\0结尾的一坨short int或者char数组...），通过这样的结构，一方面可以和Symbian的描述符表示相一致，又可以享用C++的原始支持，一举两得。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">相比_LIT的华丽，_L这个宏就土鳖不少，代表了旧生产力的落后。它其实，就是定义一个TPtrC，TPtrC中的内容指向了一个常量的数组（typedef后叫做TText...），这个常量数组，其实就是有一个char或short int的数组转型而成，也是原汁原味的C++字符串。从本质上来看，TPtrC扮演这个角色，完全是在人手短缺时的友情客串，它本不应该来做这件事情，因为其内部是通过一个指针来指向真实的字符空间，很多操作都经过多一次的取址操作，降低了效率；而TLexC，则是量身打造精心包装天生大明星，它优化掉了那个作梗的指针，提高了效率。所以摒弃_L，拥抱_LIT，是所有Symbian教学都会呼吁的内容，还是合情合理的。。。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">还有一个常用的和字符串相关联的东西，就是TLex类。它做的工作，就是大名鼎鼎的string-parsing。给它一个描述符，它可以还你一个整形数抑或是浮点数。TLex对数的解析，本质上还是基于ascii编码的，你给它的描述符编码，需要兼容ascii标准，不要拿个全角的数字为难它，它会罢工的。。。</div>
</span><img src="http://www.cnblogs.com/duguguiyu/aggbug/1491634.html?type=1" width="1" height="1" alt=""/><p>评论: 3　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/29/1491634.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/29/1491634.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Symbian手记【二】 —— Symbian对象构造</title><link>http://www.cnblogs.com/duguguiyu/archive/2009/05/28/1491213.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Wed, 27 May 2009 18:26:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2009/05/28/1491213.html</guid><description><![CDATA[<p>阅读: 2526 评论: 5 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2009-05-28 02:26 <a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/28/1491213.html" target="_blank">原文链接</a></p><span  style="font-family: Verdana; font-size: 13px; line-height: normal; ">
<h1 style="font-size: 18pt; ">【二】 Symbian对象构造</h1>
<div style="margin-top: 0px; margin-bottom: 0px; ">C++的纯手工内存管理，确实是一个万恶之源。在对象构造时，有一个著名的内存泄漏隐患问题。比如一个类如下：</div>
<blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; ">class A<br />
{<br />
<div style="margin-top: 0px; margin-bottom: 0px; ">public:</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;A()<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;{</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;a1 = new T1();<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;a2 = new T2();<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;...<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;an = new Tn();<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;}<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; "><br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">private:</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;T1 * a1;<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;T2 * a2;<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;...<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">&nbsp;&nbsp; &nbsp;Tn * an;<br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">}</div>
</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px; ">当你调用 new A() 进行分配的时候，一旦失败，可能导致内存的泄露。比如系统正吭哧吭哧分配到了a18，失败了，抛出异常了，或者返回空值了，前面a1 - a17个对象，就彻底成了没娘管的娃，一并泄漏了出去。一个解决策略是，管好每一个分配过的对象，一旦有问题，清空一切。比如 a18分配失败了，delete掉a1 - a17。且不说这么做有没有其他问题，但是这份苦力，估计就没多少人能够承受。</div>
<h2 style="font-size: 14pt; ">二阶段构造</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">为了解决对象分配的问题，Symbian琢磨了所谓的二阶段构造法，它是一个pattern，关键在于将对象中栈数据的初始化和堆对象的分配过程隔离开来。一个标准的二阶段构造类如下：</div>
<blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; ">class A<br />
{<br />
public:<br />
&nbsp;&nbsp; &nbsp;~A();<br />
&nbsp;&nbsp; &nbsp;static A * NewL();<br />
&nbsp;&nbsp; &nbsp;static A * NewLC();<br />
<br />
private:<br />
&nbsp;&nbsp; &nbsp;A();<br />
&nbsp;&nbsp; &nbsp;void ConstructL();<br />
}</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px; ">其中内容，自动构造的每个Symbian C++类中都会有。在构造函数中，只能够执行赋值等操作，就是初始化栈中内容，整个操作不会涉及到堆中对象的分配。所有需要分配的堆中对象，推迟到ConstructL函数中进行。NewL和NewLC提供一个封装，将构造函数和二阶段构造函数封装一起。当然，仅通过这样的方式，无法解决内存泄漏的问题，一个核心机制，是清理栈，即CleanupStack。</div>
<h2 style="font-size: 14pt; ">清理栈</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">CleanupStack是单件的形式呈现在程序中，GUI的程序系统为你构造好了，Console的需要人肉一个。当你在一个函数中，new了一个对象，你需要先把它push到CleanupStack中，才能调用其带L的方法，并在调用完成后将它pop出CleanupStack。一旦L函数执行失败，Leave了，并在上层用TRAP宏抓到这个Leave错误，系统会自动释放存放在CleanupStack中，还没来得及pop的对象，以保证所有资源都不会泄漏。要做到这点，有两个需要解决的问题，一是如何不在人肉delete的情形下自动析构，第二个是如何知道析构栈中多少个对象。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">解决第一个问题，关键就是利用栈对象的析构函数，每个push到CleanupStack中的对象，都被一个栈对象TCleanupItem封装了一下，作为一个成员变量TAny* iPtr存放起来。当这个栈对象被释放，会调用其析构函数，析构函数中包含delete iPtr的调用，如此，自动析构得以完成。当然，为了保持其通用性，TCleanupItem其实不是直接delete，而是通过一个TCleanupOperation的对象来实现的，这个对象负责在其析构函数中delete iPtr，当然，除了delete，不同的TCleanupOperation还可以是iPtr-&gt;close，iPtr-&gt;release之类的，这样可以将其机制轻松的扩展开来。</div>
<blockquote style="padding-top: 10px; padding-right: 10px; padding-bottom: 10px; padding-left: 10px; border-top-width: 1px; border-right-width: 1px; border-bottom-width: 1px; border-left-width: 1px; border-top-color: #dddddd; border-right-color: #dddddd; border-bottom-color: #dddddd; border-left-color: #dddddd; border-top-style: dashed; border-right-style: dashed; border-bottom-style: dashed; border-left-style: dashed; ">#define TRAP(_r, _s)	 \
<div style="margin-top: 0px; margin-bottom: 0px; ">{	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">TInt&amp; __rref = _r;	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">__rref = 0;	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">{ TRAP_INSTRUMENTATION_START; }	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">try	{	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">__WIN32SEHTRAP	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">TTrapHandler* ____t = User::MarkCleanupStack();	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">_s;	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">User::UnMarkCleanupStack(____t);	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">{ TRAP_INSTRUMENTATION_NOLEAVE; }	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">__WIN32SEHUNTRAP	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">}	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">catch (XLeaveException&amp; l)	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">{	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">__rref = l.GetReason();	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">{ TRAP_INSTRUMENTATION_LEAVE(__rref); }	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">}	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">catch (...)	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">{	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">User::Invariant();	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">}	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">{ TRAP_INSTRUMENTATION_END; }	 \</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">}</div>
</blockquote>
<div style="margin-top: 0px; margin-bottom: 0px; ">另一个问题解决之道，就是记录一个level，在函数执行前放入一个标记，一旦有错误，就消除在此标记后push进来的对象。这个机制的维系，隐藏在TRAP宏中。当你写TRAP(err, DoitL())时，TRAP会在调用DoitL()前，调用User::MarkCleanupStack()加入一个标记，并在调用结束后利用User::UnMarkCleanupStack检查并且消除该标记。放一个标记在这里，一旦你多push了少pop了，或者少push了多Pop了，都会触发异常，谨防顺手写错。而在执行函数DoitL()过程中，一旦发生Leave错误，在User::Leave()之类的函数中，都会找到最后标记的位置，清除标记后push的所有对象。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">由于栈和函数调用都属于先进先出的，整个机制是可以嵌套进行的。只要你TRAP了Leave错误，所有资源都会被保证析构（如果没有TRAP，天皇老子都帮不了你...）。这种半自动半人肉的内存管理方式，虽然不能帮助复杂的内存对象生命周期的维护，但至少可以保证每一个资源在异常时正常释放，这一点在嵌入式系统中比一般系统显得更为重要（因为内存紧张，分配不成功是常态...）。但人肉方式总归是要人来解决的，不论CleanupStack多么的好，它只是一个pattern，它不能自动去做一些事情，还是需要开发人员主动的push，pop，leave，以及TRAD，少了哪一样，整个机制全部白搭。</div>
<h2 style="font-size: 14pt; ">Symbian的异常处理</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">Symbian的异常处理，就是著名的Leave机制，如果你打开TRAP宏，便惊奇的发现，所谓Leave，只不过老瓶装新酒，它只是给C++的异常机制，穿了个丁字裤，还是超节约布料型的。你可以将所谓的TRAD看成是catch，将Leave看成throw，将带L的函数，看成是throw exception的函数，再将err code当作是异常类型，整个Leave机制，就和C++的异常匹配上了。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">当然，之所以称为老瓶装新酒，那么就有一些可以称为新的琐碎事。首先，就是对CleanupStack的维系。在TRAP宏和User::Leave中，包含了对CleanupStack的标记的管理和资源清理，没有它们，CleanupStack这套东东，就该另辟蹊径了。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">而另一方面，就是对标准异常和无法估量的异常进行了分门别类的处理。C++和.Net不一样，异常都是不同根的，我们往往需要用catch(...)去处理一些杂类的状况。在TRAD中，对所以Symbian中的异常进行了分类。一类是派生自XLeaveException的异常，它们是整个Symbian的Cleanup以及Leave的管辖范围，只有在触发此类异常的时候，所谓的自动释放内存、Leave才能发挥作用；而其他所有的异常，都被归类异类，一旦发生，直接User::Invariant()来安乐死。所以，你明明是TRAP了，在读到空指针等错误发生的时候，它完全不起作用，程序直接崩溃，因为，这超出了它的能力范畴。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">除此之外，Symbian开始支持标准的C++异常了，但对于一个合格的Symbian开发者而言，了解这些，还是有益无害的。。。</div>
</span><img src="http://www.cnblogs.com/duguguiyu/aggbug/1491213.html?type=1" width="1" height="1" alt=""/><p>评论: 5　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/28/1491213.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/28/1491213.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>Symbian手记【一】 —— Symbian命名法</title><link>http://www.cnblogs.com/duguguiyu/archive/2009/05/25/1489234.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Mon, 25 May 2009 15:56:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2009/05/25/1489234.html</guid><description><![CDATA[<p>阅读: 2121 评论: 11 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2009-05-25 23:56 <a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/25/1489234.html" target="_blank">原文链接</a></p><span  style="font-family: Verdana; font-size: 13px; line-height: normal; ">
<h1 style="font-size: 18pt; ">【一】 Symbian命名法</h1>
<div style="margin-top: 0px; margin-bottom: 0px; ">每个美感尚存的C++ coder，第一次看到Symbian C++的程序，第一反应是：这鬼代码怎么缩进的？接下来，所有人会有疑问应该是：函数和类上的乱七八糟的前后缀是啥意思？</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">娃再丑也是爸妈生的，生成这模样虽然很无奈，但确实也是事出有因。在我看来，Symbian命名法的核心出发点，就是为了更好的内存资源管理。C++的人肉内存管理模式，在给人以控制到字节的快感的同时，也带了了麻烦到每行代码的烦恼。命名法，就是Symbian设计者憋出来用来辅助管理内存资源的方式之一。</div>
<h2 style="font-size: 14pt; ">类命名</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">Symbian的类，通常都带着一个字母的前缀，比如C、M、T、R、H等等。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">所有从CBase派生而来的子类，都以C开头，形如Cxxxx。每个正确设计的，非抽象（不可实例化）的C类，都只能在堆上分配。为了保证这一点，每一个可实例化的C类，都应该按照Symbian的二阶段构造模式。但当然，这可以有意外。比如一些派生自CCoeControl的控件对象类，会需要从Resource文件中构造类的成员对象（而不仅仅通过二阶段中的ConstructL方式来构造），这使得它可能不适合按照二阶段构造的方式来封装。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">做过.Net或者Java的人应该都明白，保持一个单一根的类型系统有什么好处，.Net在没有泛型的日子里，就是通过这个共根来实现一些基础的容器和方法。但这个好处，在C++，尤其是Symbian C++中体现的并不明显。因为C++有void *（在Symbian中华丽的转身为TAny *），有模板，可以来做一些类似的事情。更重要的，在Symbian C++中，为了节约空间，把虚表的RTTI项给精简掉了，使得Symbian C++的类丧失了dynamic_cast的能力，从而导致整个Symbian在运行期的动态识别能力，很是孱弱。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">所以说，之所以要从CBase类进行派生堆上对象，很重要的一个原因，就是为了内存管理。CBase做了一件很重要的事情，就是将拷贝构造函数和赋值函数设置成了私有。这意味着，所有从CBase派生的子类，都默认被阉割了一刀，失去了拷贝构造的能力。这是为了提醒所有使用者，C对象的浅拷贝是不受欢迎的，如果你想提供该对象的拷贝功能（要深拷贝，不要浅...），往往是利用一些CloneL之类的接口来实现，保证行为的统一性。</div>
<div style="margin-top: 0px; margin-bottom: 0px; "><br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">C的类们，都涌向了堆中，栈上的活，留给了T类来完成。T类没有什么特殊的继承结构，每个T类，需要可以随意的在堆上或者栈上分配。大部分时候，它们该待的地方是栈，在栈上分配，并可以快速拷贝，一旦被析构，所以资源被释放，生不带来死不带走不留下一点残渣。因此，它们不应该包含大块的数据对象，但却可以拥有很复杂的接口，增加操作的便利性。比如，TRgb、TRect之类的系统类，就是典型的人小鬼大的代表人物。但T类不是C++的old plain类，它可以有继承结构，比如Symbian中描述符的那一堆堆T类，就拥有复杂的继承结构。</div>
<div style="margin-top: 0px; margin-bottom: 0px; "><br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">但世界是残酷的，有的类，偏偏就是投错了胎，搞得人不人，鬼不鬼。HBuf，就是此中代表。为了保持队形，维系接口，HBuf派生自TDesC，用以表示分配在堆上的Symbian描述符（就是字符串...）。但与一般T类不同，因为其占用空间动态变化，它必须在堆上分配，所以丧失了叫T的权利；另一方面，为了接口，它派生了T类，在排斥多根的C++中，它就不能够在从CBase派生了（继承的局限性，可见一斑），被断了叫C的后路。于是，就带上了H的特殊帽子，表示其在堆上分配，但不苟且于C类的屋檐下。</div>
<div style="margin-top: 0px; margin-bottom: 0px; "><br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">R类，换成通俗的描述，就是句柄类。它天生为了管理资源而存活，R类本身很简单，通常在栈上分配，可以拷贝，在这一点非常接近于T类。但与T类不同的时，R类往往带有某个堆对象的指针，指向文件之类的资源，或者是大块的堆数据对象。它析构的时候，默认是不析构这个指向的对象的，而是提供了一些类似于Close，Release之类的接口，需要人肉手动释放。有的T类也是指向另一块堆或者栈区域的，比如TPtr类。这两者一个本质的区别在于，T类指向的对象，不是它自己分配，它只是提供一个快捷方式，并不管指向对象的死活；而R类指向的资源，往往是自己本身或者另一个同类分配的资源，R类对象指向的资源，必须从这个R类的对象构造，从这个R类的对象析构（两个对象可以不同，但类是一致的）。</div>
<div style="margin-top: 0px; margin-bottom: 0px; "><br />
</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">在Symbian C++中，还有一些类，不涉及任何内存资源。一个就是接口类，它们以M开头，相当于.Net的Interface，是一个纯虚类。每个Symbian中的类，可以派生自若干个M类，但仅仅能从一个有内存资源的对象那里进行分配。理论上，作为一个纯虚类，应该提供一个虚的析构函数，但在Symbian C++中，这往往是不需要的。因为在一个没有RTTI的世界里，只有第一个被继承的接口才有可能成功析构所有对象。比如一个类，形如 class A : public Cxxx, public Mxxx。只有用Cxxx接口才能管理资源，对Mxxx接口进行delete，完全没有办法释放全部资源（除非Cxxx里面没有任何数据...）。而Symbian的堆对象往往派生自CBase，所以，不可能从一个M类来析构对象，这个析构函数成不成虚，就是无关紧要的事情了。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">另外一个不含任何资源的类，就是静态类了，在Symbian C++中它们没有任何前缀，是唯一不戴帽子的家伙。这个和.Net的static class一样，只包含一堆的静态方法，需要屏蔽所有构造、析构、拷贝接口（要没有这个闲工夫，不屏蔽也无所谓了...）。虽然，C++有函数，但出于对面向对象的热衷，使用这样的静态类，还是很值得鼓励的。。。</div>
<h2 style="font-size: 14pt; ">函数命名</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">在Symbian中，类是戴帽子的，函数则是穿裤子的。在Symbian的函数（包括成员和非成员函数）中，常有两种后缀，一个是L，另一个是LC。L，就是告诉你，这个函数可能Leave，换人类可知的语言描述，就是这个函数会抛出异常，需要谨慎处理。L是有传递性的，如果在调用该函数的地方对此L不理不睬放任其Leave，那么，在此调用函数后面，也需要添加一个L。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">除了L，还有跟进一步的LC。这通常都是构造性的函数，它告诉你，它构造的过程中，不但可能Leave，并且分配的对象处于清理栈中。这是一个接近于语法糖的功能，如果在本函数中的后续部分需要调用被构造对象的相关接口，应该用LC，然后自己pop，而不是L。</div>
<h2 style="font-size: 14pt; ">其他命名法</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">还有一些对象，是会被带着前缀的。比如对象的成员变量，都带着前缀i；函数参数，都带着a（如果后面是原因字母开头，则需要用an，*_*）。在成员变量加前缀，这是常用的手段，可以和成员变量区分开了，帮助节约命名一个变量的脑细胞。但对函数形参加前缀，就是一件很诡异的事情了，剖有画蛇添足的艺术气息。</div>
<div style="margin-top: 0px; margin-bottom: 0px; ">在Symbian中，所有的常量，都应该是K开头的，包括定义的const量，_LIT定义的字符常量等等。而枚举类型，同属于T类型，以T开头，其中的枚举值，则是以E开头。给这些类型的东西建立命名法，是常见的手段，只是Symbian不走寻常路，命名方式上不屑于与别人苟同。。。</div>
<h2 style="font-size: 14pt; ">结语</h2>
<div style="margin-top: 0px; margin-bottom: 0px; ">简而言之，Symbian制定了一套复杂的命名法规则，期待以此来规范化内存管理等操作。但世界的残酷在于，一个没有强制的标准，是不可靠的。命名法是一种弱约束的东西，工期赶的再急，也不可能无视编译和运行时的错误，但却可以无条件的忽视命名规则。并且，命名法是有强烈的破窗效应，一旦某一个函数没有合理的添加L，所有直接和间接调用它的函数，都可能会错误使用它，从而埋下隐患。况且，Symbian的命名法也算是枝繁叶茂了，很容易让人看不清楚端倪，不知不觉的就用错了，一个团队每个人在这上面犯一些错误，到最后命名法就完全丧失了效能。不过，就算是环境恶劣，对于个人而言，还是应该严于律己的，不论如何，不要轻易抛弃正确命名，这样，才可能造福大家。。。</div>
</span><img src="http://www.cnblogs.com/duguguiyu/aggbug/1489234.html?type=1" width="1" height="1" alt=""/><p>评论: 11　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/25/1489234.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/05/25/1489234.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item><item><title>分布式基础学习【二】 —— 分布式计算系统（Map/Reduce）</title><link>http://www.cnblogs.com/duguguiyu/archive/2009/02/28/1400278.html</link><dc:creator>duguguiyu</dc:creator><author>duguguiyu</author><pubDate>Sat, 28 Feb 2009 06:41:00 GMT</pubDate><guid>http://www.cnblogs.com/duguguiyu/archive/2009/02/28/1400278.html</guid><description><![CDATA[<p>阅读: 3888 评论: 7 作者: <a href="http://www.cnblogs.com/duguguiyu/" target="_blank">duguguiyu</a> 发表于 2009-02-28 14:41 <a href="http://www.cnblogs.com/duguguiyu/archive/2009/02/28/1400278.html" target="_blank">原文链接</a></p><h2>二. 分布式计算（Map/Reduce）<br />
</h2>
<div>分布式式计算，同样是一个宽泛的概念，在这里，它狭义的指代，按Google
Map/Reduce框架所设计的分布式框架。在Hadoop中，分布式文件系统，很大程度上，是为各种分布式计算需求所服务的。我们说分布式文件系统就是加了分布式的文件系统，类似的定义推广到分布式计算上，我们可以将其视为<strong>增加了分布式支持的计算函数</strong>。从计算的角度上看，Map/Reduce框架接受各种格式的键值对文件作为输入，读取计算后，最终生成自定义格式的输出文件。而从分布式的角度上看，分布式计算的输入文件往往规模巨大，且分布在多个机器上，单机计算完全不可支撑且效率低下，因此Map/Reduce框架需要提供一套机制，将此计算扩展到无限规模的机器集群上进行。依照这样的定义，我们对整个Map/Reduce的理解，也可以分别沿着这两个流程去看。。。</div>
<div>在Map/Reduce框架中，每一次计算请求，被称为<strong>作业</strong>。在分布式计算Map/Reduce框架中，为了完成这个作业，它进行两步走的战略，首先是将其拆分成若干个<strong>Map任务</strong>，分配到不同的机器上去执行，每一个Map任务拿输入文件的一部分作为自己的输入，经过一些计算，生成某种格式的中间文件，这种格式，与最终所需的文件格式完全一致，但是仅仅包含一部分数据。因此，等到所有Map任务完成后，它会进入下一个步骤，用以合并这些中间文件获得最后的输出文件。此时，系统会生成若干个<strong>Reduce任务</strong>，同样也是分配到不同的机器去执行，它的目标，就是将若干个Map任务生成的中间文件为汇总到最后的输出文件中去。当然，这个汇总不总会像1
+ 1 =
2那么直接了当，这也就是Reduce任务的价值所在。经过如上步骤，最终，作业完成，所需的目标文件生成。整个算法的关键，就在于增加了一个中间文件生成的流程，大大提高了灵活性，使其分布式扩展性得到了保证。。。</div>
<h3>I. 术语对照<br />
</h3>
<div>和分布式文件系统一样，Google、Hadoop和....我，各执一种方式表述统一概念，为了保证其统一性，特有下表。。。</div>
<div><br />
</div>
<div>
<div>
<table id="wc5x" class="" border="1" cellspacing="0" bordercolor="#000000" cellpadding="3" width="100%">
    <tr>
        <td style="text-align: center; " width="25%"><strong>文中翻译</strong></td>
        <td style="text-align: center; " width="25%"><strong>Hadoop术语</strong></td>
        <td style="text-align: center; " width="25%"><strong>Google术语</strong></td>
        <td style="text-align: center; " width="25%"><strong>相关解释</strong></td>
    </tr>
    <tr>
        <td width="25%">作业</td>
        <td width="25%">Job</td>
        <td width="25%">Job</td>
        <td width="25%">用户的每一个计算请求，就称为一个作业。</td>
    </tr>
    <tr>
        <td width="25%">作业服务器</td>
        <td width="25%">JobTracker</td>
        <td width="25%">Master</td>
        <td width="25%">用户提交作业的服务器，同时，它还负责各个作业任务的分配，管理所有的任务服务器。</td>
    </tr>
    <tr>
        <td width="25%">任务服务器</td>
        <td width="25%">TaskTracker</td>
        <td width="25%">Worker</td>
        <td width="25%">任劳任怨的工蜂，负责执行具体的任务。</td>
    </tr>
    <tr>
        <td width="25%">任务</td>
        <td width="25%">Task</td>
        <td width="25%">Task</td>
        <td width="25%">每一个作业，都需要拆分开了，交由多个服务器来完成，拆分出来的执行单位，就称为任务。</td>
    </tr>
    <tr>
        <td width="25%">备份任务</td>
        <td width="25%">Speculative Task</td>
        <td width="25%">Buckup Task</td>
        <td width="25%">每一个任务，都有可能执行失败或者缓慢，为了降低为此付出的代价，系统会未雨绸缪的实现在另外的任务服务器上执行同样一个任务，这就是备份任务。</td>
    </tr>
</table>
</div>
</div>
<h3>II. 基本架构<br />
</h3>
<div>与分布式文件系统类似，Map/Reduce的集群，也由三类服务器构成。其中<strong>作业服务器</strong>，在Hadoop中称为<strong>Job
Tracker</strong>，在Google论文中称为<strong>Master</strong>。前者告诉我们，作业服务器是负责管理运行在此框架下所有作业的，后者告诉我们，它也是为各个作业分配任务的核心。与HDFS的主控服务器类似，它也是作为单点存在的，简化了负责的同步流程。具体的<strong>负责执行用户定义操作</strong>的，是<strong>任务服务器</strong>，每一个作业被拆分成很多的<strong>任务</strong>，包括<strong>Map任务</strong>和<strong>Reduce任务</strong>等，任务是具体执行的基本单元，它们都需要分配到合适任务服务器上去执行，任务服务器一边执行一边向作业服务器汇报各个任务的状态，以此来帮助作业服务器了解作业执行的整体情况，分配新的任务等等。。。</div>
<div>除了作业的管理者执行者，还需要有一个<strong>任务的提交者</strong>，这就是客户端。与分布式文件系统一样，客户端也不是一个单独的进程，而是一组API，用户需要自定义好自己需要的内容，经由客户端相关的代码，将作业及其相关内容和配置，提交到作业服务器去，并时刻监控执行的状况。。。</div>
<div>同作为Hadoop的实现，与HDFS的通信机制相同，Hadoop
Map/Reduce也是用了协议接口来进行服务器间的交流。实现者作为RPC服务器，调用者经由RPC的代理进行调用，如此，完成大部分的通信，具体服务器的架构，和其中运行的各个协议状况，参见下图。从图中可以看到，与HDFS相比，相关的协议少了几个，客户端与任务服务器，任务服务器之间，都不再有直接通信关系。这并不意味着客户端就不需要了解具体任务的执行状况，也不意味着，任务服务器之间不需要了解别家任务执行的情形，只不过，由于整个集群各机器的联系比HDFS复杂的多，直接通信过于的难以维系，所以，都统一由作业服务器整理转发。另外，从这幅图可以看到，任务服务器不是一个人在战斗，它会像孙悟空一样招出一群宝宝帮助其具体执行任务。这样做的好处，个人觉得，应该有安全性方面的考虑，毕竟，任务的代码是用户提交的，数据也是用户指定的，这质量自然良莠不齐，万一碰上个搞破坏的，把整个任务服务器进程搞死了，就因小失大了。因此，放在单独的地盘进行，爱咋咋地，也算是权责明确了。。。</div>
<div>
<div style="padding-right: 0px; padding-left: 0px; padding-bottom: 1em; padding-top: 1em; text-align: center" id="bzsx"><img style="width: 599px; height: 507px" src="http://docs.google.com/File?id=ddwgxw9r_805f72bj3gj_b"  alt="" /></div>
</div>
<div>与分布式文件系统相比，Map/Reduce框架的还有一个特点，就是<strong>可定制性强</strong>。文件系统中很多的算法，都是很固定和直观的，不会由于所存储的内容不同而有太多的变化。而作为通用的计算框架，需要面对的问题则要复杂很多，在各种不同的问题、不同的输入、不同的需求之间，很难有一种包治百病的药能够一招鲜吃遍天。作为Map/Reduce框架而言，一方面要尽可能的抽取出公共的一些需求，实现出来。更重要的，是需要提供良好的可扩展机制，满足用户自定义各种算法的需求。Hadoop是由Java来实现的，因此通过反射来实现自定义的扩展，显得比较小菜一碟了。在<strong>JobConf</strong>类中，定义了大量的接口，这基本上是Hadoop
Map/Reduce框架所有可定制内容的一次集中展示。在JobConf中，有大量set接口接受一个<strong>Class&lt;? extends
xxx></strong>的参数，通常它都有一个默认实现的类，用户如果不满意，则可自定义实现。。。</div>
<h3>III. 计算流程<br />
</h3>
<div>如果一切都按部就班的进行，那么整个作业的计算流程，应该是作业的提交 -> Map任务的分配和执行 -> Reduce任务的分配和执行
-> 作业的完成。而在每个任务的执行中，又包含输入的准备 -> 算法的执行 ->
输出的生成，三个子步骤。沿着这个流程，我们可以很快的整理清晰整个Map/Reduce框架下作业的执行。。。</div>
<h4>1、作业的提交<br />
</h4>
<div>一个作业，在提交之前，需要把所有应该配置的东西都配置好，因为一旦提交到了作业服务器上，就陷入了完全自动化的流程，用户除了观望，最多也就能起一个监督作用，惩治一些不好好工作的任务。。。</div>
<div>基本上，用户在提交代码阶段，需要做的工作主要是这样的：</div>
<div>首先，书写好所有自定的代码，最起码，需要有Map和Reduce的执行代码。在Hadoop中，Map需要派生自<strong>Mapper&lt;K1, V1,
K2, V2></strong>接口，Reduce需要派生自<strong>Reducer&lt;K2, V2, K3,
V3></strong>接口。这里都是用的泛型，用以支持不同的键值类型。这两个接口都仅有一个方法，一个是map，一个是reduce，这两个方法都直接受四个参数，前两个是输入的<strong>键</strong>和<strong>值</strong>相关的数据结构，第三个是作为<strong>输出</strong>相关的数据结构，最后一个，是一个<strong>Reporter</strong>类的实例，实现的时候可以利用它来统计一些计数。除了这两个接口，还有大量可以派生的接口，比如分割的<strong>Partitioner&lt;K2,
V2></strong>接口。。。</div>
<div>然后，需要书写好主函数的代码，其中最主要的内容就是实例化一个<strong>JobConf</strong>类的对象，然后调用其丰富的setXXX接口，设定好所需的内容，包括输入输出的文件路径，Map和Reduce的类，甚至包括读取写入文件所需的格式支持类，等等。。。</div>
<div>最后，调用<strong>JobClient</strong>的<strong>runJob</strong>方法，提交此JobConf对象。runJob方法会先行调用到<strong>JobSubmissionProtocol</strong>接口所定义的<strong>submitJob</strong>方法，将此作业，提交给作业服务器。接着，runJob开始循环，不停的调用JobSubmissionProtocol的<strong>getTaskCompletionEvents</strong>方法，获得<strong>TaskCompletionEvent</strong>类的对象实例，了解此作业各任务的执行状况。。。</div>
<h4>2、Map任务的分配</h4>
<div>当一个作业提交到了作业服务器上，作业服务器会生成若干个Map任务，每一个Map任务，负责将一部分的输入转换成格式与最终格式相同的中间文件。通常一个<strong>作业的输入都是基于分布式文件系统的文件</strong>（当然在单机环境下，文件系统单机的也可以...），因为，它可以很天然的和分布式的计算产生联系。而对于一个Map任务而言，它的输入往往是输入文件的一个数据块，或者是数据块的一部分，但通常，<strong>不跨数据块</strong>。因为，一旦跨了数据块，就可能涉及到多个服务器，带来了不必要的复杂性。。。</div>
<div>当一个作业，从客户端提交到了作业服务器上，作业服务器会生成一个<strong>JobInProgress</strong>对象，作为与之对应的标识，用于管理。作业被拆分成若干个Map任务后，会预先挂在作业服务器上的任务服务器拓扑树。这是依照分布式文件数据块的位置来划分的，比如一个Map任务需要用某个数据块，这个数据块有三份备份，那么，在这三台服务器上都会挂上此任务，可以视为是一个预分配。。。</div>
<div>关于任务管理和分配的大部分的真实功能和逻辑的实现，JobInProgress则依托<strong>JobInProgressListener</strong>和<strong>TaskScheduler</strong>的子类。TaskScheduler，顾名思义是用于任务分配的策略类（为了简化描述，用它代指所有TaskScheduler的子类...）。它会掌握好所有作业的任务信息，其<strong>assignTasks</strong>函数，接受一个<strong>TaskTrackerStatus</strong>作为参数，依照此任务服务器的状态和现有的任务状况，为其分配新的任务。而为了掌握所有作业相关任务的状况，TaskScheduler会将若干个JobInProgressListener注册到<strong>JobTracker</strong>中去，当有新的作业到达、移除或更新的时候，JobTracker会告知给所有的JobInProgressListener，以便它们做出相应的处理。。。</div>
<div>任务分配是一个重要的环节，所谓任务分配，就是<strong>将合适作业的合适任务分配到合适的服务器上</strong>。不难看出，里面蕴含了两个步骤，先是选择作业，然后是在此作业中选择任务。和所有分配工作一样，任务分配也是一个复杂的活。不良好的任务分配，可能会导致网络流量增加、某些任务服务器负载过重效率下降，等等。不仅如此，任务分配还是一个无一致模式的问题，不同的业务背景，可能需要不同的算法才能满足需求。因此，在Hadoop中，有很多TaskScheduler的子类，像Facebook，Yahoo，都为其贡献出了自家用的算法。在Hadoop中，默认的任务分配器，是<strong>JobQueueTaskScheduler</strong>类。它选择作业的基本次序是：Map
Clean Up Task（Map任务服务器的清理任务，用于清理相关的过期的文件和环境...） -> Map Setup
Task（Map任务服务器的安装任务，负责配置好相关的环境...） -> Map Tasks -> Reduce Clean Up Task
-> Reduce Setup Task -> Reduce
Tasks。在这个前提下，具体到Map任务的分配上来。当一个任务服务器工作的游刃有余，期待获得新的任务的时候，JobQueueTaskScheduler会按照各个作业的优先级，从<strong>最高优先级的作业</strong>开始分配。每分配一个，还会为其留出余量，已被不时之需。举一个例子：系统目前有优先级3、2、1的三个作业，每个作业都有一个可分配的Map任务，一个任务服务器来申请新的任务，它还有能力承载3个任务的执行，JobQueueTaskScheduler会先从优先级3的作业上取一个任务分配给它，然后再留出一个1任务的余量。此时，系统只能在将优先级2作业的任务分配给此服务器，而不能分配优先级1的任务。这样的策略，基本思路就是<strong>一切为高优先级的作业服务</strong>，优先分配不说，分配了好保留有余力以备不时之需，如此优待，足以让高优先级的作业喜极而泣，让低优先级的作业感慨既生瑜何生亮，甚至是活活饿死。。。</div>
<div>确定了从哪个作业提取任务后，具体的分配算法，经过一系列的调用，最后实际是由<strong>JobInProgress</strong>的<strong>findNewMapTask</strong>函数完成的。它的算法很简单，就是<strong>尽全力为此服务器非配且尽可能好的分配任务</strong>，也就是说，只要还有可分配的任务，就一定会分给它，而不考虑后来者。作业服务器会从离它最近的服务器开始，看上面是否还挂着未分配的任务（预分配上的），从近到远，如果所有的任务都分配了，那么看有没有开启多次执行，如果开启，考虑把未完成的任务再分配一次（后面有地方详述...）。。。</div>
<div>对于作业服务器来说，把一个任务分配出去了，并不意味着它就彻底解放，可以对此任务可以不管不顾了。因为任务可以在任务服务器上执行失败，可能执行缓慢，这都需要作业服务器帮助它们再来一次。因此在Task中，记录有一个<strong>TaskAttemptID</strong>，对于任务服务器而言，它们每次跑的，其实都只是一个Attempt而已，Reduce任务只需要采信一个的输出，其他都算白忙乎了。。。</div>
<h4>3、Map任务的执行</h4>
<div>与HDFS类似，任务服务器是通过心跳消息，向作业服务器汇报此时此刻其上各个任务执行的状况，并向作业服务器申请新的任务的。具体实现，是<strong>TaskTracker</strong>调用<strong>InterTrackerProtocol</strong>协议的<strong>heartbeat</strong>方法来做的。这个方法接受一个<strong>TaskTrackerStatus</strong>对象作为参数，它描述了此时此任务服务器的状态。当其有余力接受新的任务的时候，它还会传入<strong>acceptNewTasks</strong>为true的参数，表示希望作业服务器委以重任。<strong>JobTracker</strong>接收到相关的参数后，经过处理，会返回一个<strong>HeartbeatResponse</strong>对象。这个对象中，定义了一组TaskTrackerAction，用于指导任务服务器进行下一步的工作。系统中已定义的了一堆其TaskTrackerAction的子类，有的对携带的参数进行了扩充，有的只是标明了下ID，具体不详写了，一看便知。。。</div>
<div>当TaskTracker收到的TaskTrackerAction中，包含了<strong>LaunchTaskAction</strong>，它会开始执行所分配的新的任务。在TaskTracker中，有一个<strong>TaskTracker.TaskLauncher</strong>线程（确切的说是两个，一个等Map任务，一个等Reduce任务），它们在痴痴的守候着新任务的来到。一旦等到了，会最终调用到Task的<strong>createRunner</strong>方法，构造出一个<strong>TaskRunner</strong>对象，新建一个线程来执行。对于一个Map任务，它对应的Runner是TaskRunner的子类<strong>MapTaskRunner</strong>，不过，核心部分都在TaskRunner的实现内。TaskRunner会先将所需的文件全部下载并拆包好，并记录到一个全局缓存中，这是一个全局的目录，可以供所有此作业的所有任务使用。它会用一些软链接，将一些文件名链接到这个缓存中来。然后，根据不同的参数，配置出一个JVM执行的环境，这个环境与<strong>JvmEnv</strong>类的对象对应。</div>
<div style="margin-top: 0px; margin-bottom: 0px">接着，TaskRunner会调用<strong>JvmManager</strong>的<strong>launchJvm</strong>方法，提交给JvmManager处理。JvmManager用于管理该TaskTracker上所有运行的Task子进程。在目前的实现中，尝试的是池化的方式。有若干个固定的槽，如果槽没有满，那么就启动新的子进程，否则，就寻找idle的进程，如果是同Job的直接放进去，否则杀死这个进程，用一个新的进程代替。每一个进程都是由JvmRunner来管理的，它也是位于单独线程中的。但是从实现上看，这个机制好像没有部署开，子进程是死循环等待，而不会阻塞在父进程的相关线程上，父线程的变量一直都没有个调整，一旦分配，始终都处在繁忙的状况了。</div>
<div style="margin-top: 0px; margin-bottom: 0px">真实的执行载体，是Child，它包含一个main函数，进程执行，会将相关参数传进来，它会拆解这些参数，并且构造出相关的Task实例，调用其run函数进行执行。每一个子进程，可以执行指定个数量的Task，这就是上面所说的池化的配置。但是，这套机制在我看来，并没有运行起来，每个进程其实都没有机会不死而执行新的任务，只是傻傻的等待进程池满，而被一刀毙命。也许是我老眼昏花，没看出其中实现的端倪。。。</div>
<h4>4、Reduce任务的分配与执行</h4>
<div>比之Map任务，Reduce的分配及其简单，基本上是所有Map任务完成了，有空闲的任务服务器，来了就给分配一个Job任务。因为Map任务的结果星罗棋布，且变化多端，真要搞一个全局优化的算法，绝对是得不偿失。而Reduce任务的执行进程的构造和分配流程，与Map基本完全的一致，没有啥可说的了。。。</div>
<div>但其实，Reduce任务与Map任务的最大不同，是Map任务的文件都在本地隔着，而Reduce任务需要到处采集。这个流程是作业服务器经由此Reduce任务所处的任务服务器，告诉Reduce任务正在执行的进程，它需要的Map任务执行过的服务器地址，此Reduce任务服务器会于原Map任务服务器联系（当然本地就免了...），通过FTP服务，下载过来。这个隐含的直接数据联系，就是执行Reduce任务与执行Map任务最大的不同了。。。</div>
<h4>5、作业的完成</h4>
<div>当所有Reduce任务都完成了，所需数据都写到了分布式文件系统上，整个作业才正式完成了。此中，涉及到很多的类，很多的文件，很多的服务器，所以说起来很费劲，话说，一图解千语，说了那么多，我还是画两幅图，彻底表达一下吧。。。</div>
<div>首先，是一个时序图。它模拟了一个由3个Map任务和1个Reduce任务构成的作业执行流程。我们可以看到，在执行的过程中，只要有人太慢，或者失败，就会增加一次尝试，以此换取最快的执行总时间。一旦所有Map任务完成，Reduce开始运作（其实，不一定要这样的...），对于每一个Map任务来说，只有执行到Reduce任务把它上面的数据下载完成，才算成功，否则，都是失败，需要重新进行尝试。。。</div>
<div>
<div style="padding-right: 0px; padding-left: 0px; padding-bottom: 1em; padding-top: 1em; text-align: center" id="e3.3">
<div style="padding-right: 1em; padding-left: 1em; padding-bottom: 1em; padding-top: 1em; text-align: center" id="r.iq"><img style="width: 988px; height: 347px" src="http://docs.google.com/File?id=ddwgxw9r_809gzj5zpdt_b"  alt="" /></div>
</div>
</div>
<div>而第二副图，不是我画的，就不转载了，参见<a id="i15g" title="这里" href="http://www.cppblog.com/javenstudio/articles/43073.html">这里</a>，它描述了整个Map/Reduce的服务器状况图，包括整体流程、所处服务器进程、输入输出等，看清楚这幅图，对Map/Reduce的基本流程应该能完全跑通了。有这几点，可能图中描述的不够清晰需要提及一下，一个是在HDFS中，其实还有日志文件，图中没有标明；另一个是步骤5，其实是由TaskTracker主动去拉取而不是JobTracker推送过来的；还有步骤8和步骤11，创建出来的MapTask和ReduceTask，在Hadoop中都是运行在独立的进程上的。。。</div>
<h3>IV. Map任务详请</h3>
<div>从上面，可以了解到整个Map和Reduce任务的整体流程，而后面要啰嗦的，是具体执行中的细节。Map任务的输入，是分布式文件系统上的，包含键值对信息的文件。为了给每一个Map任务指定输入，我们需要掌握文件格式把它分切成块，并从每一块中分离出键值信息。在HDFS中，输入的文件格式，是由<strong>InputFormat&lt;K,
V></strong>类来表示的，在JobConf中，它的默认值是<strong>TextInputFormat</strong>类（见<strong>getInputFormat</strong>），此类是特化的<strong>FileInputFormat&lt;LongWritable,
Text></strong>子类，而<strong>FileInputFormat&lt;K, V></strong>正是InputFormat&lt;K,
V>的子类。通过这样的关系我们可以很容易的理解，默认的文件格式是<strong>文本文件</strong>，且键是<strong>LongWritable</strong>类型（整形数），值是<strong>Text</strong>类型（字符串）。仅仅知道文件类型是不够的，我们还需要将文件中的每一条数据，分离成键值对，这个工作，是<strong>RecordReader&lt;K,
V></strong>来做的。在TextInputFormat的<strong>getRecordReader</strong>方法中我们可以看到，与TextInputFormat默认配套使用的，是<strong>LineRecordReader</strong>类，是特化的<strong>RecordReader&lt;LongWritable,
Text></strong>的子类，它将每<strong>一行作为一个记录，起始的位置作为键，整行的字符串作为值</strong>。有了格式，分出了键值，还需要切开分给每一个Map任务。每一个Map任务的输入用<strong>InputSplit</strong>接口表示，对于一个文件输入而言，其实现是<strong>FileSplit</strong>，它包含着<strong>文件名、起始位置、长度和存储它的一组服务器地址</strong>。。。</div>
<div>当Map任务拿到所属的InputSplit后，就开始一条条读取记录，并调用用于定义的Mapper，进行计算（参见MapRunner&lt;K1,
V1, K2, V2>和MapTask的run方法），然后，输出。MapTask会传递给Mapper一个OutputCollector&lt;K,
V>对象，作为输出的数据结构。它定义了一个collect的函数，接受一个键值对。在MapTask中，定义了两个OutputCollector的子类，一个是MapTask.DirectMapOutputCollector&lt;K,
V>，人如其名，它的实现确实很Direct，直截了当。它会利用一个RecordWriter&lt;K,
V>对象，collect一调用，就直接调用RecordWriter&lt;K,
V>的write方法，写入本地的文件中去。如果觉着RecordWriter&lt;K,
V>出现的很突兀，那么看看上一段提到的RecordReader&lt;K,
V>，基本上，数据结构都是对应着的，一个是输入一个是输出。输出很对称也是由RecordWriter&lt;K,
V>和OutputFormat&lt;K, V>来协同完成的，其默认实现是LineRecordWriter&lt;K,
V>和TextOutputFormat&lt;K, V>，多么的眼熟啊。。。</div>
<div>除了这个非常直接的实现之外，MapTask中还有一个复杂的多的实现，是MapTask.MapOutputBuffer&lt;K extends
Object, V extends
Object>。有道是简单压倒一切，那为什么有很简单的实现，要琢磨一个复杂的呢。原因在于，看上去很美的往往带着刺，简单的输出实现，每调用一次collect就写一次文件，频繁的硬盘操作很有可能导致此方案的低效。为了解决这个问题，这就有了这个复杂版本，它先开好一段内存做<strong>缓存</strong>，然后制定一个比例做<strong>阈值</strong>，<strong>开一个线程监控</strong>此缓存。collect来的内容，先写到缓存中，当监控线程发现缓存的内容比例超过阈值，挂起所有写入操作，建一个<strong>新的文件</strong>，把缓存的内容批量<strong>刷到此文件中</strong>去，清空缓存，重新开放，接受继续collect。。。</div>
<div>为什么说是刷到文件中去呢。因为这不是一个简单的照本宣科简单复制的过程，在写入之前，会先将缓存中的内存，经过排序、合并器（Combiner）统计之后，才会写入。如果你觉得Combiner这个名词听着太陌生，那么考虑一下Reducer，Combiner也就是一个Reducer类，通过JobConf的setCombinerClass进行设置，在常用的配置中，Combiner往往就是用用户为Reduce任务定义的那个Reducer子类。只不过，Combiner只是服务的范围更小一些而已，它在Map任务执行的服务器本地，依照Map处理过的那一小部分数据，先做一次Reduce操作，这样，可以压缩需要传输内容的大小，提高速度。每一次刷缓存，都会开一个新的文件，等此任务所有的输入都处理完成后，就有了若干个有序的、经过合并的输出文件。系统会将这些文件搞在一起，再做一个多路的归并外排，同时使用合并器进行合并，最终，得到了唯一的、有序的、经过合并的中间文件（注：文件数量等同于分类数量，在不考虑分类的时候，简单的视为一个...）。它，就是Reduce任务梦寐以求的输入文件。。。</div>
<div>除了做合并，复杂版本的OutputCollector，还具有<strong>分类</strong>的功能。分类，是通过<strong>Partitioner&lt;K2,
V2></strong>来定义的，默认实现是<strong>HashPartitioner&lt;K2,
V2>，</strong>作业提交者可以通过JobConf的<strong>setPartitionerClass</strong>来自定义。分类的含义是什么呢，简单的说，就是将Map任务的输出，划分到若干个文件中（通常与Reduce任务数目相等），使得每一个Reduce任务，可以处理某一类文件。这样的好处是大大的，举一个例子说明一下。比如有一个作业是进行<strong>单词统计</strong>的，其Map任务的中间结果应该是<strong>以单词为键，以单词数量为值的文件</strong>。如果这时候只有一个Reduce任务，那还好说，从<strong>全部的Map任务</strong>那里收集文件过来，分别统计得到最后的输出文件就好。但是，如果单Reduce任务无法承载此负载或效率太低，就需要多个Reduce任务并行执行。此时，再沿用之前的模式就有了问题。每个Reduce任务从<strong>一部分Map任务</strong>那里获得输入文件，但最终的输出结果并不正确，因为同一个单词可能在不同的Reduce任务那里都有统计，需要想方法把它们统计在一起才能获得最后结果，这样就没有将Map/Reduce的作用完全发挥出来。这时候，就需要用到分类。如果此时有两个Reduce任务，那么将输出分成两类，一类存放字母表排序较高的单词，一类存放字母表排序低的单词，每一个Reduce任务从<strong>所有的Map任务</strong>那里获取一类的中间文件，得到自己的输出结果。最终的结果，只需要把各个Reduce任务输出的，拼接在一起就可以了。本质上，这就是将Reduce任务的输入，<strong>由垂直分割，变成了水平分割</strong>。Partitioner的作用，正是接受一个键值，返回一个分类的序号。它会在从缓存刷到文件之前做这个工作，其实只是多了一个文件名的选择而已，别的逻辑都不需要变化。。。</div>
<div>除了缓存、合并、分类等附加工作之外，复杂版本的OutputCollector还支持错误数据的跳过功能，在后面分布式将排错的时候，还会提及，标记一下，按下不表。。。</div>
<h3>V. Reduce任务详情</h3>
<div>理论上看，Reduce任务的整个执行流程要比Map任务更为的罗嗦一些，因为，它需要收集输入文件，然后才能进行处理。Reduce任务，主要有这么三个步骤：<strong>Copy</strong>、<strong>Sort</strong>、<strong>Reduce</strong>（参见ReduceTask的run方法）。所谓Copy，就是从执行各个Map任务的服务器那里，收罗到本地来。拷贝的任务，是由<strong>ReduceTask.ReduceCopier</strong>类来负责，它有一个内嵌类，叫<strong>MapOutputCopier</strong>，它会在一个单独的线程内，负责某个Map任务服务器上文件的拷贝工作。远程拷贝过来的内容（当然也可以是本地了...），作为MapOutput对象存在，它可以在内存中也可以序列化在磁盘上，这个根据内存使用状况来自动调节。整个拷贝过程是一个动态的过程，也就是说它不是一次给好所有输入信息就不再变化了。它会不停的调用<strong>TaskUmbilicalProtocol</strong>协议的<strong>getMapCompletionEvents</strong>方法，向其父TaskTracker询问此作业个Map任务的完成状况（TaskTracker要向JobTracker询问后再转告给它...）。当获取到相关Map任务执行服务器的信息后，都会有一个线程开启，做具体的拷贝工作。同时，还有一个内存Merger线程和一个文件Merger线程在同步工作，它们将新鲜下载过来的文件（可能在内存中，简单的统称为文件...），做着归并排序，以此，节约时间，降低输入文件的数量，为后续的排序工作减负。。。</div>
<div>Sort，排序工作，就相当于上述排序工作的一个延续。它会在所有的文件都拷贝完毕后进行，因为虽然同步有做着归并的工作，但可能留着尾巴，没做彻底。经过这一个流程，该彻底的都彻底了，一个崭新的、合并了所有所需Map任务输出文件的新文件，诞生了。而那些千行万苦从其他各个服务器网罗过来的Map任务输出文件，很快的结束了它们的历史使命，被扫地出门一扫而光，全部删除了。。。<br />
所谓好戏在后头，Reduce任务的最后一个阶段，正是Reduce本身。它也会准备一个<strong>OutputCollector</strong>收集输出，与MapTask不同，这个OutputCollector更为简单，仅仅是打开一个<strong>RecordWriter</strong>，collect一次，write一次。最大的不同在于，这次传入RecordWriter的文件系统，基本都是<strong>分布式文件系统</strong>，或者说是HDFS。而在输入方面，ReduceTask会从JobConf那里调用一堆getMapOutputKeyClass、getMapOutputValueClass、getOutputKeyComparator等等之类的自定义类，构造出Reducer所需的键类型，和值的迭代类型Iterator（一个键到了这里一般是对应一组值）。具体实现颇为拐弯抹角，建议看一下<strong>Merger.MergeQueue</strong>，<strong>RawKeyValueIterator</strong>，<strong>ReduceTask.ReduceValuesIterator</strong>等等之类的实现。有了输入，有了输出，不断循环调用自定义的Reducer，最终，Reduce阶段完成。。。</div>
<h3>VI. 分布式支持</h3>
<h4>1、服务器正确性保证</h4>
<div>Hadoop
Map/Reduce服务器状况和HDFS很类似，由此可知，救死扶伤的方法也是大同小异。废话不多说了，直接切正题。同作为客户端，Map/Reduce的客户端只是将作业提交，就开始搬个板凳看戏，没有占茅坑的行动。因此，一旦它挂了，也就挂了，不伤大雅。而任务服务器，也需要随时与作业服务器保持心跳联系，一旦有了问题，作业服务器可以将其上运行的任务，移交给它人完成。作业服务器，作为一个单点，非常类似的是利用还原点（等同于HDFS的镜像）和历史记录（等同于HDFS的日志），来进行恢复。其上，需要持久化用于恢复的内容，包含作业状况、任务状况、各个任务尝试的工作状况等。有了这些内容，再加上任务服务器的动态注册，就算挪了个窝，还是很容易恢复的。<strong>JobHistory</strong>是历史记录相关的一个静态类，本来，它也就是一个干写日志活的，只是在Hadoop的实现中，对日志的写入做了面向对象的封装，同时又大量用到观察者模式做了些嵌入，使得看起来不是那么直观。本质上，它就是打开若干个日志文件，利用各类接口来往里面写内容。只不过，这些日志，会放在分布式文件系统中，就不需要像HDFS那样，来一个SecondXXX随时候命，由此可见，有巨人在脚下踩着，真好。JobTracker.RecoveryManager类是作业服务器中用于进行恢复相关的事情，当作业服务器启动的时候，会调用其recover方法，恢复日志文件中的内容。其中步骤，注释中写的很清楚，请自行查看。。。</div>
<h4>2、任务执行的正确和速度</h4>
<div>整个作业流程的执行，秉承着木桶原理。执行的最慢的Map任务和Reduce任务，决定了系统整体执行时间（当然，如果执行时间在整个流程中占比例很小的话，也许就微不足道了...）。因此，尽量加快最慢的任务执行速度，成为提高整体速度关键。所使用的策略，简约而不简单，就是<strong>一个任务多次执行</strong>。当所有未执行的任务都分配出去了，并且先富起来的那部分任务已经完成了，并还有任务服务器孜孜不倦的索取任务的时候，作业服务器会开始炒剩饭，把那些正在吭哧吭哧在某个服务器上慢慢执行的任务，再把此任务分配到一个新的任务服务器上，同时执行。两个服务器各尽其力，成王败寇，先结束者的结果将被采纳。这样的策略，隐含着一个假设，就是我们相信，输入文件的分割算法是公平的，某个任务执行慢，并不是由于这个任务本身负担太重，而是由于服务器不争气负担太重能力有限或者是即将撒手西去，给它换个新环境，人挪死树挪活事半功倍。。。</div>
当然，肯定有哽咽的任务，不论是在哪个服务器上，都无法顺利完成。这就说明，此问题不在于服务器上，而是任务本身天资有缺憾。缺憾在何处？每个作业，功能代码都是一样的，别的任务成功了，就是这个任务不成功，很显然，问题出在输入那里。输入中有非法的输入条目，导致程序无法辨识，只能挥泪惜别。说到这里，解决策略也浮出水面了，三十六计走位上，惹不起，还是躲得起的。在MapTask中的MapTask.SkippingRecordReader&lt;K,
V>和ReduceTask里的ReduceTask.SkippingReduceValuesIterator&lt;KEY,VALUE>，都是用于干这个事情的。它们的原理很简单，就是在读一条记录前，把当前的位置信息，封装成SortedRanges.Range对象，经由Task的reportNextRecordRange方法提交到TaskTracker上去。TaskTracker会把这些内容，搁在TaskStatus对象中，随着心跳消息，汇报到JobTracker上面。这样，作业服务器就可以随时随刻了解清楚，每个任务正读取在那个位置，一旦出错，再次执行的时候，就在分配的任务信息里面添加一组SortedRanges信息。MapTask或ReduceTask读取的时候，会看一下这些区域，如果当前区域正好处于上述雷区，跳过不读。如此反复，正可谓，道路曲折，前途光明啊。。。
<h3>VII. 总结</h3>
<div>对于Map/Reduce而言，真正的困难，在于提高其适应能力，打造一款能够包治百病的执行框架。Hadoop已经做得很好了，但只有真正搞清楚了整个流程，你才能帮助它做的更好。。。</div><img src="http://www.cnblogs.com/duguguiyu/aggbug/1400278.html?type=1" width="1" height="1" alt=""/><p>评论: 7　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/02/28/1400278.html#pagedcomment" target="_blank">查看评论</a>　<a href="http://www.cnblogs.com/duguguiyu/archive/2009/02/28/1400278.html#commentform" target="_blank">发表评论</a></p><hr/><p>最新新闻：<br/>· <a href="http://news.cnblogs.com/n/56835/" target="_blank">王冉：中国会成全球最大移动互联网市场</a><span style="color:gray">(2010-02-10 13:42)</span><br/>· <a href="http://news.cnblogs.com/n/56833/" target="_blank">冷静，社交游戏！</a><span style="color:gray">(2010-02-10 13:38)</span><br/>· <a href="http://news.cnblogs.com/n/56834/" target="_blank">百度新宠：网络视频</a><span style="color:gray">(2010-02-10 13:37)</span><br/>· <a href="http://news.cnblogs.com/n/56832/" target="_blank">国产软件的“拿来主义”：开源软件、主导权</a><span style="color:gray">(2010-02-10 13:26)</span><br/>· <a href="http://news.cnblogs.com/n/56831/" target="_blank">谷姐瘫痪 发公开信称已遭黑客攻击5天</a><span style="color:gray">(2010-02-10 13:21)</span><br/></p><p>编辑推荐：<a href="http://news.cnblogs.com/n/56812/" target="_blank">Google社会化产品Buzz发布会实录</a><br/></p><p>网站导航：<a href="http://www.cnblogs.com" target="_blank">博客园首页</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/" target="_blank">个人主页</a>&nbsp;&nbsp;<a href="http://news.cnblogs.com" target="_blank">新闻</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/ing/" target="_blank">闪存</a>&nbsp;&nbsp;<a href="http://home.cnblogs.com/group/" target="_blank">小组</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com/q/" target="_blank">博问</a>&nbsp;&nbsp;<a href="http://space.cnblogs.com" target="_blank">社区</a>&nbsp;&nbsp;<a href="http://kb.cnblogs.com" target="_blank">知识库</a></p>]]></description></item></channel></rss>