您的位置:首页 > 大数据 > 人工智能

如何利用Adobe AIR创建桌面对话应用程序

2008-01-31 11:06 531 查看
桌面程序是Adobe® Integrated Runtime(AIR)的杀手级应用(killer app)。在与其他
工程师谈论Adobe AIR时,他们经常会提出同一个问题:什么是Adobe AIR的杀手级应用?对这个问题考虑的越深入,我就越觉得,除了工程师或架构师以外,其他人只是想使用某些应用程序中的桌面功能。

以对话应用程序为例。当然,可以通过web进行对话。AOL就有基于web的AOL Instant Messenger(AIM),还有其他不计其数的web对话小部件。 但人们仍然喜欢使用AIM、iChat或Trillian客户机,因为他们觉得用着方便。用户无须打开浏览器即可启动对话客户机。这些客户机的特点与桌面应用程序一样:拥有本地菜单;在系统托盘中显示;拥有弹出式本地窗口;可与其他本地应用程序合作;用户双击即可打开等等。

用户能够通过Adobe AIR访问跨平台技术中的所有桌面功能,同时还能使用开发web应用程序所用的那些开发工具。这真是太完美了!

本文将演示如何创建基于Adobe AIR的简单对话应用程序。它将与利用JSON传输数据的Ruby on Rails web应用程序进行通讯。为执行与Ajax有关的任务,它将利用广受欢迎的Prototype.js库与Rails应用程序进行通讯。

Adobe AIR对话应用程序还符合Adobe AIR新安全模型,这可确保从服务器上下载且在客户机上执行的JavaScript代码不会直接访问Adobe AIR应用程序编程接口(API)。通过这种方法,就不会通过强大的Adobe AIR接口下载恶意代码,以至于危害用户的计算机。

从图1可以看到,这个Adobe AIR对话应用程序实际上是由不同的HTML页面构成的:root.htm页面,它可访问Adobe AIR API并可利用这些API维护对话消息的本地数据库缓存;ui.htm页面,它可利用Prototype.js库与Rails服务器进行通讯。




图1. 处理与Ajax有关的任务以及本地数据库缓存的两个AIR页面
在Rails新安全模型中,root.html页面禁止运行解释JSON代码所必需的eval命令。这可防止JSON代码诱骗客户机运行Adobe AIR方法,以至对客户机器造成损害。

ui.html页面可运行eval命令,以解释来自服务器的JSON响应。但它不能直接访问Adobe AIR接口。在root.html和ui.html这两个页面之间有一个特殊“桥梁”,当它下载了新消息时,ui.html页面就能通知 root.html页面。这种更小型、更安全的“桥梁”就是使客户机免受恶意代码侵害的机制。

对整个对话系统有了基本理解后,接下来将着手创建Rails服务器后台应用程序。

创建Rails服务器
要创建Rails应用程序,首先选择Macintosh中很好用的Locomotive应用程序。在Windows平台中,可使用Instant Rails。这些套装应用程序都拥有创建Rails应用程序所需的一切功能。它们的界面很简单,如图2所示,其中将显示机器中的所有应用程序,并可创建新 应用程序。




图2. Locomotive界面

我要求Locomotive创建名为chat的新应用程序。然后利用chat应用程序中的上下文菜单在主页面中加载浏览器,如图3所示。




图3. Rails加载页面
好了,一切就绪。现在要创建数据库和对话模型了。首先创建三个MySQL数据库:chat_development、chat_test和chat_production。然后利用Rails生成器创建名为messages的新模型。
然后编辑生成的001_create_messages.rb文件,并添加要在表格中显示的数据列,如清单1所示。
清单 1. 001_create_messages.rb
class CreateMessages < ActiveRecord::Migration
def self.up
create_table :messages do |t|
t.column :user, :string
t.column :posted, :datetime
t.column :message, :string
end
end

def self.down
drop_table :messages
end
end
我添加了三列:用户名字段、消息提交日期和时间字段以及消息正文字段。如果想使用其他对话主题扩展本例,只需在此处添加其他表格并在控制器中增加新方法。
我还对models目录下的message.rb文件进行了必要的修改,如清单2所示。
清单 2. message.rb
class Message < ActiveRecord::Base
end
瞧瞧,我没进行任何修改。Rails的功能难道不齐全吗?
接着要创建对话控制器,Adobe AIR对话应用程序将利用该控制器发送新消息并选择新消息。清单3就是该控制器的代码。
清单 3. chat_controller.rb
class ChatController < ApplicationController
scaffold :message

def post
msg = Message.new
msg.user = params[:user]
msg.message = params[:message]
msg.posted = DateTime.now
msg.save

render( :text => { :id => msg.id }.to_json )
end

def getall
render( :text => Message.find(:all).to_json )
end

def getsince
msgs = Message.find( :all, :conditions => [ "id > ?", params[:id ] ] )
render( :text => msgs.to_json )
end
end
在代码开始部分,调用了经典的Rails scaffolding方法,以在浏览器中显示这些消息。Adobe AIR应用程序开始运行后,就可删除该方法。我还添加了新的post方法,它使用“user”和“message”两个参数,然后将新消息的提交日期设为当日。
getall方法返回一个JSON数组,数组中包含消息表中的所有数据。getsince方法也返回消息的JSON数组,其中只包含在指定id之后创建的消息。这样可提高Adobe AIR应用程序中的选择效率。
为测试该控制器,我在Rails应用程序中打开对话控制器。结果如图4所示。




图4. scaffolding列表界面
由于此时的数据库中没有任何消息,因此列表为空。单击New message链接添加消息,这将打开数据输入表单,如图5所示。




图5.使用scaffolding创建新消息
在其中输入示例消息并单击Create,这将返回如图6所示的列表。



图5.创建消息后的列表
该列表显示我有了一条新记录。很好,这表示数据库连接运行正常。但Adobe AIR应用程序不会使用该接口,因此要测试JSON接口。首先是getall方法,我在浏览器中修改URL,使其指向getall动作,就是这样!JSON返回了该条记录,如图7所示。




图 1-7.JSON getall动作
效果真不错。在导出JSON数据时,to_json方法的确非常方便。
接下来测试post方法,Adobe AIR应用程序将利用该方法提交对话消息。只要在URL中输入post动作,并指定“user”和“message”作为URL参数,就可测试该方法。
post动作返回以JSON编码的新建记录的id值,如图8所示。



图8. 消息提交动作产生的JSON响应
最后测试getsince动作。它与getall类似,但使用id参数,并且只返回id值大于指定值的消息。这里我将id指定为0,这将返回所有消息。可以看到返回了两条记录,一条是利用scaffolding添加的消息,另一条是刚刚提交的消息。
图9就是getsince动作的结果。



图9. getsince动作的JSON响应
以上就是对话示例的服务器部分。认真地讲,Ruby实现这种功能需要多少行代码?也许15行以上。真疯狂!而Rails绝不会让我失望。
接着创建在用户桌面计算机中运行的AIR对话应用程序。
创建Adobe AIR对话应用程序
我设想的对话应用程序其实很简单:在窗口顶端显示对话消息,首先显示用户名,然后显示消息。在窗口底部,我想有个地方可以输入用户名和消息,然后可以单击一个按钮将其添加到对话窗口中。我的要求如图10所示。



图10. AIR对话窗口
我将其设计得很简单,以方便读者可以根据需要对其进行修改,并创建自己的对话应用程序。
代码开始部分是XML文件,它告诉应用程序有关Adobe AIR的信息。清单4就是application.xml文件的代码。
清单 4. application.xml
<?xml version="1.0" encoding="utf-8" ?>
<application xmlns="http://ns.adobe.com/air/application/1.0.M5"
appId="com.example.SimpleChat" version="1.0">
<name>Simple Chat</name>
<description></description>
<copyright></copyright>

<initialWindow>
<title>Chat Window</title>
<content>root.html</content>
<systemChrome>standard</systemChrome>
<transparent>false</transparent>
<visible>true</visible>
<minimizable>true</minimizable>
<maximizable>true</maximizable>
<resizable>true</resizable>
<width>250</width>
<height>450</height>
<x>150</x>
<y>150</y>
</initialWindow>
<icon />
<fileTypes />
</application>
其中只有两个关键元素:在<title>标签中指定的对话应用程序的标题;在<width>和<height>标签 中指定的窗口初始大小。其中设置了该页面的用户界面大小,以使其适应250x450的面积。接着是大小调整代码,它用来调整窗口的大小,并修改窗口中所有 元素的形状,使其保持清晰。
清单5就是root.html页面的代码,用以处理数据库。
清单 5. root.html
<html>
<head>
<title>Chat Window</title>
<script type="text/javascript" src="AIRAliases.js"></script>
<script type="text/javascript">
var db;

var Exposed = {};

Exposed.trace = function(str) { air.trace(str); }

Exposed.alertNewMessage = function() {
if ( air.Shell.supportsDockIcon ) {
air.Shell.shell.icon.bounce();
}
}

Exposed.addMessage = function( id, user, posted, message ) {
var del = new air.SQLStatement();
del.text = "DELETE FROM messages WHERE id = :id;";
del.sqlConnection = db;
del.parameters[':id'] = id;
del.execute( );

var add = new air.SQLStatement();
add.text = "INSERT INTO messages (id, user, posted, message) VALUES ( :id, :user, :posted, :message );";
add.sqlConnection = db;
add.parameters[':id'] = id;
add.parameters[':user'] = user;
add.parameters[':posted'] = posted;
add.parameters[':message'] = message;
add.execute( );
}

Exposed.getMessages = function() {
var get = new air.SQLStatement();
get.text = "SELECT * FROM messages;";
get.sqlConnection = db;
get.execute();
var data = get.getResult().data;
return ( data == null ) ? [] : data;
}

function doLoad() {
document.getElementById('UI').contentWindow.parentSandboxBridge = Exposed;
window.resize = document.getElementById('UI').contentWindow.childSandboxBridge.onResizeEvent;

db = new air.SQLConnection( true );
db.open( air.File.applicationResourceDirectory.resolvePath( "chat.db" ), true );

var cr_exp = new air.SQLStatement();
cr_exp.text = "CREATE TABLE IF NOT EXISTS messages ( id, user, posted, message );";
cr_exp.sqlConnection = db;
cr_exp.execute();

if ( window.addEventListener != null )
window.addEventListener( 'resize', function() { window.resize( ); }, false );
if ( document.addEventListener != null )
document.addEventListener( 'resize', function() { window.resize( ); }, false );

document.getElementById('UI').contentWindow.childSandboxBridge.startMessages();
}
</script>
</head>
<body onload="doLoad();" style="margin:2px;padding;0px;">
<iframe id="UI"
src="http://muttmansion.com/ui.html"
sandboxRoot="http://muttmansion.com/"
documentRoot="app-resource:/"
width="95%"
height="95%"
style="border: 0px; margin: 0px; padding: 0px; width: 95%; height:95%">
</iframe>
</body>
</html>
您也许想知道为何要在对话应用程序中使用本地数据库。道理很简单:为了快速启动。如果旧消息存储在本地,则当每次加载应用程序时就不必回头查找它们。所要做的就是首先显示本地存储的消息,然后要求用户提供排在最后一个id值之后的新消息,这样即可得到所有的新消息。
doLoad()函数创建了该数据库。然后,为提供给ui.html页面的Exposed变量增加两个方法,分别用于添加消息以及获取消息列表。这就是本 文开头提到的root.html沙盒和ui.html沙盒之间的“桥梁”。这可确保在ui.html中运行的JSON评估代码不会运行任何Adobe AIR方法。它只可访问消息添加和消息获取方法。
alertNewMessage是root.html的另一个函数,它既有趣又有用。当一条需要用户关注的新消息到达时,alertNewMessage 就会使Dock栏中的应用程序图标(适用于Mac OS X机器)上下跳动。这是AIR的一项强大功能。它可以方便地访问诸如此类的操作系统特性,使用户感觉是在使用一种全面集成的应用程序。
root.html的最后一项重要功能是,它可将大小调整消息转发给ui.html,以确保将窗口调整到适当大小。
清单6就是ui.html页面的代码。
清单 6. ui.html
<html>
<head>
<head><title>Chat Window</title>
<script src="prototype.js"></script>
<style>
body { font-family: arial, verdana, sans-serif; font-size:small; margin:0px; padding:0px; }
#chat td { font-size: small; }
</style>
</head>
<body onload="doLoad()">
<div id="chatcontainer" style="height:315px;overflow:auto;">
<table id="chat">
</table>
</div>

<form id="chatmessage" style="margin:0px;">
Username: <input type="text" name="user" value="Jack" size="7" /><br/>
<textarea name="message" id="messagetext" style="width:100%;">
</textarea>
</form>
<button onclick="addmessage()">Add Message</button>

<script>
var lastid = 0;

function addmessage()
{
new Ajax.Request( 'http://192.168.1.192:3005/chat/post', {
method: 'post',
parameters: $('chatmessage').serialize(),
onSuccess: function( transport ) {
$('messagetext').value = '';
}
} );
}

function startMessages()
{
var msgs = parentSandboxBridge.getMessages();
for( var i = 0; i < msgs.length; i++ )
{
var elTR = $('chat').insertRow( -1 );
var elTD1 = elTR.insertCell( -1 );
elTD1.appendChild( document.createTextNode( msgs[i].user ) );
var elTD2 = elTR.insertCell( -1 );
elTD2.appendChild( document.createTextNode( msgs[i].message ) );
lastid = parseInt( msgs[i].id );
}
window.setTimeout( getMessages, 1000 );
}

function getMessages()
{
new Ajax.Request( 'http://192.168.1.192:3005/chat/getsince?id='+lastid, {
onSuccess: function( transport ) {
var msgs = eval( transport.responseText );
for( var i = 0; i < msgs.length; i++ )
{
var message = msgs[i].attributes.message;
var user = msgs[i].attributes.user;
var posted = msgs[i].attributes.posted;
var id = parseInt( msgs[i].attributes.id );

parentSandboxBridge.addMessage( id, user, posted, message );

if ( id > lastid )
{
var elTR = $('chat').insertRow( -1 );
var elTD1 = elTR.insertCell( -1 );
elTD1.appendChild( document.createTextNode( user ) );
var elTD2 = elTR.insertCell( -1 );
elTD2.appendChild( document.createTextNode( message ) );

lastid = id;

parentSandboxBridge.alertNewMessage();
}
}
window.setTimeout( getMessages, 1000 );
}
} );
}

function onResizeEvent() {
$('chatcontainer').style.width = (document.width-5)+'px';
$('chatcontainer').style.height = (document.height-100)+'px';
}

childSandboxBridge = { onResizeEvent: onResizeEvent, startMessages: startMessages };

function doLoad()
{
onResizeEvent();
}
</script>

</body>
</html>
该页面首先设置onResizeEvent 回调函数。然后利用startMessages()函数从数据库中获取缓存消息的当前列表,并预加载消息表格。startMessages()函数启动一 个计时器,它将调用getMessages()函数以从服务器中获取所有新消息。
getMessages()函数使用了Ajax.Request类,这是Prototype.js库中一个很好用的类,以向服务器请求执行getsince动作。当JSON返回时,代码将利用eval函数从JSON数组中获取数据,然后向表中添加新消息。
当用户单击Add Message按钮时将调用addmessage()函数,它利用Ajax.Request向对话控制器中的post动作发送用户名和消息。然后,当getMessages()函数下一次执行选择时,它就会挑选这条新消息,而该函数每秒运行一次。
最后一段代码是onResizeEvent函数,它按照窗口的新宽度和高度调整消息表。
我利用Adobe AIR SDK中的adl函数加载对话应用程序。接着输入“Cool!”并单击Add Message按钮,最后出现如图11所示的窗口。



图11.添加消息之后
要测试大小调整代码,可选中窗口右下角的控件,然后调整形状,使其更宽及更短。 图12就是窗口的最终效果。



图12.调整窗口大小之后
大小调整代码修改了表的大小,以大体适应窗口的目前面积。

后续工作
显然,这个对话应用程序远不如iChat或Trillian那样完善。但它是一个不错的应用程序基础,读者可在开发自己的应用程序时将其作为模板。就是 说,通过Adobe AIR特性集,读者可以创建适用于任何协议的、功能完善的对话客户机。Adobe AIR可启动本地窗口、使任务栏图标上下跳动、播放声音、管理本地菜单、读取并写入本地文件系统、处理拖放操作等等。它拥有创建高端对话客户机所需的一切 功能,且所有功能均可以JavaScript或Flex/Flash的形式提供,而这取决于您的需要。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: