尝试瘦客户端的手机应用程序设计

2010-12-24 13:06 by hackerzhou

市面上形形色色的手机系统层出不穷,Android/iOS/Plam/BB/Symbian/Windows Mobile和最新出来的Windows Phone 7。如何减少重复的工作做统一的开发对于很多大公司变得尤为重要起来,如果要为每个产品在每个手机操作系统都写一套程序,开发量无疑是非常巨大的。因此很多公司会开发应对手机终端的Web界面来兼容各个平台,但是应用程序有着Web所无法代替的很多特点,比如后台提醒等等,这也就解释了为什么开心网和新浪微博除了有Web界面外还要开发手机终端软件。笔者拿Windows Phone 7做了一个小尝试,希望对有这方面需求的朋友有帮助。

代码SVN地址:
1. 手机客户端: http://hackerzhou.googlecode.com/svn/trunk/Phone/DynamicUI/
2. 服务器端: http://hackerzhou.googlecode.com/svn/trunk/CSharp/DynamicUIWeb/
当然,由于刚刚上手搞Windows Phone,代码写的并不好,请大家海涵~

visio

我尝试了一种瘦客户端的设计模式,客户端可以当作一个渲染器,本身没有任何业务逻辑和固定的界面,也就是说所有的页面导航/事件处理/UI/Data都是交给服务器来处理。这样,这个程序可以用来包装不同应用程序而不用改客户端的代码,对于应用的维护升级/新功能的推送也有积极的影响——只需要更新服务器端代码,无需调整客户端。

整个设计的原理图可见上图,其实原理就是那么简单,只要客户端支持从代码创建控件(Windows Phone和Android试过都可以),那么动态的解析服务器传回来的事件定义,UI Layout和Data都不是难事,再将对应的控件增加到页面上,为控件增加事件,就搞定了。所以说,在这种瘦客户端模型下,客户端只是一个负责解析XML并生成界面的渲染器,服务器端处理每一个事件。

和传统的手机客户端编程不一样的主要有两点:

1.原先是客户端知道每一个页面后续的分支页面,也就是说客户端知道下一个页面可能会是哪几个,但是在这种模式下客户端永远不清楚下一个页面是什么,依靠的是服务器端发送过来的页面。
2.UI谁来定义,在传统的手机客户端程序中,UI是写死在程序里的,但是这中模式下,UI是在服务器端,可动态配置/调整的。

这年头都喜欢做艰难的决定,那么我也赶时髦效仿下,如下是我所做的几个艰难的决定:

1. 关于事件处理到底客户端还是服务器端来做
客户端做:优点,自然是能够提高效率,毕竟客户端处理比交还给服务器处理少了网络传输的延时。缺点:如果由客户端来做,则必须把事件处理代码发送到客户端,客户端要承担动态解析并执行代码的工作,有些手机系统不允许动态load代码,而且写这套解释执行的类库会违背了这个设计的初衷:轻量级,可复用的客户端。

2.关于UI Layout Manager
本来是想实现不同的Layout Manager的,后来由于时间和精力原因就放弃了,不过应该没多大问题,增加相应的标签和属性就可以了,在这个Demo中我采用的都是绝对定位,当然,绝对定位也有好处,可以比较容易就写一个可视化的设计器用来拖拽生成代表页面的代码。

3.关于是不是有必要将整个表单发送回服务器
我是将页面元素序列化成XML后Postback到服务器,这样的话就可以知道客户端所有的状态,服务器端不需要保存客户端的浏览状态,用类似ViewState的方法来回的传输浏览状态。当然,这也是可以优化的地方,时间有限,完全发送回去比较简单。

有图有真相:

1.初始页面,点击按钮从服务器动态载入页面,其实可以忽略,在程序start的时候请求就可以了。 2.从服务器返回的XML经过渲染(解析控件和事件)之后得到如下界面。点击按钮将整个Page Postback回服务器并触发相关事件。
2010-12-24 12-25-22 2010-12-24 12-31-49
3.如果什么都不输入或者输入错误的用户名密码,提交之后在服务器端验证不通过,服务器设置Message返回登录页面(因此有红色的警告)。 3.如果输入正确的用户名密码(正确输入为用户名hackerzhou,密码随意),服务器验证通过后返回Portal Page给客户端。
2010-12-24 12-32-18 2010-12-24 12-31-30

以上是一个非常简单的例子,包含动态控件渲染和简单的事件处理,整套源代码见文章开头的链接,需要VS2010+Windows Phone SDK来运行。

一些需要注意的小细节:
1.由于用的是.NET Framework 4建立的 DynamicUIWeb ,因此在发送XML回去的时候会报“System.Web.HttpRequestValidationException: 从客户端…中检测到有潜在危险的 Request.Form 值”的异常,网上搜了下是由于出于安全考虑,禁止POST XML/HTML到WebService导致的。
网上的解决方法是在Web.config文件的system.web节点下增加如下节点:

<pages validaterequest="false" />

结果发现不起作用,看了skyaspnet的文章,发现原来是.NET 4.0请求验证模式变化导致的这句配置失效,需要再在system.web节点下增加节点来启用.NET 2.0的兼容模式

<httpruntime requestvalidationmode="2.0" />

2.我采用了.NET中的Attribute来做快捷的属性设置,Java中的话可以用Annotation来实现,反射什么的两者差不多。

CSharp获取设置的Attribute需要在获取了特定的FieldInfo之后调用FieldInfo.GetCustomAttributes来拿在此属性上设置的Attribute。

3.事件机制说明

XML里面定义了ItemAction节点,有name,url,method,paraName等属性来制定Postback到哪里,调用哪个函数,参数名是什么。Item上有actionName,指定调用哪个Action。

因此在手机客户端这边读到Item有actionName这个属性的时候就去注册对应的handler,一旦事件被出发,则将整个页面Post到特定的Web Service获取新的页面,这个页面会打上Form的名字和ActionName,指示是哪个Action的触发引起的Postback。

在服务器端接受了Postback的xml之后,找到特定的Form去实例化一个出来,用反射设置好对应的参数(我的例子里面是用户名/密码),然后调用OnAction,将解析得到的ActionName作为参数传进去,执行对应的处理逻辑。OnAction的返回是一个页面,可以导航到另一个页面。随后,序列化页面成XML传回客户端,整套事件处理流程完成。

4.关于不用Service Reference动态异步调用Web Service

昨天我写了篇文章,用来说明具体的实现,https://www.hackerzhou.me/2010/12/windows-phone-7-dynamic-asynchronous-webservice-call.html

本文基于 署名 2.5 中国大陆 许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名 hackerzhou 并包含 原文链接
发表评论

本文有 3 条评论

  1. jasonhan
    2014-02-09 22:21

    这个想法很好啊。其实和现在的跨平台移动开发框架PhoneGap很像,只不过它的模式是:html+css+js -> PhoneGap SDK -> 各个平台本地渲染UI 。随着网络带宽和手机硬件的发展,总有一天会统一开发的。

  2. 阳光牧场
    2011-11-23 09:20

    你这个理念符合当前的流行的‘云’,但是用在手机终端不现实,手机终端不像PC端流量可以浪费…

    • hackerzhou
      2011-11-23 18:37

      恩,当时是作为一个poc项目来证实一下想法的可行性。不过我觉得流量啥的以后都不会是问题,参考以前56K的电话线带宽

发表评论