
今年九月份的统计数据显示,目前在Android Market中已经有超过10K的Softwares和Games提供给Android手机用户下载。一些曾经在IPhone APP Store创下辉煌战绩的Applications已经开始集体移植到这个拥有巨大潜力的平台。官方第二届Android Development Challenge(简称:ADC2)已经进入到了最后评审阶段,据称这次拥有大量惊奇创新的应用软件,AR会针对排名靠前的一些软件和游戏提供比较详细的技术剖析,希望大家保持关注。今天要说的内容与创新无关,主要是讲解如何将现有功能互补的软件通过Intents无 缝整合,使其最大限度的提高用户体验(不同的软件之间相互协调,不但可以达到优势互补的目的,而且通过相互间的合作可以在某个领域创造出相对完整的应用体 验)。手机用户尤其厌烦手动输入大量信息(奥巴马曾对外界声称自己不使用Twitter的理由是不想忍受用手机输入信息的痛苦感受),所以这样的整合可以 促成多方软件协同工作的“共赢”局面。而在这其中起到绝对关键性的技术因素是利用了Android平台最独特和最优雅的“Intent机 制”,平凡的岗位创造出不平凡的“成就”。为此,大家需要多多留意那些看似微小的事物,往往越是简单的东西越容易改变这个世界。下面引述一个例子来形象展现这项应用所带来的好处:正在使用WHERE和GoodFood的用户可以基于当前数据直接快速利用OpenTable程序完成订餐服务,整个过程避免对 某些已经存在信息的重复调用。
名词解释:
- OpenTable(用户可以指定特定的区域,搜索符合用餐时间和用餐人数的餐馆,并将其标注在地图上)
- Where(基于精确地理位置的本地化信息检索工具,例如:哪里的汽油最便宜,路况信息和距离最近的咖啡店等等)
- uLocate (与Where出自同一个公司,同样也是利用地理位置信息,提供一个完整的沟通服务平台)
发挥Intent整合优势的准备工作
首先第一个步骤是需要设计使用的Intent接口(或者API)。例如:OpenTable开放了可供使用的公共RESERVE Intent ,利用特定的参数通过URI传递给处理预约申请的Activities(包括地点、时间和人数等基本要求)下面是一个简单的例子:
startActivity(new Intent("com.opentable.action.RESERVE",
Uri.parse("reserve://opentable.com/2947?partySize=3")));
我们的目的是创建一个可供其他开发者容易理解和应用简单的开放接口,而这完全取决于如何定义Action和Uri的样式。那么问题来了:什么样才算得上是比较理想的开放接口呢?
- 首先考虑如何定义合适的Action。如果应用程序仅仅是在常规状态下被调用,一般比较好的方法是直接采用系统提供的标准。针对有特殊要求的应用,则需要采 用自定义的Action,从而更加准确的提供局部应用支持(局部应用的含义:Application包括多个Activities,通常仅仅是一个或者一 小部分Activities通过开放的接口被外部的其它应用程序调用)。自定义Action需要符合Android平台的命名规 范:<package-name>.action.<action-name>。因为考虑到Action完全是String格 式,所以需要特别强调Namespace的重要性。(在本文提供的例子中将Action定义 为:”com.opentable.action.RESERVE”)
- 其次考虑外部接口程序根据特定的需求初始化数据。Android平台提供了Bundle(mapping:通过String索引相匹配的数据)用于传递简单或者复杂的数据。在本文的例子中采用了另外一种较灵活的方式,将参数直接捆绑在Uri中(”reserve://opentable.com/2947?partySize=3“)。应用”reserve://”字头的目的是为了匹配我们所定义的Action(android:scheme),”opentable.com”可以理解为作用域(android:host),前两者将用来在AndroidManifest.xml中声明<data /> (<data android:scheme=”reserve” android:host=”opentable.com”/>)。最后2947和partySize分别代表了餐馆ID和限定用餐人数。
Exposing 在系统中注册Intents
前面已经创建了Intent的内容。当条件满足时,系统将负责启动匹配Intent的应用程序(例子中所指:OpenTable)。为了让系统认识这位 “新朋友”(Intent),需要在<Intent-filter>标签中根据Intent的内容修改<action> 和<category>的属性,并根据Uri的描述创建一个<data>新标签:(以上设置都被包含在AndroidManifest.xml中)
...
...
如果程序中包含有多个开放的接口,那么可以根据上面的方法对每个Activity中的intent-filter根据需要修改成可以容易被外部直接调用的开放接口。在本文的例子中仅仅是修改了负责Reservation的Activity。Category设置成DEFAULT的 目的是为了保证当前Application可以直接被使用,而不会出现供用户选择启动程序的对话框,然而前提条件是保证这个Action在当前系统中的唯 一性,避免出现相同的Action同样以”DEFAULT”属性出现在不同的Activities中。<data>标签没有直接列出Uri中 提供的参数名称,在下面的小节中将讲解从Uri中解析参数值的方法。同时对于某些隐性的设置,有必要通过文档的方式让其它开发者能清楚的了解参数传递的标准。
Processing 处理调用请求
下面是OpenTable中负责处理Servation的Activity解析Intent中的参数和做出回应的主要部分:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); final Uri uri; final int restaurantId; try { uri = getIntent().getData(); restaurantId = Integer.parseInt( uri.getPathSegments().get(0)); } catch(Exception e) { // Restaurant ID is required Log.e(e); startActivity( FindTable.start(FindTablePublic.this)); finish(); return; } final String partySize = uri.getQueryParameter("partySize"); ... }
定义两个final的成员变量(final:表明变量仅可以被赋值一次)“uir”和“restaurantId”,分别用于存储Uri字符串和餐馆ID。
- getIntent().getData()可以返回当前Intent中包含的Uri。
- getPathSegments().get(int index)可以返回索引<index>代表的参数值。
- getQueryParameter(String Key)d 这是另外一种获取参数值的方法,适合于Key=Value(例子中:partySize=3)。
尽管没有包括完整的代码,但是基本上阐述了如何实现接口的方法。但仅仅实现了基本功能还是远远不够的,一个健壮的应 用程序需要考虑非常多的复杂情况。利用Intent机制完成某些功能调用的系统,需要特别强调参数的合法性以及如何有效的应对错误信息等。例如:当解析的 Restaurant ID出现问题时,需要提供用户另外一种可以手动输入的方法。 由此需要特别注意:满足常规功能仅是整个开发周期的一部分,另外还需要投入更多的时间处理异常情况。
优雅的处理一些特殊情况
以下是几种可预见的特殊情况:如何处理客户端没有安装OpenTable?如何处理传递了无效的Restaurant ID?
| Restaurant ID known | Restaurant ID unknown | |
| User has OpenTable | Call OpenTable Intent | Don’t show reserve button |
| User doesn’t have OpenTable | Call Market Intent | Don’t show reserve button |
下面的代码提供了处理当前上面特殊情况的方法:1)如果当前传递的Restaurant ID无效,那么Opentable按钮将设为不可用状态。2)如果当前客户端没有安装Opentable,那么将Intent切换为Market,并自动转为Opentable下载界面。
// setup the Intent to call OpenTable
Uri reserveUri = Uri.parse(String.format( "reserve://opentable.com/%s?refId=5449",
opentableId));
Intent opentableIntent = new Intent("com.opentable.action.RESERVE", reserveUri);
// setup the Intent to deep link into Android Market
Uri marketUri = Uri.parse("market://search?q=pname:com.opentable");
Intent marketIntent = new Intent(Intent.ACTION_VIEW).setData(marketUri);
opentableButton.setVisibility(opentableId > 0 ? View.VISIBLE : View.GONE);
opentableButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
PackageManager pm = getPackageManager();
startActivity(pm.queryIntentActivities(opentableIntent, 0).size() == 0 ? opentableIntent : marketIntent);
}
});
}


公布当前Application开放接口的应用标准
到目前与技术相关的内容已经完成。还剩下最后一步,同时也是最重要的环节。必须让其他开发者了解开放接口的使用方法,否则前面的努力都将变得毫无价值。其中最简单的方式是将使用文档发布在网站上供其他开发者查阅,而且建议文档中带有较全面的实例。
Posted in Resource, Tutorial |



