锻造坊 - OpenSocial 和 Google Gadgets
过去几个月,我撰写了关于 Facebook API 的文章,该 API 允许第三方开发者将其应用程序集成到 Facebook 中。已经存在大量此类应用程序,并且每天都有更多应用程序被创建和发布。
然而,Facebook 并不是唯一的社交网络站点。实际上,Facebook 甚至不是最大的社交网络站点——尽管它是增长最快的,并且似乎具有很大的发展势头。这在很大程度上归功于开发者创建新应用程序并将其集成到 Facebook 中的能力。而且,尽管大多数 Facebook 应用程序(我认为)都很愚蠢,但这并没有阻止人们尝试它们,甚至经常使用它们。
Facebook 提供开发者 API 对 Facebook 用户来说绝对是件好事。但是,这对至少其他三类人来说是坏消息。首先,其他社交网络系统的用户突然面临使用不太受欢迎的系统的前景。(在社交网络世界中,不太受欢迎的系统也不太理想。)其次,运行非 Facebook 社交网络站点的人,例如 LinkedIn 和 MySpace,突然面临他们的用户离开 Facebook 的前景。最后,软件开发者开始将 Facebook 视为他们应该为其开发的最理想平台,因为它拥有最大的用户群。即使一个或多个竞争站点推出 API,即使它像 Facebook API 一样丰富,它也可能无法覆盖足够多的用户以使其加倍努力值得。
所以,我很高兴通过 Marc Andreessen 的博客了解到,许多社交网络站点正在以一种让所有这三类人群都满意的方式回应 Facebook。他们宣布了一个 API,该 API 将允许一个应用程序跨多个不同的社交网络站点工作。这个 API,被称为 OpenSocial,可以添加到任何站点(“容器”)或应用程序中。如果您编写一个 Facebook 应用程序,它将只能在 Facebook 上运行。但是,如果您编写一个 OpenSocial 应用程序,它将在 Ning、MySpace、Orkut 和近十几个其他系统下运行。
当然,OpenSocial 与 Facebook API 并不完全相同。事实上,与 Facebook API 相比,它有一些缺点。此外,当我 2007 年 12 月中旬写下这些文字时,OpenSocial 仍处于早期 beta 版本中。
然而,OpenSocial 从几个角度来看都很有趣。首先,这是对 Facebook 的一次有趣的挑战,值得我们关注,仅仅因为它展示了公司现在为了吸引开发者和用户会付出多大的努力。但是,它也很有趣,因为它是我想到的第一个基于 HTTP、JavaScript 和 HTML 的应用程序标准。也就是说,我相信 OpenSocial 是第一个完全客户端而非服务器端的 Web 开发 API。如果没有别的,这表明 JavaScript 对 Web 开发者来说变得多么重要。
本月,我们开始从应用程序开发者的角度来看待 OpenSocial。OpenSocial 构建于 Google 完成的工作之上;因此,它基于 Google 开发的几种技术,包括 Google Gadgets。因此,让我们从讨论 Google Gadgets 以及我们如何创建和使用它们开始我们的 OpenSocial 讨论。下个月,我们将研究如何将一个简单的 gadget 变成一个社交 gadget 并将其与 OpenSocial 容器连接起来。
一个 OpenSocial 应用程序,其核心是 XML 和 JavaScript 的组合,使用特殊版本的 Google Gadgets。代码用 JavaScript 编写,gadget 的首选项和指南使用 XML 设置。Google 的在线文档中最简单的 gadget 如下
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Hello world" /> <Content type="html"> <![CDATA[ Hello, world! ]]> </Content> </Module>
正如您所能想象的,上面的 gadget 没有做太多事情。第一行显示它是一个 XML 文档,并且它使用 UTF-8 编码。这意味着我们可以用我们喜欢的任何语言编写 gadget,并且它们应该可以正常工作。然后,gadget 包含在 <Module> 标签内,显然是因为 gadget 在开发时被称为模块。gadget 的内容位于 <Module> 内。
gadget 内有三个潜在的部分
ModulePrefs:定义特定 gadget 的设置。
Content:包含为用户显示的 HTML,以及用户将与之交互的任何 JavaScript 代码。
UserPrefs:用于存储用户首选项。
上面的测试 gadget 不包含任何 UserPrefs,并且其 Content 部分仅包含 HTML,但它仍然有效。
要查看此 gadget 的实际效果,您需要创建一个 iGoogle 页面。这需要拥有一个 Google 登录名。(我熟悉越来越多地提出的关于 Google 的隐私问题。OpenSocial 不会与 Google 绑定;因此,它不需要 Google 登录名。但是,目前,为 iGoogle 页面创建一个 gadget 最容易。)转到您的个人 iGoogle 页面:google.com/ig。
屏幕右侧有一个名为“添加内容”的链接。这就是您将新 gadget 添加到您的个人 iGoogle 页面的方式。默认情况下,它显示最受欢迎的 gadget,您当然可以随意添加任意数量的这些 gadget。但是,如果您要开发 gadget,请添加“我的 Gadgets”gadget,它为您提供一些额外的控制和功能。使用搜索框查找“我的 gadgets”,当您在搜索结果列表中找到它时,单击“立即添加”链接。您将被带回到您的 iGoogle 页面,这个新的 gadget 现在可用了。
Google 试图使 gadget 开发尽可能容易。它简化学习曲线的一种方式是通过创建许多在线工具,这些工具消除了许多开发者的编辑和存储需求。因此,尽管许多 Web 开发者(像我,也可能是您)很乐意在 Emacs 中编写程序并将它们放在他们自己的私有 Web 服务器上,但 Google 意识到并非每个人都拥有(或熟悉)此类工具的访问权限。因此,Google 提供了一个基于 Web 的编辑器(GGE,Google Gadget Editor),它不仅允许人们通过 Web 浏览器编辑他们自己的 gadget,还为 gadget 提供免费存储。
在本专栏中,我将采取更传统的存储方式,尽管您可以忽略我的示例。我将把我的 gadget 放在我的 Web 服务器上 (atf.lerner.co.il)。要将这些 gadget 合并到我的 iGoogle 页面中,我必须转到“我的 Gadgets”gadget 并输入 gadget 的完整 URL。例如,我将上面的“Hello, world”gadget 存储在我的服务器上,命名为 rmlgadget1.xml。因此,我在“我的 Gadgets”中输入了以下 URL:http://atf.lerner.co.il/rmlgadget1.xml。
果然,加载片刻后,我在我的 iGoogle 屏幕上看到了“Hello, world!”。每个 gadget 都显示在一个 iframe 内,iframe 是一个 HTML 实体,允许开发者创建独立于其周围环境的内容。或者,换一种方式思考,iframe 确保 gadget 之间不会相互干扰,而是“锁定”在它们的框架内。
不用说,大多数开发者不会满足于制作“Hello, world”程序。相反,我们通常希望做一些更实质性的事情。
为了做到这一点,我们需要在 <Content> 部分中创建更多 HTML。我们可能还应该创建一些 JavaScript 来操作该 HTML,因为我们有一个完全开放的画布。
请注意,我将修改我创建的原始 gadget,我将其命名为 rmlgadget1。Google 缓存 gadget,这意味着一旦您将一个 gadget 加载到您的 iGoogle 页面上,对 gadget 所做的修改将不会显示出来。这时您必须启动您可靠的“我的 Gadgets”gadget,并取消选中 gadget 的缓存复选框(在我的示例中,rmlgadget1)。重新加载 iGoogle 页面将从 Web 服务器重新加载 gadget,使您能够获得更具交互性和生产力的开发体验。
这是一个更新,演示了如何在 gadget 中使用 JavaScript
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Hello world" /> <Content type="html"> <![CDATA[ <div id="content">Hello, world!</div> <script type="text/javascript"> var element = document.getElementById('content'); element.innerHTML = "Foo"; </script> ]]> </Content> </Module>
再一次,这个 widget 没有太多内容。我们只是简单地使用 JavaScript 和 DOM 来修改 div 的内容。因此,让我们让事情变得更有趣,并从 Linux Journal 的 RSS feed 中检索最新的头条新闻。然后,我们可以显示前几个头条新闻,甚至使它们可链接
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Reuven's Gadget" /> <Content type="html"> <![CDATA[ <div id="content">Loading feeds...</div> <script type="text/javascript"> var html = ''; var url = "http://feeds.feedburner.com/linuxjournalcom"; var callback = function(feed) { html += "<ul>\n"; for (var counter = 0; counter < feed.Entry.length; counter++) { html += "<li>" + '<a href="' + feed.Entry[counter].Link + '" ta\ rget="_blank">' + feed.Entry[counter].Title + "</a>" + "</li>\n"; } html += "</ul>\n"; _gel('content').innerHTML = html; }; var num_entries = 5; var get_summaries = false; _IG_FetchFeedAsJSON(url, callback, num_entries, get_summaries); </script> ]]> </Content> </Module>
上面的 gadget 代码以与我们之前的 gadget 相同的静态代码开始,尽管我将其从“Hello, world”更改为更有用的东西(“正在加载 feeds...”),因为此文本将在 feeds 加载时出现。
这个 gadget 中的 JavaScript 有些有趣,主要是因为它依赖于 Google 提供给 gadget 开发者的 _IG_FetchFeedAsJSON 函数。此函数接受四个参数,前两个是必需的——要从中获取参数的 URL 和在检索到 feed 时应调用的回调函数。对于我们的示例,我正在使用 FeedBurner.com 提供的 Linux Journal 的 RSS/Atom feed URL。因此,我们将获得由站点管理员定义的最新 www.linuxjournal.com 头条新闻列表。
回调函数(我在这里命名为 callback)使用单个参数调用,该参数是 JSON (JavaScript Object Notation),表示从我们的 URL 检索到的 feed。该 JSON 包含一个名为 Entry 的数组,其元素包含 feed 信息。每个元素都包含 Title 和 Link 属性,我们将使用它们来构造输出 HTML。
当调用 callback 时,我们首先转到 FeedBurner.com 并检索最新的五个头条新闻
_IG_FetchFeedAsJSON(url, callback, num_entries, get_summaries);
然后,我们迭代 Entry 的元素,将它们附加到一个我们方便地命名为 html 的变量中,并将每个 Title 放在一个 HTML 链接中,该链接在新标签页或窗口中打开目标 URL(感谢target="_blank"):
for (var counter = 0; counter < feed.Entry.length; counter++) { html += "<li>" + '<a href="' + feed.Entry[counter].Link + '" ta\ rget="_blank">' + feed.Entry[counter].Title + "</a>" + "</li>\n"; }
最后,我们分配我们的 div(以“正在加载 feeds...”开头)
_gel('content').innerHTML = html;
果然,我们的 gadget 工作得非常好,为我们提供了来自 Linux Journal 的动态更新的头条新闻列表。还有什么比这更好的呢?
Google Gadgets 最有趣的特性之一是它们完全自包含,与周围的页面和应用程序隔离。正如我之前提到的,这是因为每个 gadget 都位于 iframe 内,这无疑是 gadget 被用作 OpenSocial 基础的原因之一。
然而,我们已经可以看到这将如何导致这样一种情况:应用程序,而不是托管 OpenSocial “容器”站点,决定了外观和感觉。这意味着如果您包含六个 OpenSocial 应用程序,每个应用程序都将有自己的外观和感觉。这与 Facebook 有很大不同,在 Facebook 中,应用程序在很大程度上被迫遵守 Facebook 的外观和感觉,从而创造了相当愉悦的用户体验。时间会证明这是否会引起问题,或者开发者和用户是否会在此问题上达成愉快的折衷方案。
另一个单独的问题是,每个 gadget 仅包含一个 HTML 页面。gadget 内发生的任何更新,正如我们所看到的,都归功于 JavaScript 对 DOM 的操作。这不是一件坏事,并且随着 Ajax 在 Web 开发者中变得越来越普遍,这种情况变得越来越普遍。但是,对于仍然使用每单击一页范例的开发者来说,这可能有点陌生。
Google Gadgets 是用 XML、HTML 和 JavaScript 的组合编写的小型、自包含的迷你页面。它们可以由 Google 或您自己的服务器托管,并且迄今为止,它们主要用于个性化的 iGoogle 服务。然而,Google Gadgets 现在构成了 OpenSocial 的基础,OpenSocial 是 Facebook 以外的社交网络站点使用的开放应用程序标准。下个月,我们将看到如何将我们的 Google Gadget 转换为 OpenSocial 应用程序。
资源
有关 OpenSocial 的最新更新,请查阅 Google OpenSocial 组:groups.google.com/group/opensocial。我特别建议查看最近活动列表,网址为:groups.google.com/group/opensocial/web/whats-up-with-opensocial。
有关 Google Gadgets 的大量信息可以在 code.google.com/apis/gadgets/docs/basic.html 中找到,包括许多示例。一些示例和说明有点过时,但通过一些挖掘,您应该能够弄清楚发生了什么。
要了解更多关于本月特定示例(涉及检索远程内容)的信息,请查阅 code.google.com/apis/gadgets/docs/remote-content.html。
Marc Andreessen,Netscape 的联合创始人,现在运营着用于创建社交网络的 Ning 站点,他在 blog.pmarca.com 上撰写关于软件行业、初创公司和 OpenSocial 的博客。
Reuven M. Lerner,一位长期的 Web/数据库开发者和顾问,是西北大学学习科学博士候选人,研究在线学习社区。在芝加哥地区生活四年后,他最近(与他的妻子和三个孩子)返回他们在以色列 Modi'in 的家。