[原创]用.Net打造一个移动客户端(Android/IOS)的服务端框架NHM(一)
作者:洪洋
本文的目的
随着移动互联网的迅猛发展,关于移动客户端技术解决方案的讨论越来越多,本系列文章将试图针对移动客户端开发中的服务器端开发,提供一个.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的互动。
mWebView.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; }; });
mWebView.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读取对应的固定信息:
switch (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文件,供读者参考:
<script type="text/javascript"> $(function () { $("#Login").click(function () {$.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程序。但这些似乎都不属于本文索要探讨的范围。而且似乎,有点跑题了,第一章读完,好像都没有给读者朋友介绍我们这个框架的基本功能,好吧,先看图。
通过截图,大家都可以对该框架的功能进行大略的了解,接下来的几篇,将进入重点,开始研究我们的Nhm框架,敬请期待吧。 |