Smartphone对抗 Pocket PC Phone Edition
你也许听说过Pocket PC Phone Edition,它是另一个装备微软软件的电话。为了避免混乱,我将澄清Pocket PC Phone Edition与Smartphone之间的区别。图1显示了两者的区别(如果你有兴趣查阅建立Smartphone图形界面的信息,可以查看"开发高效的Smartphone用户界面")。Pocket PC Phone Edition是一款功能全面的内建移动电话能力的Pocket PC设备。在尺寸上它与其它的Pocket PC设备相近,体积比Smartphone要大一些。Pocket PC Phone Edition要求更大的功率和更多的内存;此外,它有一个触摸屛,面积比通常的Smartphone屏幕(它不支持触摸屛)要大得多。用户与设备的交互途径也不同:Pocket PC Phone Edition使用输入笔与触摸屛交互,而Smartphone使用电话的小键盘。
图1.屏幕对比
哪一类型的设备适合你的需要取决于三个主要因素:成本、功能和使用的简便性。微软估计Smartphone的定价与其它的高端移动电话相近,比Pocket PC Phone Edition设备低。你需要的功能依赖于应用程序设计成做什么的。对于多数移动应用程序,Smartphone和Pocket PC Phone Edition执行的情况相同。但是,如果应用程序需要大量的磁盘空间和内存、附加的硬件或者大屏幕,Pocket PC Phone Edition也许是正确的选择。
无论哪种设备和在它上面运行的应用程序,都必须易于使用,要考虑数据输入的问题。首先,你必须决定使用输入笔还是小键盘,这实际上没有关系。通过菜单选项的数据输入易于被其它设备处理。但是,如果输入文本信息,Pocket PC Phone Edition明显好用一些。幸运的是两种设备都支持Windows CE API,使你能轻易地为它们建立一个基础代码。
建立Smartphone应用程序
真实的Smartphone应用程序需要传递什么?在多数移动应用程序中很多事务是共同的。它们从一个远程数据存储中检索数据、处理、保存、在本地离线处理数据、上载更改并与电话特性交互。如果你在需要时能够了解并建立这些元素,就能够从事真正的Smartphone开发了。
下一步是决定将建立的应用程序的类型:基于浏览器的(browser-based)还是独立的(standalone)。
Smartphone上的基于浏览器的方案包含能被袖珍互联网浏览器(Pocket Internet Explorer)访问的Web内容。这种类型的应用程序是已存在的内容提供商的最好选择,他们希望将出售物扩展到Smartphone所有者。Smartphone上的基于浏览器的应用程序与其它平台上的好处相同:简单的软件分发和维护,因为所有的代码在Web服务器上。当然,缺点是并不能保证始终连接到互联网。另一个问题是袖珍互联网浏览器应用程序不能与设备的内建特性交互。尽管如此,这类应用程序可以使用已经存在的(例如ASP.NET移动控件)Web开发和优化工具开发。
与Web应用程序不同,你能使用类似嵌入式(eMbedded)Visual C++的工具来开发一个定制的应用程序。幸运的是Smartphone 与Pocket PC平台相似。因为大多数API相同,使用嵌入式Visual C++为Pocket PC平台开发的应用程序能简单地移植到Smartphone上,其结果是在很大程度上节省了为两个设备开发的成本。我移植了两个应用程序,一个只需要简单地重新编译,另一个只花了结果几个小时来解决平台之间的微弱不同。
开发独立的应用程序时你有更大的灵活性,这类应用程序往往更加牢固,并能用于连接和不连接的模式。对多数商业应用,独立的应用程序也许是最好的选择。它们在离线环境下使用,也提供了根据需要在远程系统间传递数据的机制。
此外,独立应用程序也可以使用特定设备的特性,包括技术特性和访问本地数据存储,例如Pocket Outlook和Windows CE数据库(CEDB)引擎。当离线时,为了将来同步,数据可以被存储起来。但是,独立应用程序面临着更多的分发和维护问题。此外建立独立应用程序还需要C++经验,通常它的成本更高并难以找到开发者。但是这种情况不会发生了,预计微软.NET Compact Framework和Smart Device Extensions最后将支持Smartphone。如果这实现了,开发者可以使用C#和Visual Basic .NET等语言建立Smartphone应用程序。
数据检索
因为任何商业应用程序最基本的特性是检索数据,设计的部分问题围绕可用连接的类型。你能依赖持续的连接或者连接可能中断吗?你的系统只有一定的次数和访问点才需要连接吗?
Smartphone为从远程数据源检索数据提供了几种选择。一种是ActiveSync,你可以使用它手动地在桌面计算机和Smartphone之间推/拉(push/pull)文档。另一个选择是编程从PC上访问该设备并使用远程API(RAPI)复制文件。虽然这些方法完成了任务,但是不是最好的选择。Smartphone的内建互联网连接更好。
在Smartphone设备上通过互联网检索远程数据非常简单。实际上,如果你已经开发了一个通过互联网检索数据的Pocket PC应用程序,就已经知道所有需要的了。在Smartphone上与远程服务器通讯的最简单的方法是WinInet功能(HTTP或者FTP)、在远程载入数据的XMLDOM API和微软互联网浏览器(见图2)。
图2.在Smartphone上检索远程数据
XMLDOM 给Web开发人员提供了几种数据检索的方法。使用DOM你能通过两个主要的方法检索数据:文档对象和XMLHTTP对象的Load方法。两个对象都允许从远程Web服务器上的XML格式中检索数据。两者之间最大的区别是XMLHTTP对象允许你传递一个XML对象作为调用的一部分,尽管Load命令没有允许。在为了检索数据而需要传递数据的情况下,你应该使用XMLHTTP对象。
不管你在XMLDOM中使用那种方法,只需要在代码中作一点点更改。首先你必须包括正确的头文件:
#include
namespace MSXML
{
#include
}
#include
objsafe.h和ocidl.h都是必须的,因为它们包含GUID描述,而GUID是与COM一起工作的指针必须的。为了使COM调用进入MSXML分析程序,你必须建立MSXML名字空间并包含msxml.h。此外,为了项目编译正确,你应该把ole32.lib和oleaut32.lib库添加到连接程序设置中。
一旦代码使用XMLDOM,你能简单地使用DOMDocument 的load方法从远程服务器检索数据。该方法只需要一个能找到并检索数据的URL。它在文档中重复构建窗体上的一个微调控件(spinner control)(图3)。完成后它显示一个包含整个XML文档的消息框。
MSXML::IXMLDOMDocument *iXMLDoc = NULL;
MSXML::IXMLDOMParseError *pParsingErr = NULL;
MSXML::IXMLDOMElement *iXMLElm = NULL;
MSXML::IXMLDOMNodeList *iXMLChild = NULL;
MSXML::IXMLDOMNode *iXMLItem = NULL;
short tEmpty;
BSTR bStr;
VARIANT vXMLSrc;
HRESULT hr;
HWND hListBox;
long lLength;
hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
if(!SUCCEEDED(hr)) return 0;
hr = CoCreateInstance (MSXML::CLSID_DOMDocument,
NULL,CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
MSXML::IID_IXMLDOMDocument, (LPVOID *)&iXMLDoc);
if(iXMLDoc)
{
iXMLDoc->put_async(VARIANT_FALSE);
//Smartphone 2002 工作区:
//删除文档安全选项
IObjectSafety *pSafety;
DWORD dwSupported, dwEnabled;
if ( SUCCEEDED(iXMLDoc->QueryInterface(
IID_IObjectSafety, (void**)&pSafety)))
{
pSafety->GetInterfaceSafetyOptions(
MSXML::IID_IXMLDOMDocument, &dwSupported, &dwEnabled );
pSafety->SetInterfaceSafetyOptions(
MSXML::IID_IXMLDOMDocument, dwSupported, 0 );
}
VariantInit( &vXMLSrc );
vXMLSrc.vt = VT_BSTR;
iXMLDoc->put_async(VARIANT_FALSE);
vXMLSrc.bstrVal = SysAllocString(L"http:/localhost/
smartphonearticledata.xml");
hr = iXMLDoc->load(vXMLSrc, &tEmpty);
SysFreeString(vXMLSrc.bstrVal);
iXMLDoc->get_documentElement(&iXMLElm);
iXMLElm->selectNodes(L"//DATA/STATUS/S",&iXMLChild);
//iXMLElm->get_childNodes(&iXMLChild);
iXMLChild->get_length(&lLength);
hListBox = GetDlgItem( hWnd, IDC_LISTISSUES);
for (int x=0;xget_item(x,&iXMLItem);
iXMLItem->get_text(&bStr);
SendMessage (hListBox, LB_ADDSTRING, 0, (LPARAM) bStr);
}
//iXMLChild->get_item(1,&iXMLItem);
iXMLDoc->get_xml(&bStr);
MessageBox(NULL,bStr,TEXT("Article Demo"),MB_OK);
图3.XML Load方法示例
本地存储数据
在检索数据到Smartphone后,下一步是研究在设备上本地存储数据的方法。一种选择是根本不保存。你可以检索数据,执行一些操作,接着从内存中释放数据。不幸的是在大多数情况下没有这么简单,因此你不得不决定适合应用程序的格式。你也许会保存数据到文件存储(例如闪存卡),或者保存在本地的数据存储(例如CEDB)中。
对于多数商业应用程序,你需要把数据保存在文件系统中。尽管能够将数据写入RAM文件系统,但是并不推荐这样使用,因为当设备关闭或掉电时所有数据都会丢失。对于持续的存储器,Smartphone设备提供作为IPSM内存控件的闪存文件系统。在多数Smartphone上空间受到限制,但是许多设备包含一个存储卡槽,允许使用可移走的存储卡。
编程决定Smartphone上在哪儿存储数据很简单,这得感谢你在Windows CE 平台上进行开发所熟悉的一些API。这些调用可以在Windows Shell API文档中找到。现在我将聚焦于SHGetSpecialFolderPath函数。该函数允许你访问主要的Smartphone文件系统并在运行时检索完整的路径。在检索路径后,你能使用该路径存储与应用程序相关的数据。该函数需要一个CSIDL_常数作为输入参数。这些常数反映了Smartphone操作系统中的共有文件夹(应用程序数据、收藏夹、程序、开始等等)。因为这些公共区域也许在不同设备上,CSIDL常量提供了一个唯一的系统标识来识别这些目录。推荐客户应用程序在CSIDL_APPDATA常量返回目录的子目录中保存数据。例如:
#ifdef CSIDL_APPDATA
if(!SHGetSpecialFolderPath(NULL, szFolderPath, CSIDL_APPDATA, TRUE))
{
ASSERT(FALSE);
hr = HRESULT_FROM_WIN32(GetLastError());
}
#else
// Pocket PC没有这种定义来在根中读取目录。
//也许未来会作些改变,但代码仍然可以工作
_tcscpy(szFolderPath, TEXT("\\"));
#endif
我先前讲到,有些设备需要附加存储卡。与你的桌面计算机的硬盘驱动器不同,Smartphone没有为存储卡分配驱动器符号。作为代替,操作系统在根目录中建立目录来表现每块存储卡上的不同部分。为了访问一个设备中的不同卡,使用FindFirstFlashCard和FindNextFlashCard函数。如果找到了存储卡,FindFirstFlashCard返回一个指向第一块存储卡的句柄和指针。如果句柄是正确的,你可以把它传递给FindNextFlashCard,该函数将返回下一块存储卡的指针和一个BOOL值来表明搜索是否成功。
一旦你找到了希望使用的存储卡,下一步就是查找一个位置来存储数据。在访问设备上的文档时通常使用SHGetDocumentsFolder函数。它可用于新的或者原来的存储卡。下面是使用SHGetDocumentsFolder的一个例子:
TCHAR szDocPath[250];
if (!SHGetDocumentsFolder(L"\\",szDocPath))
MessageBox(NULL,L"Retrieveing Path Failure",L"Call Failure",MB_OK); else
MessageBox(NULL,szDocPath,L"Documents Folder Path",MB_OK);
模拟器笔记:Smartphone模拟器不支持存储卡。它也不支持附加在运行模拟器的PC上的真正的存储卡。
数据库操作
对于几乎所有商业应用程序来说,把数据保存到本地数据库是必要的,对于许多Smartphone应用程序也是这样。与Windows CE和Pocket PC相比, Smartphone上数据库的选择只有少许的不同。对于第一个Smartphone版本Smartphone 2002,只支持本地Windows CE数据库,目前它不支持SQL Server CE或者Pocket Access。此外访问Windows CE数据库的唯一选择是使用包含在Windows CE 3.0中的数据库API。不过访问Windows CE数据库的代码与为Windows CE设备所写的代码几乎相同。
同步数据
在使用完连接并处理数据后,下一步就是与远程数据存储同步。因为我已经介绍过使用XMLDOM通过互联网检索数据,我将继续使用该模型并将改变通过XMLHTTP对象发送回远程服务器。
在介绍使用XMLHTTP的细节前,我们看看Smartphone怎样使用Web服务。目前,在Smartphone平台上没有内建库或组件提供SOAP客户端功能。但是Smartphone 的SOAP客户端并不在可能的范围之外,因为Smartphone 2002 SDK支持所有可能需要的组件。如果Smartphone上需要SOAP客户端,我建议查看第三方库,它也许能移植到Smartphone而没有太多问题。此外你应该记得一旦Smartphone上的.NET简洁框架组件可以使用,就能使用Smartphone客户端的功能了。
从Smartphone向远程数据存储同步数据的第一步是从主存储器中检索所有改变了的数据并转换为XML格式。完成后,实例化XMLHTTP对象并把数据传递到处理同步的特定URL。
先前讲到,因为XMLHTTP对象拥有使用XML格式发送信息到远程服务器的能力,在这儿它将被使用到。XMLHTTP对象是前面的项目中包含的MSXML库中可用的另一个对象。图4是该对象的使用。它很容易使用,从IXMLHttpRequest接口中建立了一个对象。此外,建立一个VARIANT来保持IXMLDOMDocument对象。为了传递这个复杂的类型,VARIANT必须通过VT_DISPATCH类型设置。剩下的步骤是调用XMLHTTP 对象的open和send方法。
MSXML::IXMLDOMDocument *iXMLDoc = NULL;
MSXML::IXMLHttpRequest *iXMLHttp = NULL;
HRESULT hr;
BSTR bStr = NULL;
VARIANT vUserID;
VARIANT vPassword;
VARIANT vPassValue;
VARIANT vAsync;
short tEmpty;
hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
if(!SUCCEEDED(hr)) return 0;
hr = CoCreateInstance (MSXML::CLSID_DOMDocument, NULL,
CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
MSXML::IID_IXMLDOMDocument, (LPVOID *)&iXMLDoc);
if(iXMLDoc)
{
iXMLDoc->put_async(VARIANT_FALSE);
// Smartphone 2002工作区:
//删除文档安全选项
IObjectSafety *pSafety;
DWORD dwSupported, dwEnabled;
if ( SUCCEEDED(iXMLDoc->QueryInterface(IID_IObjectSafety,
(void**)&pSafety)))
{
pSafety->GetInterfaceSafetyOptions(
MSXML::IID_IXMLDOMDocument, &dwSupported, &dwEnabled );
pSafety->SetInterfaceSafetyOptions(
MSXML::IID_IXMLDOMDocument, dwSupported, 0 );
}
iXMLDoc->put_async(VARIANT_FALSE);
iXMLDoc->loadXML(L"Joe
Smith", &tEmpty);
}
VariantInit(&vAsync);
vAsync.vt = VT_BOOL;
vAsync.boolVal = false;
VariantInit(&vUserID);
vUserID.vt = VT_BSTR;
vUserID.bstrVal = L"";
VariantInit(&vPassword);
vPassword.vt = VT_BSTR;
vPassword.bstrVal = L"";
VariantInit(&vPassValue);
vPassValue.vt = VT_DISPATCH;
vPassValue.pdispVal = iXMLDoc;
hr = CoInitializeEx(NULL,COINIT_MULTITHREADED);
if(!SUCCEEDED(hr)) return 0;
hr = CoCreateInstance (MSXML::CLSID_XMLHTTPRequest, NULL,
CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER,
MSXML::IID_IXMLHttpRequest, (LPVOID *)&iXMLHttp);
iXMLHttp->open(L"GET", L"http://localhost/smartphonesavetest.asp",
vAsync,vUserID,vPassword);
hr=iXMLHttp->send(vPassValue);
iXMLHttp->get_responseText(&bStr);
图4 XMLHTTP例子
访问电话特性
通过Windows CE API,开发人员能简单地给应用程序添加电话功能。类似自动拨号、与调用日志交互、访问SIM卡的能力、发送和接收SMS消息等等特性可以简单地添加到应用程序。为了工作正常,大多数技术API需要真实的设备或者GSM无线设备连接到模拟器,因此如果你计划开发这些类型的特性,准备好测试真实的设备。
电话特性的一个最简单的例子是在应用程序中建立语音通话。因为我们有随着Smartphone一起发布的辅助TAPI,只需要一行代码。所有需要作的是在代码中包含astdtapi.h文件,在连接程序设置中添加cellcore.lib,并调用tapiRequestMakeCall函数:
TCHAR szDefaultNum[] = TEXT("+1 (555) 555-5555");
LONG lResult;
lResult = tapiRequestMakeCall((LPTSTR) szDefaultNum, NULL, NULL, NULL);
return TRUE;
发送SMS消息也很简单。有了辅助TAPI,你只需要在代码中添加一个文件引用(sms.h),在连接程序设置中添加一个库(sms.lib)。发送或者接收消息的第一步是调用SmsOpen函数,下一步初始化源和目的地址,完成后调用SmsSendMessage函数。最后一步是调用SmsClose函数清除建立的SMS_HANDLE。代码见图5。
SMS_HANDLE smshHandle;
SMS_ADDRESS smsaSource;
SMS_ADDRESS smsaDestination;
TEXT_PROVIDER_SPECIFIC_DATA tpsd;
SMS_MESSAGE_ID smsmidMessageID;
//尝试打开一个SMS句柄
if(FAILED(SmsOpen(SMS_MSGTYPE_TEXT, SMS_MODE_SEND, &smshHandle, NULL)))
{
MessageBox(NULL,L"Call to SmsOpen failed.",L"Error",MB_OK |
MB_ICONERROR);
return;
}
//建立源地址
if(!bUseDefaultSMSC)
{
smsaSource.smsatAddressType = SMSAT_INTERNATIONAL;
_tcsncpy(smsaSource.ptsAddress, lpszSMSC, SMS_MAX_ADDRESS_LENGTH);
}
//建立目标地址
smsaDestination.smsatAddressType = SMSAT_INTERNATIONAL;
_tcsncpy(smsaDestination.ptsAddress, lpszRecipient, SMS_MAX_ADDRESS_LENGTH);
//建立提供者特定数据
tpsd.dwMessageOptions = bSendConfirmation ? PS_MESSAGE_OPTION_STATUSREPORT
: PS_MESSAGE_OPTION_NONE;
tpsd.psMessageClass = PS_MESSAGE_CLASS1;
tpsd.psReplaceOption = PSRO_NONE;
//发送消息,显示源或者失败
if(SUCCEEDED(SmsSendMessage(smshHandle, ((bUseDefaultSMSC) ? NULL :
&smsaSource),
&smsaDestination, NULL, (PBYTE) lpszMessage,
_tcslen(lpszMessage) * sizeof(TCHAR), (PBYTE) &tpsd,
sizeof(TEXT_PROVIDER_SPECIFIC_DATA), SMSDE_OPTIMAL,
SMS_OPTION_DELIVERY_NONE, &smsmidMessageID)))
{
MessageBox(NULL, L"Message sent", L"Success",MB_OK);
}
else
{
MessageBox(NULL, L"SmsSendMessage Failed", L"Error",MB_OK |
MB_ICONERROR);
}
//清除
VERIFY(SUCCEEDED(SmsClose(smshHandle)));
图5.SMS代码例子
它还提供了更多的API,我只介绍了几个基本的。有了这些能力,移动应用程序在原来的基础上有了很大的扩充。例如,当应用程序中特定事件发生时能够触发自动呼叫或消息。
结论
本文我讨论了建立真实Smartphone应用程序的一些基础。你可以看到该类型的Smartphone应用程序在企业计算领域是非常引人注目的。
作者:陶刚 来源:赛迪网
http://www.biplip.com/Default.aspx?tabid=34&mid=348&ctl=View&ItemID=281