求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
 
  
 
 
     
   
分享到
将 Google 的云计算功能连接到 Apple 的 iPhone 中
 

发布于2013-6-04

 

简介

过去几年出现了很多创新技术,2008 年对技术而言是不同凡响的一年。两项最让人兴奋的创新是云计算和移动应用程序开发。在本文中,我们将探究一种通信方法,这种方法能利用这两个技术来实现协作开发人员的梦想。在本文中,我们将使用 Google App Engine(Google 的云计算平台)和 iPhone(Apple 的移动平台)来开发一个能同步 “云” 数据的应用程序。

我们将利用一种简单的方法来从 App Engine 拉出数据放到 iPhone 上; 这种方法需要大量使用 python 和 App Engine。使用 RSS、ATOM 或 REST 将数据连锁到 iPhone 的常规方法非常简单,但是必须要编写一个解析器。更简单的一种做法是使用 XML 属性列表或 plist。根据属性列表的手册页面(参见 参考资料):“属性列表使用几个核心基础类型将数据组织成指定的值和值的列表,这些类型包括 CFString、CFNumber、CFBoolean、CFDate、CFData、CFArray 和 CFDictionary。借助这些类型,您就能够生成结构良好、可传输、可存储和可访问的数据,并且还尽可能提高了效率。”

plist 消除了在 iPhone 上解析 XML 的烦扰,因为这些 plist 是 XML 文件格式的,Cocoa Touch 可以很容易将其解析并转变成有意义的对象。在 App Engine 上使用 Python 内的 plist 库,不用费什么力气就能将任意一个简单 Python 库对象发送给 iPhone,但前提是 Python 库内的数据类型是 plist 允许的。本文展示了使用 TouchEngine 开源库开发应用程序以便查看莎士比亚的十四行诗。要获得 Google Code 项目的链接,请参见 参考资料。

背景

首先,让我们先来看看有关 iPhone SDK 和 Google App Engine 的背景信息。

iPhone SDK

Native iPhone SDK 可通过 Objective-C 语言得到。它非常类似于 Mac OS X? 上的 Cocoa 编程,包括了能充分利用 iPhone 独特特性的一些 API,比如 GPS、触摸屏(multi-touch)、加速器(accelerometer)以及屏幕键盘。将来的功能还将包括对通知自动推入(push notification)等技术的支持。有关 iPhone Native SDK 的更多信息,请参见 参考资料。

对于移动应用程序开发人员而言,iPhone 提供了丰富的开发环境。直到最近,Objective-C 对 很多开发人员而言仍旧是一种相当深奥的语言,因为它只用于 NeXT 和 Apple,但是现在通过 Cocoa Touch SDK,它的支持者开始多了起来。借助 iPhone,Objective-C 更是成为了全部新一代移动应用程序开发人员的前沿和中心。

何为 Google App Engine?

有了 Amazon 的 S3 存储和 EC2 弹性计算服务,云计算在可视化方面得到了很大的推进。Google App Engine 是基于服务的云计算市场的一个新生力量。Google App Engine 为著名的 Google 可伸缩数据中心提供了一个 Python 语言的 API(将来会出现其他语言的版本)。这是一个极大的变革,它让软件开发人员能够从管理应用程序伸缩性的固有复杂性中解脱出来,让他们能将精力集中于应用程序的编写。

从 Google App Engine 生成 plist 文件

我们先来看看如何从 Google App Engine 生成 plist 文件,之后,您会通过 iPhone Cocoa Touch SDK 在 iPhone 上使用该文件。由于 App Engine 起初是免费的,所以它成为了移动应用程序开发人员的一种有趣的原型化方法。此外,此 API 是 Python 版本,而该语言享有开发迅速的美誉;而且它还是一种解释效率很高的语言。通过 App Engine 和 Python 将 iPhone 应用程序的繁重任务以及数据存储外包给 “云功能”,是一种非常有益的做法。

要跟随本文进行操作,需要下载 App Engine SDK(参见 参考资料 以获得最新版本)。有了 App Engine,很容易就能让一个 protype 在几分钟内工作起来。请注意,您也可以从本文附带的源代码下载此示例。

为了将 plist 文件提供给 iPhone 应用程序使用,只需将 App Engine project 目录内的 plistlib.py 包括进来,稍微修改一下 main.py 脚本,再包括进 sonnet.py。Sonnet.py 是一个 Python 源文件,其中的一个目录包含所有莎士比亚十四行诗的文本。清单 1 所示的就是这个 main.py 文件。

清单 1. main.py

#!/usr/bin/env python
#Python sonnet maker

import wsgiref.handlers
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app

#external imports
import sonnet
import plistlib

class MainHandler(webapp.RequestHandler):
"""Returns sonnets dictionary as a converted plist"""
def get(self):
plist = plistlib.writeplistToString(sonnet.verses)
self.response.out.write(plist)

def main():
application = webapp.WSGIApplication([('/plists/sonnets', MainHandler),
],
debug=True)
run_wsgi_app(application)


if __name__ == '__main__':
main()

上面这个代码片段将包含莎士比亚十四行诗的字典的内容转变成一个 XML plist,并将其提供给任何请求此 /plists/sonnets URL 的客户机。不管您相信与否,这就是我们的这个 Google App Engine 应用程序的主体。清单 2 给出了 sonnet.py 的一小部分。

清单 2. sonnet.py 的示例

verses={"verses":[["I","""FROM fairest creatures we desire increase,
That thereby beauty's rose might never die,
But as the riper should by time decease,
His tender heir might bear his memory:
But thou, contracted to thine own bright eyes,
Feed'st thy light'st flame with self-substantial fuel,
Making a famine where abundance lies,
Thyself thy foe, to thy sweet self too cruel.
Thou that art now the world's fresh ornament
And only herald to the gaudy spring,
Within thine own bud buriest thy content
And, tender churl, makest waste in niggarding.
Pity the world, or else this glutton be,
To eat the world's due, by the grave and thee."""]}

[NOTE: EDITED FOR SPACE]

main 函数将 URL /plists/sonnets 传递到类 MainHandler。如果此客户机通过 HTTP GET 请求数据,就会返回类似清单 3 的结果。

清单 3. HTTP Get 的结果

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD plist 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>verses</key>
<array>
<array>
<string>I</string>
<string>FROM fairest creatures we desire increase,
That thereby beauty's rose might never die,
But as the riper should by time decease,
His tender heir might bear his memory:
But thou, contracted to thine own bright eyes,
Feed'st thy light'st flame with self-substantial fuel,
Making a famine where abundance lies,
Thyself thy foe, to thy sweet self too cruel.
Thou that art now the world's fresh ornament
And only herald to the gaudy spring,
Within thine own bud buriest thy content
And, tender churl, makest waste in niggarding.
Pity the world, or else this glutton be,
To eat the world's due, by the grave and thee.</string>
</array>
<array>
[NOTE: EDITED FOR SPACE]

访问 isonnet 项目主页(相关链接,请参考 参考资料),可以看到此输出。此 URL 给出的是一个编辑后的莎士比亚十四行诗的完整 plist 表示。不过,请注意,您的浏览器可能将此显示为一个巨大的纯文本文件。XML plist 是有效的 XML 而且大多数浏览器都会试图显示它。请参阅页面的源代码,查看格式化的 plist。

Python 和 App Engine 示例的更详细信息以及有关 Google App Engine 的高级教程的链接,请参看 参考资料。但是,目前,让我们先来看看一个 iPhone 应用程序是如何接受此 plist 数据来更改应用程序数据的。

创建一个能从 Google App Engine 动态读取并缓存 XML plist 文件的 iPhone 应用程序

TouchEngine 包含一组对象,使得在 iPhone 上下载和缓存 XML plist 变得十分简单。我们用来下载和缓存 sonnet plist 所用的对象在其头文件 GRplistController.h 内描述,如清单 4 所示。

清单 4. GRplistController.h

#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
#import "Reachability.h"
#import "GRplistControllerDelegateProtocol.h"

typedef enum {
kGRplistDownloadCannotInitiate = 0,
kGRplistConnectionFailure,
kGRplistFileFormatFailure
} GRErrorCode;

#define GR_ERROR_DOMAIN @"GRplistController_Error_Domain"

@class GRplistModel;

// This class will grab a remote plist from the server whenever update
// is called.

// Also registers with the Reachability object for notifications when
// the remote host changes availability and updates accordingly
// asking the delegate first if downloading new data is desirable.

@interface GRplistController : NSObject {
NSURL *remoteURL;
NSObject <GRplistControllerDelegate> *delegate;

NetworkStatus remoteHostStatus;
NetworkStatus internetConnectionStatus;
NetworkStatus localWiFiConnectionStatus;
BOOL loadingData;
@private
GRplistModel *_model;
BOOL hostIsReachable;
NSMutableDictionary *plistIndex;
NSMutableData *receivedData;
NSURLConnection *connection;
}

@property(nonatomic, retain)NSURL *remoteURL;
@property(nonatomic, assign)NSObject *delegate;
@property(nonatomic, readonly)GRplistModel *model;

//date of last download
@property(nonatomic, retain)NSDate *lastUpdate;
@property(nonatomic, getter=isLoadingData)BOOL loadingData;
@property NetworkStatus remoteHostStatus;
@property NetworkStatus internetConnectionStatus;
@property NetworkStatus localWiFiConnectionStatus;

//designated Initializer
- (id)initWithRemoteURL:(NSURL *)aRemoteURL;

- (void)updateDataFromDisk;
- (void)download;
- (void)cancelDownload;
@end

为了使用此类,我们提供了一个 NSURL,XML plist 就位于其中,我们告知它从磁盘下载或拉取数据。GRplistController 在此用户的数据存储目录生成一个 plist 字典文件,以便存储组成远程 URL 的缓存后的 plist 的位置和最近一次下载日期。plist 被下载一次后,后续的数据加载就可直接从磁盘进行。另外,还可以针对给定 URL 的最后一次下载的日期和时间使用 lastUpdate 属性来查询 GRplistController 对象,并决定何时从 Web 刷新数据。GRplistController 总是使用 NSURLConnection 异步下载数据,这样用户界面不会在等待新数据时冻结起来。如果新的数据不可用或不可访问,可以继续使用所缓存的数据,直至新数据可用且可完全下载。您还可以将一个对象设为 GRplistController 的一个代理(delegate),借此就可对数据下载进行细粒度控制,就能在更新数据到来时提供通知,在远端数据被证实不可访问时提供详细的错误报告。GRplistController 的这些代理方法在 GRplistControllerDelegateProtocol.h 内定义,如清单 5 所示。

清单 5. GRplistControllerDelegateProtocol.h

@class GRplistController;

@protocol GRplistControllerDelegate

@optional

// the list controller will automatically try to update data when the network status
// changes, so it's asking permission.
- (BOOL)listControllerShouldDownloadRemoteData:(GRplistController *)listController;
- (void)listController:(GRplistController *)
listController downloadDidFailWithError:(NSError *)err;

// if the data from the server has changed...
- (void)listControllerDataWillChange:(GRplistController *)listController;
- (void)listControllerDataDidChange:(GRplistController *)listController;

@end

我们的 iPhone 演示应用程序 Sonnet 的源代码包含在本文后面的 下载 部分,此应用程序能从我们在 App Engine 服务器上的项目拉取所有莎士比亚十四行诗。这就让我们能不时地上传修正(比如拼写错误、不准确之处等),而在此之前,这常常需要进行重新编译和应用程序更新(如果数据由应用程序附带的话)。我们既希望能不时地更新通用的应用程序数据,又希望能够避免进行应用程序重编译,因为我们的 UI 并未更改。这就让应用程序数据更新能够与特性添加和 bug 修复区分开来。而且,通过在连接到 Internet 的时候启动应用程序,用户总是可以拥有最新的数据,而不像原来那样,必须等待我们应用程序的更新出现在 iPhone Application Store,而这要花些时间。

Sonnet 的用户界面非常简单。应用程序首先用 UITableView 加载 RootViewController,而后者会立即显示来自所有可用缓存数据的每个十四行诗的前三行,并会在之后显示任何被更新的数据。

图 1. 表视图 iSonnet

如果用户触及了此表视图内的一个单元格,RootViewController 就会将 GRSonnetViewController 推到屏幕上来。GRSonnetViewController 之后会显示相应的完整的十四行诗。

图 2. Sonnet 视图

第一次运行时,应用程序没有数据显示给用户,所以在试图从 App Engine 服务器拉取数据却没有获得数据时最好是显示错误消息。否则,用户将会看到一个空白的表视图。(应用程序的正式版本将会在应用程序包中包括数据,但演示应用程序却没有。

图 3. 显示错误

这是惟一一次应用程序向用户显示网络通信错误的时候,因为在所有其他情况下应用程序已经下载并缓存了数据。有关 UI 实现的细节,可以参考 示例代码。

在 Sonnet,RootViewController 对象充当了 GRPlistController 的一个代理。RootViewController 的初始化代码如清单 6 所示。

清单 6 - RootViewController 初始化代码

(void)viewDidLoad {
[super viewDidLoad];
// Add the following line if you want the list to be editable
NSString *rootURL = [[self class] defaultURL];
self.sonnetsController = [[[GRplistController alloc]
initWithRemoteURL:[NSURL URLWithString:rootURL]] autorelease];
self.sonnetsController.delegate = self;

[self.sonnetsController updateDataFromDisk];
self.sonnets = nil;
[self updateSonnetsFromModel];

//hit the web for new information
[self updateSonnets];
}

在 Sonnet 启动时,RootViewController 先是试图从磁盘加载所缓存的 plist 数据。如果所缓存的数据可用,它就会立即被载入到应用程序内以便应用程序能立即可用。一旦载入了任何缓存数据,应用程序就会查询 App Engine 站点以便异步获得新数据(有关 NSURLConnection 实现的细节,参见 “#pragma mark Downloading of data” 行下面的 GRplistController.m )。如有新数据,此新数据会与缓存数据比较,如果发现修改,GRPlistController 就会通过 listControllerDataDidChange 方法通知 RootViewController。而 RootViewController 之后会重新载入带有新数据的十四行诗表。

结束语

App Engine 和 iPhone 开发综合在一起就形成了一种功能强大的工具,可用于编写和原型化移动应用程序。这个示例 Web 应用程序支持着一个小型的 iPhone 应用程序 Sonnet,这个小程序可从 Apple 的 Application Store 免费下载。综合 Google App Engine 和 iPhone 开发的强大之处在于可以使用 Python 快速原型化应用程序的某些部分,在这之前用 Objective-C 进行编码十分繁琐,另外这种综合也增强了(在线和离线)数据存储的灵活性。

TouchEngine 汇集了各种优点于一身。借助 TouchEngine,混合应用程序的开发人员可以很容易地编写软件,实现在异步更新云数据的同时在 iPhone 上本地缓存数据。这就让应用程序既能迅速响应用户输入,同时又能在线维护数据以便及时更新。

iPhone 和 Google App Engine 开发人员社区均提供了丰富的资源,可用来加速开发。如果您对面向这两个平台的开发或综合两平台的特性(如本文所示)感兴趣,我们建议您详细阅读官方文档的相关内容。Google 和 Apple 均有很棒的书面教程,某些情况下,也都提供有基于视频的教程。Google App Engine 还在全球范围内举行了 “Hack-A-Thon” 活动,您不妨用 Google 查查,看在您所在地区是否有这样的活动。Apple 的 WWDC 会议也是面向 iPhone SDK 程序员的一个很有价值的资源。

参考资料

学习

1、您可以参阅本文在 developerWorks 全球网站上的 英文原文。在 Apple Web 站点可以找到属性列表的 手册页。

2、isonnet 项目页 包含有莎士比亚十四行诗的诗文。

3、借助 “ Python For Unix and Linux System Administration ” 探索 Google App Engine Example。

4、文章 “Getting Started With Google App Engine O'Reilly Article”给出了有关 Google App Engine 的信息,还帮助您构建了一个应用程序。

5、Google App Engine in Action 简介了如何使用 Google App Engine 架构构建 Web 应用程序。

6、了解如何能借助 Google 的 GData APIs 访问存储于 Google 服务内的特定于用户的数据。

7、借助这个 Python Tutorial 了解 Python 语言及系统的基本概念和特性。

8、了解更多有关 TouchEngine Open Source Project: Synch plist files to App Engine 的信息。

9、通过这篇 Developer Connection 文章获得有关 learning Objective-C 2 的信息。

获得产品和技术

1、下载最新的 App Engine SDK。

2、用 iPhone SDK 开始进行开发。

相关文章

深度解析:清理烂代码
如何编写出拥抱变化的代码
重构-使代码更简洁优美
团队项目开发"编码规范"系列文章
相关文档

重构-改善既有代码的设计
软件重构v2
代码整洁之道
高质量编程规范
相关课程

基于HTML5客户端、Web端的应用开发
HTML 5+CSS 开发
嵌入式C高质量编程
C++高级编程
 
分享到
 
 


android人机界面指南
Android手机开发(一)
Android手机开发(二)
Android手机开发(三)
Android手机开发(四)
iPhone消息推送机制实现探讨
手机软件测试用例设计实践
手机客户端UI测试分析
手机软件自动化测试研究报告
更多...   


Android高级移动应用程序
Android应用开发
Android系统开发
手机软件测试
嵌入式软件测试
Android软、硬、云整合


领先IT公司 android开发平台最佳实践
北京 Android开发技术进阶
某新能源领域企业 Android开发技术
某航天公司 Android、IOS应用软件开发
阿尔卡特 Linux内核驱动
艾默生 嵌入式软件架构设计
西门子 嵌入式架构设计
更多...