随着移动互联网的迅猛发展,关于移动客户端技术解决方案的讨论越来越多,本系列文章将试图针对移动客户端开发中的服务器端开发,提供一个.NET平台的框架解决方案。
由于本文是探讨针对.Net服务端编程,所以理论上与手机端平台无关,但为了方便描述,本文所提供的例子均为Android平台,服务端编程语言使用C#。
本文将计划以10篇左右的篇幅,从架构、逻辑、实现三个方面对框架进行阐述,其中最后3篇计划做一个例子,实现框架的一些基本的功能。虽然本框架在作者的团队中已经基本成熟,且有一个Android应用已经接近开发完成,但由于一些原因作者不可能将框架开源出来,所以对完全开源有所期待的读者说声抱歉,但最后的例子作者将会开源,它将是一个NHM Lite,具备完整的架构和简洁的功能,方便读者拓展。
由于作者才疏学浅,在编写此文的过程中难免疏漏,还请各位指教。
目前的移动客户端开发,但凡涉及到与服务器频繁交互数据的应用,如:微博、人人网、招商银行等,都要考虑到如何传输数据以及如何在手机上展示这样两个问题,而对此,当前主要有基于手机API和WebKit这样两种技术路线,在进行接下来的内容之前,我们不妨先对这两种技术进行一下简单的分析:
(一)手机API(典型应用:微博)
原理:手机端使用手机API,如AndroidAPI,进行开发,服务端只是一个数据提供者,如JSON。手机端接到JSON后将JSON反序列化成对象,进行逻辑处理,再在View层进行展示。当然你也可以不用JSON,用XML、甚至你自己能读懂的某种字符串如commaString(逗号分割的字符串),虽然这一切都没有JSON在JAVA中来的方便,本文就将使用JSON。这种方式,相当于传统开发中的C/S模式,如图:
优缺点:这种方式的优点在于,手机端开发更为灵活,可以应用手机API提供的所有API,可以对手机进行底层的控制,如:可以使用系统提供的更炫的UI、很方便的使用摄像头、播放视频、拨打电话、调用联系人、发送短信(虽然后三者在android平台用webkit的方式也可以比较方便的实现,但这里讲的是针对全平台的思路)……这种方式的缺点也显而易见,手机端开发周期长、可移植性差、无法跨平台,即使你之前已经有了很强大的WEB应用,针对手机客户端这部分WEB端也可能要重新开发。
服务器角色:在这种方式中,WebServer所扮演的是数据提供者的角色,它处理手机客户端的请求,并将请求通过业务逻辑层的处理生成客户端要求的JSON回发到客户端,于是:视图层仅仅是显示JSON而已,没有Jquery、没有Ajax、甚至没有HTML。可能读者要问了,这样服务端的显示岂不是很简单?即使重新开发也不会很复杂啊!说显示简单也许是对的,但说逻辑不复杂,也许就不尽然。虽然视图很简单,但你依然要处理:业务逻辑、异常、传输加密、登陆注册、用户访问权限……,还要与你现有网站的数据进行整合(如果有的话),与重新做一个小的WAP网站无太大区别。
本系列文章将使用这一种方式进行阐述,在这个系列的文章完成后,下一个系列作者将会关注“PhoneGap”和“JqueryMobile”,到时将采用下面一种方式进行讲解,由于现在为时尚早,且不说“敬请期待”。
(二)Webkit(典型应用:招商银行)
原理:由于目前的几大智能手机平台都支持WebKit,所以可以遵循Webkit的标准为手机客户端开发跨平台的网站应用,这时手机端仅仅是一个包了浏览器外壳的简单程序,这个外壳通过访问Web服务器,获得HTML流,并将HTML用支持WebKit的浏览器控件解析(如Android的WebView),从而实现界面的展现。另外在Android中,还可以“重载”JavaScript方法,获得更接近Android原生程序的用户体验,如将JS中的alert”转换”成Android的AlertDailog,再比如解析链接中的"tel: ”调用拨号界面,等等。但互动效果依然有限。这种方法相当于传统开发中的B/S模式,如图:
优缺点:WebKit方式的优缺点与API方式更好相反,广义的讲,WebKit无法提供手机原生UI、对手机硬件的控制能力有限,导致程序交互性较差;由于视图层依赖于Web服务端,所以程序会更加依赖网络,可能更加耗费流量;但WebKit方式的优点同样令人着迷:跨平台、手机端开发周期很短、如果你已经有了很强大的WEB应用,开发WebKit或许就仅仅是做一个新的视图层那么简单。对于最后一点,也许我该多说几句。对于JSON方式,由于要使用JSON输出,你就要重新构造一个各种类转换成必要JSON的逻辑,比如我要在手机客户端中显示一个用户的若干信息,我需要让服务器传一个User Json给客户端,那这个JSON就要在服务器重新构造,那怎么构造呢?BLL要改、DAL同样要改,这个不是个简单的事情!当然,你也可以增加一个序列化对象为JSON的通用静态方法,但这样的方式往往并不是万能。首先通用静态方法只能处理简单的对象,当一个对象中包含另一个需要被序列化的对象,那通用方法则未必成功;又比如,你需要一些转换,比方说username属性,输出时改名为uid(为了给客户端节约点流量嘛),那还是需要为每一个类增加对应的序列化方法,还是要在BLL、DAL动手脚,延长开发周期。
服务器角色:WebKit方式的服务器角色,就不仅仅是数据提供者那么简单,还需要提供完整的HTML视图,似乎看来,相比第一种方式,是加了视图层,但是从某种角度说,添加视图却正好实现了对之前业务逻辑的复用,开发反而更为简单。当然或许你没有“之前”的项目,一切从零开始,就像我这个项目一样,我想说,那也不错,从一开始就考虑到这样两种实现方式,会让你有更宽阔的思路设计你的项目架构,以便应对和适应将来可能发生的一些转变。
比如,招商银行这个项目,之所以选择了WebKit方式,正是因为对于网上查询、缴费这些功能,招行已经有了一个很完善的Web程序,包括了必要的业务逻辑和安全性、证书、加密等机制,现在要开发手机客户端,要把视图层放到手机端,想想都是一个太过庞大的工程,但是有了WebKit,就可以把视图层依然留在Web服务器,而客户端看起来又不是那么山寨。(此项目与作者无关,作者仅作为例子说明文中观点)
由于本文将采用JSON方式继续阐述,那我想有必要对WebKit方式进行一个小的梳理,举一个例子,分析一下工作方式,也让读者心中有数。
例1-1:设计一个登陆界面,使用WebKit的方式,要求尽量优化用户体验。
实现:
手机端:
程序结构:
WebViewActivity.java:
它就是显示WebView的Activity了,通过重载webview的一些方法,实现了与Android原生UI的互动。
12345678910111213141516171819mWebView.setWebChromeClient(
new
WebChromeClient() {
@Override
public
boolean
onJsAlert(WebView view, String url, String message,
final
android.webkit.JsResult result) {
new
AlertDialog.Builder(WebViewActivity.
this
)
.setTitle(
getResources()
.getString(R.string.setting_title))
.setMessage(message)
.setPositiveButton(android.R.string.ok,
new
AlertDialog.OnClickListener() {
public
void
onClick(DialogInterface dialog,
int
which) {
result.confirm();
}
}).setCancelable(
false
).create().show();
return
true
;
};
});
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162mWebView.setWebViewClient(
new
WebViewClient() {
@Override
public
boolean
shouldOverrideUrlLoading(WebView view, String url) {
// view.loadUrl(url);
if
(url.startsWith(
"tel:"
)) {
Intent myIntentDial =
new
Intent(
"android.intent.action.DIAL"
, Uri.parse(url));
startActivity(myIntentDial);
}
else
{
view.loadUrl(url);
}
return
true
;
}
@Override
public
void
onPageFinished(WebView view, String url) {
// TODO Auto-generated method stub
super
.onPageFinished(view, url);
process.setVisibility(View.GONE);
}
@Override
public
void
onReceivedError(
final
WebView view,
int
errorCode,
String description, String failingUrl) {
// TODO Auto-generated method stub
// Toast.makeText(getBaseContext(),
// R.string.NoRouteToHostException, Toast.LENGTH_SHORT)
// .show();
new
AlertDialog.Builder(WebViewActivity.
this
)
.setTitle(getResources().getString(R.string.error))
.setMessage(R.string.NoRouteToHostException)
.setCancelable(
false
)
.setPositiveButton(
getResources().getString(R.string.ok),
new
DialogInterface.OnClickListener() {
public
void
onClick(DialogInterface dialog,
int
id) {
if
(view.canGoBack()) {
view.goBack();
}
else
{
WebViewActivity.
this
.finish();
}
}
})
.show();
super
.onReceivedError(view, errorCode, description, failingUrl);
}
@Override
public
void
onLoadResource(WebView view, String url) {
return
;
}
});
此外,还可以通过传入不同的cmd参数来让webview读取对应的固定信息:
12345678910switch
(cmd) {
case
ServiceConst.WEB_GET_ABOUT:
String abouthtml = ResourcesUtils.getFromRaw(
this
, R.raw.about);
mWebView.loadData(abouthtml,
"text/html"
,
"UTF-8"
);
break
;
case
ServiceConst.WEB_GET_URL:
mWebView.loadUrl(getIntent().getStringExtra(
"url"
));
break
;
}
服务器端:
webkit的服务器端就是彻底的网站程序了,这里仅贴出.aspx文件,供读者参考:
123<
script
type
=
"text/javascript"
>
$(function () {
$("#Login").click(function () {
1234567891011121314151617181920212223242526272829303132333435363738394041424344
$.ajax({
type: "POST", //提交方式
url: "/ajax/LoginHandler.ashx", //提交的handler
data: GenInputParams(), //处理页面参数的JS方法
beforeSend: function (XMLHttpRequest) {
$('#Login').attr('disabled', true); ;
$('#Login').text("正在登录...");
},
success: function (msg) {
$('#Login').attr('disabled', false); ;
if (msg == "success") {
alert("登录成功");
window.location.reload();
}
},
error: function (xhr, msg, e) {
var emsg = eval('(' + xhr.responseText + ')');
$('#tipsTypeDiv').text(emsg.ErrCode);
$('#Login').attr('disabled', false); ;
alert(emsg.ErrMsg);
}
});
});
});
</
script
>
<
asp:Panel
ID
=
"pnGuest"
runat
=
"server"
>
用户名:
<
input
type
=
"text"
id
=
"username"
class
=
"inp"
/><
br
/>
密码:
<
input
type
=
"password"
id
=
"password"
class
=
"inp"
/><
br
/>
<
div
>
<
input
type
=
"hidden"
id
=
"method"
value
=
"shopowner"
class
=
"inp"
/><
input
type
=
"button"
id
=
"Login"
value
=
"登陆"
class
=
"inp"
/>
</
div
>
</
asp:Panel
>
<
asp:Panel
ID
=
"pnLogged"
runat
=
"server"
>
<
div
>
<
asp:Label
ID
=
"uname"
runat
=
"server"
Text
=
"Label"
></
asp:Label
>,欢迎您</
div
>
<
asp:Button
ID
=
"btnExit"
runat
=
"server"
Text
=
"退出"
onclick
=
"btnExit_Click"
class
=
"inp"
/>
</
asp:Panel
>
在这一章中,我们主要探讨了当前主流手机客户端的两种实现方式,当然,在这个问题上,还有更多可以探讨。如:广义上说WebKit不能控制手机底层的硬件,如摄像头,但第三方框架如:PhoneGap已经可以实现对摄像头、GPS模块等操作,也可以开发出互动性不错的App程序。但这些似乎都不属于本文索要探讨的范围。而且似乎,有点跑题了,第一章读完,好像都没有给读者朋友介绍我们这个框架的基本功能,好吧,先看图。
通过截图,大家都可以对该框架的功能进行大略的了解,接下来的几篇,将进入重点,开始研 |