分享
 
 
 

开发SmartPhone应用程序以及调用WebService

王朝other·作者佚名  2006-01-09
窄屏简体版  字體: |||超大  

Orange SPV是第一款进入市场的运行Smartphone 2002的移动电话,现在我们可以开始开发Smartphone应用程序了。本文我们建立了一个可以使用ASP.NET Web服务的Smartphone客户端来提供地理信息。本文假定读者有Web服务、ASP.NET和Win32编程的基本知识。

.NET框架组件使我们有了一组类帮助使用Web服务,从分析WSDL文档到自动建立代理类再到程序依赖。这使大多数依靠Web服务的编程比较琐碎,可是对于Smartphone来说简单框架组件(Compact Framework)还不能使用,因此我们需要作其它的选择。

典型的COM能够使用Soap工具包,对Smartphone来说它也不能使用,但是我们将使用它来帮助跟踪调用。

我们可以依靠Web服务编程仍然不使用这些技术,为了实现它,我们需要查看Web服务的格式和传送。在.NET 1.0 Web服务中使用两种标准来通讯:SOAP和WSDL。

Web服务基础

如果没有基于SOAP的API帮助使用Web服务,我们就需要直接处理SOAP消息和WSDL文档指定的格式。请注意使用WSDL使用的好处之一是类型元素中的概要信息。这些信息指定了SOAP正文的格式,启动WSDL察觉客户端(例如.NET)来定义SOAP包可以进行串并转换的类。

对于Smartphone来说,我们必须构造自己的SOAP消息以确保它们可以被Web服务接受。这些消息通过HTTP POST发送到服务器,我们的客户端等待SOAP响应。这需要按次序分析、检查soap错误,返回提取的值。看起来这需要大量的工作,但是在实际中它与Soap 工具包提供的低层API相近。

手动地从纯WSDL构造SOAP消息是复杂的事务(如map.wsdl显示)。这需要定位正确的操作元素,找到输入消息,映射正确类型。如果Web服务用的是ASP.NET,简单的途径是查看产生的asmx页,选择Webmethod,并查看SOAP请求示例(图1)。

图1 从ASP.NET产生的Web服务文档

如果你不能访问这些信息,有另一个机制用于支持WSDL的客户端调用Web服务,并跟踪SOAP包。SOAP工具包提供了一个工具Trace Utility(MsSoapT3.exe),它对调试Web服务很有用。

图2 Trace Utility,来自Soap工具包3.0

Smartphone Web服务

在上面的请求中很容易看出怎样构造SOAP消息。在这个例子中,我建立了一个SoapWriter类提供写SOAP消息的低层功能。

#include "Soap.h"

SoapWriter *pSoap = new SoapWriter();

pSoap->StartEnvelope();

pSoap->StartBody();

pSoap->StartElement(L"GetLatLong", L"http://mapmobile");

pSoap->WriteElementString(L"addressLine", L"new bond street");

pSoap->WriteElementString(L"city", L"bath");

pSoap->StartElement(L"postCode");

pSoap->EndElement(L"postCode");

pSoap->WriteElementString(L"country", L"UK");

pSoap->EndElement(L"GetLatLong");

pSoap->EndBody();

pSoap->EndEnvelope();

pSoap->FinalizeSoap();

为了向Web服务发送SOAP请求,我将使用WinInet API。WinInet一般用于HTTP和FTP通讯。它为Internet访问提供了高层次API而没有采用WinSock 编程。幸运的是Web服务使用SOAP调用,在我们的例子中使用HTTP调用。

为了使发送SOAP请求更加容易,我建立了SoapConnector类,它允许我们发送SOAP请求和载入响应信息。一旦连接到了Web服务,客户端就可能作多个调用,使用存取程序检索SOAP响应信息。

#include "Soap.h"

SoapWriter *pSoap = new SoapWriter();

//在此处建立SOAP 请求

SoapConnector *pCon = new SoapConnector();

pCon->Init();

//连接到服务器和Web服务

pCon->Connect(L"http://chungw02:8080/mapmobile/map.asmx");

//同SOAP 消息和SoapAction 一起调用

pCon->Invoke(pSoap, L"http://mapmobile/GetLatLong");

//提取响应信息

int iLen = 0;

pCon->GetSoapLength(&iLen);

TCHAR *pResponse = new TCHAR[iLen+1];

pCon->GetSoap(&pResponse);

客户端接收到的XML文件接着被载入DOM分析器并返回提取的值。本例中,我建立了一个将msxml中的IXMLDOMDocument接口暴露的包装类,这需要COM存在。

#include "Soap.h"

//读取SOAP 响应信息

pSoapReader = new SoapReader();

pSoapReader->Init();

//通过传递SoapConnector或者Xml字符串载入SOAP响应信息

pSoapReader->LoadXml(pCon);

一旦它被载入了,可以通过m_pDom成员访问DOM,选择节点值或运行XPath查询。

MSXML::IXMLDOMNode *pNode = NULL;

MSXML::IXMLDOMNodeList *pNodeList = NULL;

MSXML::IXMLDOMNode *pTextNode = NULL;

TCHAR *lpNodeValue = NULL;

TCHAR *XPath = new TCHAR[50];

_tcscpy(XPath, L"/soap:Envelope/soap:Body/Node");

VARIANT vNodeVal;

HRESULT hr;

//使用前面建立的pSoapReader

try {

//选择节点

hr = pSoapReader ->m_pDom->selectSingleNode(XPath, &pNode);

if (FAILED(hr))

__leave;

if (pNode == NULL)

__leave;

//获取子节点

hr = pNode->get_childNodes(&pNodeList);

if (FAILED(hr))

__leave;

//获取下一个节点

hr = pNodeList->get_item(0, &pTextNode);

if (FAILED(hr))

__leave;

//获取文本节点值

VariantInit(&vNodeVal);

hr = pTextNode->get_nodeValue(&vNodeVal);

if (FAILED(hr)) {

VariantClear(&vNodeVal);

__leave;

}

//将值指定给lpNodeValue

lpNodeValue = TCHAR[SysStringLen(vNodeVal.bstrVal) + 1];

_tcscpy(lpNodeValue, vNodeVal.bstrVal);

VariantClear(&vNodeVal);

//使用lpNodeValue处理事务

}

__finally {

if (pNode != NULL)

pNode->Release();

if (pNodeList != NULL)

pNodeList->Release();

if (pTextNode != NULL)

pTextNode->Release();

if (lpNodeValue !=NULL)

delete[] lpNodeValue;

}

作为选择,我提供了一个辅助方法SelectSingleTextNode来提取文本节点。它分配了TCHAR大小空间来适应文本节点的值。

//注意这些有辅助方法SelectSingleTextNode分配

TCHAR *latitude = NULL;

TCHAR *longitude = NULL;

//使用前面建立的pSoapReader

pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/GetLatLongResponse

/latitude", &latitude);

pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/GetLatLongResponse

/longitude", &longitude);

//使用返回值处理事务

//清除

if (latitude != NULL)

delete[] latitude;

if (longitude != NULL)

delete[] longitude;

即将调用的Web服务返回包含文本节点数据的SOAP消息,因此SelectSingleTextNode方法提供了从Web服务调用中提取数据的一个有用的机制。如果你经常需要提取数据的不同方式,例如属性数据和评估节点集合,SoapReader类可以扩充包含其它的辅助方法。

到此为止SoapWriter、SoapConnector和SoapReader提供了构造、调用和从Web服务读取响应信息的功能。为了用这些类实现客户端,我们需要了解SOAP消息的格式。下面将看到建立Web服务和Smartphone客户端。

MapMobile和MapPoint .NET Web服务

例程包含一个能从Web服务检索地图信息并在Smartphone上的显示的客户端。地理搜索和地图数据由MapPoint .NET提供,它是一个商用Web服务。尽管Smartphone终端用户并没有MapPoint .NET帐号,我建立了一个叫MapMobile的包装Web服务来依靠MapPoint .NET进行调用和认证。对于Smartphone 2002来说没有摘要(digest)认证提供者,因此如果我要直接与MapPoint .NET对话,需要手工实现认证头(header)。包装的其它原因是包含了改变地理数据提供者的能力,而不需要影响终端用户,例如从MapPoint .NET 2.0升级到3.0。

图3. MapMobile结构

我们的包装服务暴露了两个简单的Web服务方法,使客户端能够定位和显示地图。第一个调用返回地址的经度和纬度,第二个调用返回包含匹配的GIF图象的字节数组。

[WebMethod]

[SoapHeader("_phoneNumber", Direction=SoapHeaderDirection.In)]

public void GetLatLong(string addressLine, string city, string postCode, string

country, out double latitude, out double longitude)

[WebMethod]

[SoapHeader("_phoneNumber", Direction=SoapHeaderDirection.In)]

public byte[] GetMap(double latitude, double longitude, double zoom, int width,

int height, string tag)

为了防止MapMobile Web服务的无限制调用,所有的调用都包含一个SOAP头,它包含了Smartphone的电话号码。在服务器端,检测头信息是否为有效的数字。如果失败了,SOAP故障将返回客户端。这种认证机制只供例子使用,不禁止有效号码的欺骗。由于提供了很多安全选项,它们依靠你的Web服务,你可以选择不认证,使用数字署名,或使用新全局XML Web服务结构(Global XML Web Services Architecture,GXA)。

SmartMap客户端

客户端应用程序提供了一个输入屏幕用于输入地址。接着调用MapMobile Web服务并检索存储在文件系统中的地图。这使用户可以查看下载的最新地图。

图4. SmartMap用户界面

我们的客户端应用程序包含一个启动屏幕(主窗体类)和三个对话框(输入屏幕、Web服务调用状态和地图显示)。起初启动屏幕显示,跟着显示Find Location对话框。设备电话号码也被提取和保存,用于soap头中。在仿真程序中这些调用不会工作,因此在我们的例子中,如果调用失败就将数字设为1234567890以供测试。

TCHAR *g_PhoneNumber = new TCHAR[40];

SMS_ADDRESS pAddr;

//获取电话号码

SmsGetPhoneNumber(&pAddr);

_tcscpy(g_PhoneNumber, pAddr.ptsAddress);

当用户选择Search菜单选项,应用程序为MapMobile Web服务构造正确的SOAP调用。这包括包含设备电话号码的SOAP头。

SoapWriter *pSoap = new SoapWriter();

sw->StartEnvelope();

//构造头

sw->StartHeader();

sw->StartHeaderElement(L"MapMobileHeader", L"http://mapmobile");

sw->WriteHeaderElementString(L"PhoneNumber", g_PhoneNumber);

sw->EndHeaderElement(L"MapMobileHeader");

sw->EndHeader();

//Soap正文

sw->StartBody();

//此处构造SOAP正文

sw->EndBody();

sw->EndEnvelope();

sw->FinalizeSoap();

这完成后SoapConnector将被载入SoapReader中用于提取64位编码数据。

TCHAR *map64;

//分配内存到目标变量

hrSearch =

pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/GetMapResponse/GetMap

Result", &map64);

我们需要不断检查HRESULT,如果SoapReader失败了,就检查响应信息是否包含Soap故障。

TCHAR &soapfault;

hrSearch =

pSoapReader->SelectSingleTextNode(L"/soap:Envelope/soap:Body/soap:Fault

/faultstring", &soapfault);

if (SUCCEEDED(hrSearch))

MessageBox(hDlg, soapfault, L"Soap Error", MB_OK | MB_ICONWARNING);

else

MessageBox(hDlg, L"Call failed", L"Error", MB_OK | MB_ICONWARNING);

图5. Soap故障传回客户端

如果所有调用成功完成,64位字符串将解码成为一个字符数组并作为SmartMap.gif保存。我们希望该文件在电话关闭也保存在My Documents文件夹中。但是固化存储器的文件结构可以被OEM的改变,因此调用SHGetSpecialFolderPath API来检索这些信息。

CHAR szMapFile[MAX_PATH];

SHGetSpecialFolderPath(hwnd, szMapFile, CSIDL_PERSONAL , FALSE);

_tcscat(szMapFile, L"\\SmartMap.gif");

//在Orange SPV上 szMapFile包含在 \IPSM\My Document\SmartMap.gif

//在模拟器上为: \My Document\SmartMap.gif

最后建立map对话框,它包含一个图象控件(IDC_IMAGE)。

图6.选择其它数据

当该对话框载入后,保存的地图图象将从文件系统中读入并且STM_SETIMAGE传递到对话框句柄。该图象使用IMGDECMP.dll图象库载入,它时CE 3.0平台的一部分。

HBITMAP g_hbm = NULL;

ReadPictureFile(hDlg, szMapFile, & g_hbm);

SendDlgItemMessage(hDlg, IDC_IMAGE, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM) g_hbm);

图7.选择其它数据

这时SmartMap客户端几乎已经完成。如果可能的话,用户需要能查看最新地图。为了实现功能,使用了一个弹出菜单,在Find Location对话框的Option按钮中。如果文件存在就会捕获一个WM_INITMENUPOPUP消息,由此弹出菜单被激活或者禁止。

//菜单弹出前调用

case WM_INITMENUPOPUP:

{

//查看是否文件存在和是否激活最新地图菜单

DWORD dwFile = GetFileAttributes(szMapFile);

BOOL bEnabled = (dwFile == 0xFFFFFFFF ? FALSE : TRUE);

EnableMenuItem((HMENU)wParam, ID_EXIT_LASTMAP, MF_BYCOMMAND |

(bEnabled?MF_ENABLED:MF_GRAYED));

break;

}

如果用户可以选择该菜单,接着map对话框将再次被建立,它从文件系统自动载入图象。

总结

我曾经查阅过Web服务、工具和技术所包含的内容来帮助查看SOAP包。使用提供的例程,我们看到了在Smartphone 2002上,在例子MapPoint .NET中使用Web服务的一条途径。通过建立Web服务察觉(service-aware)客户端,我们获得了工作于混合连接环境,连接和断开连接,合计和储存数据的移动设备上的新一代应用程序的好处,并提供了新的经验。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有