Symbian OS中的消息存儲(chǔ)與常用操作
說(shuō)明:本文前面消息的基本知識(shí)主要參考《Series60應(yīng)用程序開(kāi)發(fā)》中的有關(guān)內(nèi)容,后面是前段做MTM開(kāi)發(fā)中用到的代碼。
一、消息存儲(chǔ)基本知識(shí)
Symbian OS提供的消息傳送架構(gòu)基于Client/Server機(jī)制,服務(wù)器負(fù)責(zé)管理手機(jī)上的各種消息,在進(jìn)行消息相關(guān)操作之前我們需要了解Symbian OS是如何組織和存儲(chǔ)消息的。
手機(jī)中的各種消息都是以數(shù)據(jù)項(xiàng)(Entry)形式供程序操作,數(shù)據(jù)項(xiàng)有4種類型,SymbianOS為每種數(shù)據(jù)項(xiàng)提供了相應(yīng)的常量標(biāo)識(shí)UID,這些UID保存在msvuids.h文件中:
Ø 文件夾類型,,對(duì)應(yīng)常量UID為KUidMsvFolderEntry,和PC上的文件系統(tǒng)一樣,每個(gè)文件夾可以包含其它數(shù)據(jù)項(xiàng)也可能是其它數(shù)據(jù)項(xiàng)的子數(shù)據(jù)項(xiàng)。
Ø 消息類型,對(duì)應(yīng)常量UID為KUidMsvMessageEntry,它表示該數(shù)據(jù)項(xiàng)是一條消息。
Ø 附件類型,對(duì)應(yīng)常量UID為KUidMsvAttachmentEntry,它表示該數(shù)據(jù)項(xiàng)是某條信息的附件。
Ø 服務(wù)類型,對(duì)應(yīng)常量UID為KUidMsvServiceEntry,服務(wù)數(shù)據(jù)項(xiàng)包含某個(gè)消息服務(wù)的配置信息,在一般情況還擁有通過(guò)該服務(wù)收發(fā)的消息數(shù)據(jù)項(xiàng)。
除了上面提到的四種類型UID還有常用到的UID是KUidMsvRootEntry(msvids.h),它指的是根數(shù)據(jù)項(xiàng),根數(shù)據(jù)項(xiàng)包含了4個(gè)標(biāo)準(zhǔn)文件夾數(shù)據(jù)項(xiàng),分別是收件箱(KMsvGlobalInBoxIndexEntryId)、發(fā)件箱(KMsvGlobalOutBoxIndexEntryId)、草稿箱(KMsvDraftEntryId)和已發(fā)送項(xiàng)(KMsvSentEntryId),另外根數(shù)據(jù)項(xiàng)下面還包含有各種消息服務(wù)的服務(wù)項(xiàng),Symbian OS中消息存儲(chǔ)如下圖所示:
Symbian OS中的消息服務(wù)器負(fù)責(zé)保存各種類型的數(shù)據(jù)項(xiàng),這里有兩個(gè)基本概念需要了解:消息存儲(chǔ)和消息索引。消息存儲(chǔ)保存了數(shù)據(jù)項(xiàng)的數(shù)據(jù),保存的數(shù)據(jù)格式取決于消息服務(wù),服務(wù)數(shù)據(jù)項(xiàng)使用消息存儲(chǔ)保存服務(wù)配置信息,文件夾數(shù)據(jù)項(xiàng)不使用消息存儲(chǔ)(即存儲(chǔ)為空),Symbian 提供了CMsvStore類來(lái)訪問(wèn)數(shù)據(jù)項(xiàng)的消息存儲(chǔ);為了節(jié)省內(nèi)存和快速檢索消息,消息服務(wù)器把數(shù)據(jù)項(xiàng)的一些概要信息(標(biāo)題,日期,類型,ID等)寫到消息索引中,當(dāng)消息服務(wù)器啟動(dòng)時(shí)將索引裝載到RAM中直到消息服務(wù)器關(guān)閉,Symbian提供了TMsvEntry類表示數(shù)據(jù)項(xiàng)的索引。
操作消息常用的類和數(shù)據(jù)類型:
CMsvSession
該類代表客戶端(客戶端MTM、用戶接口MTM或者客戶端消息應(yīng)用程序)與消息服務(wù)器端的通訊通道即C/S架構(gòu)中客戶端與消息服務(wù)器的會(huì)話。每一個(gè)客戶端線程對(duì)應(yīng)一個(gè)該類的實(shí)例,使用它獲得下面將要提到的CMsvEntry上下文對(duì)象。一個(gè)消息客戶端應(yīng)用必須在正常使用任何MTM或CMsvEntry對(duì)象前,使用OpenSyncL()或者OpenASyncL()來(lái)新建一個(gè)session對(duì)象。
CClientMtmRegistry
Registry掌握了客戶端所有目前可用的MTM有關(guān)的細(xì)節(jié),消息客戶端可以使用該類獲得從CBaseMtm繼承的對(duì)象。
CBaseMtm
這個(gè)類主要用來(lái)操作消息條目,比如可以新建、修改、發(fā)送消息條目。
TMsvId
它只是一個(gè)TInt32的typedef,消息服務(wù)器為每個(gè)數(shù)據(jù)項(xiàng)分配一個(gè)惟一的數(shù)值做為標(biāo)識(shí),除了上面提到的幾個(gè)固定的標(biāo)識(shí),其它的標(biāo)識(shí)都是動(dòng)態(tài)分配的。想要對(duì)某個(gè)消息進(jìn)行操作必須先得到它的ID,Symbian中消息相關(guān)的大部分函數(shù)都會(huì)用到TMsvId。
CMsvEntrySelection
CMsvEntrySelection是一個(gè)可以存儲(chǔ)TMsvId的數(shù)組,在使用CMsvEntry(CMsvServerEntry)的許多操作中都會(huì)做為參數(shù)或者返回對(duì)象。
TMsvEntry
上面已經(jīng)提到過(guò)了它表示數(shù)據(jù)項(xiàng)的索引,只包含消息的一些概要信息,主要會(huì)用到Id()成員函數(shù)得到數(shù)據(jù)項(xiàng)的標(biāo)識(shí)ID和公有數(shù)據(jù)成員iDetails、iDescription和iDate,前面兩個(gè)成員可以用來(lái)獲取和設(shè)置索引的概要信息,iDate成員可以獲取和設(shè)置數(shù)據(jù)項(xiàng)的日期及時(shí)間。
CMsvEntry和CMsvServerEntry
CMsvEntry和CMsvServerEntry可以理解為數(shù)據(jù)項(xiàng)的上下文(Context),這兩個(gè)類非常類似,只不過(guò)CMsvEntry用于客戶端,CMsvServerEntry用于實(shí)現(xiàn)消息的服務(wù)器端。它包含兩個(gè)部分的功能:一是可以允許訪問(wèn)與這個(gè)entry關(guān)聯(lián)的,不同類型的數(shù)據(jù)(比如可以根據(jù)指定ID定位數(shù)據(jù)項(xiàng)、獲得消息存儲(chǔ)和消息索引);二是運(yùn)行訪問(wèn)它的子entry和父entry(當(dāng)然對(duì)新得條目又可以進(jìn)行一功能)。
CMsvStore
上面已經(jīng)提到過(guò)它表示數(shù)據(jù)項(xiàng)的存儲(chǔ),可以通過(guò)CMsvEntry(CMsvServerEntry)的 EditStoreL(),ReadStoreL()函數(shù)取得可編輯存儲(chǔ)或只讀存儲(chǔ)。
二、數(shù)據(jù)項(xiàng)常用操作
因?yàn)橄⑻幚斫?/span>C/S架構(gòu)上,所以在消息操作之前,先要進(jìn)行一些預(yù)處理,大致步驟如下:
1、通過(guò)消息會(huì)話類CMsvSession連接到消息服務(wù)器,建立會(huì)話。因?yàn)橥ǔ_B接都采用異步方式,所以為了獲取連接的事件通知,實(shí)例化CMsvSession對(duì)象的類需要繼承自MMsvSessionObserver
2、構(gòu)造客戶端MTM注冊(cè)表對(duì)象(通過(guò)CClientMtmRegistry::NewL(CMsvSession &aMsvSession)來(lái)實(shí)現(xiàn))
3、構(gòu)造具體的客戶端MTM對(duì)象(通過(guò)CClientMtmRegistry::NewMtmL(TUid aMtmTypeUid)這里的MtmTypeUID和TMsvEntry內(nèi)的消息類型ID是一致的)
4、接下來(lái)的操作就是根據(jù)具體的需求創(chuàng)建、編輯、驗(yàn)證、發(fā)送消息條目(如果只是創(chuàng)建和編輯消息條目,則不用如上這么復(fù)雜,可省略MTM對(duì)象構(gòu)造)。
下面的消息操作使用了一個(gè)CMsvEntry或 CMsvServerEntry的指針對(duì)象,這兩個(gè)類提供的功能基本一樣,但有一部分函數(shù)名會(huì)不一樣,可以查一下SDK。
1. 獲得當(dāng)前數(shù)據(jù)項(xiàng)索引和ID
TMsvEntry oldEntry = iServerEntry->Entry(); //這里iServerEntry應(yīng)該是CMsvServerEntry
TMsvId oldContext = oldEntry.Id(); //如果使用CMsvEntry可以直接使用Id()
2. 定位到指定數(shù)據(jù)項(xiàng)
在更換當(dāng)前數(shù)據(jù)項(xiàng)之前通常先保存當(dāng)前數(shù)據(jù)項(xiàng)索引ID,更換數(shù)據(jù)項(xiàng)并完成相關(guān)操作后再更換回原來(lái)的數(shù)據(jù)項(xiàng),這可以避免影響其它函數(shù),是一個(gè)很好的習(xí)慣。
TMsvId oldContext = iServerEntry->Entry().Id();
//使用SetEntry()更換當(dāng)前數(shù)據(jù)項(xiàng)到root
iServerEntry->SetEntry(KMsvRootIndexEntryId);
//具體操作后更換回原來(lái)數(shù)據(jù)項(xiàng)
iServerEntry->SetEntry(oldContext);
3. 查找數(shù)據(jù)項(xiàng)
下面的三個(gè)CMsvEntry成員函數(shù)都能完成在當(dāng)前數(shù)據(jù)項(xiàng)下進(jìn)行查找的功能:
CMsvEntrySelection* ChildrenWithMtmL(TUid aMtm) const;
根據(jù)消息服務(wù)(MTM)進(jìn)行查找,查找消息索引對(duì)象(TMsvEntry)的成員iMtm等于aMtm的數(shù)據(jù)項(xiàng)ID。
CMsvEntrySelection* ChildrenWithServiceL(TMsvId aId) const;
根據(jù)消息服務(wù)ID進(jìn)行查找,查找消息索引對(duì)象(TMsvEntry)的成員iServiceId等于aId的數(shù)據(jù)項(xiàng)ID。
CMsvEntrySelection* ChildrenWithTypeL(TUid aEntryType) const;
根據(jù)數(shù)據(jù)項(xiàng)類型進(jìn)行查找,查找消息索引對(duì)角的(TMsvEntry)的成員iType等于aEntryType的數(shù)據(jù)項(xiàng)ID。
CMsvServerEntry與之相對(duì)應(yīng)的三個(gè)函數(shù)為GetChildrenWithMtm(), GetChildrenWithService(), GetChildrenWithType(),注意的一點(diǎn)是CMsvEntry的三個(gè)函數(shù)都返回一個(gè)CMsvEntrySelection對(duì)象的指針,使用完之后我們要負(fù)責(zé)釋放,使用CMsvServerEntry的三個(gè)函數(shù)需要事先構(gòu)造一個(gè)CMsvEntrySelection對(duì)象,用完之后也需要釋放。
找出POP3郵箱個(gè)數(shù)的代碼
iMsvEntry->SetEntryL( KUidMsgTypePop3 );
CMsvEntrySelection* sel = NULL;
sel = entry->ChildrenWithMtmL( KPkiSmtpTechnologyTypeUid );
TInt cnt = sel->Count(); //獲得集合中數(shù)據(jù)項(xiàng)的個(gè)數(shù)
delete sel;
4. 更改消息索引
TMsvEntry entry = iMsvEntry->Entry();
entry.iDetails.Set( _L( “New details” ) );
iMsvEntry->ChangeL( entry ); //把更改后的數(shù)據(jù)項(xiàng)索引寫回消息索引中去(相當(dāng)于commit提交)
這里最后的ChangeL很重要,如果不進(jìn)行該步調(diào)用,那么未將修改的消息索引寫回到消息存儲(chǔ)中。
5. 數(shù)據(jù)項(xiàng)的讀寫
在進(jìn)行數(shù)據(jù)項(xiàng)的讀寫之前需要使用EditStoreL(),ReadStoreL()函數(shù)得到相應(yīng)的存儲(chǔ)CMsvStore通過(guò)它提供的接口進(jìn)行操作。
void CMessageView::ViewMessageL(TMsvId aId)
{
// Construct the CMsvEntry
CMsvEntry* entry = iSession->GetEntryL(aId);
CleanupStack::PushL(entry);
// Get the messaging store
CMsvStore* store = entry->ReadStoreL();
CleanupStack::PushL(store);
// Construct the CRichText and restore the body text
CParaFormatLayer* paraLayer = CParaFormatLayer::NewL();
CleanupStack::PushL(paraLayer);
CCharFormatLayer* charLayer = CCharFormatLayer::NewL();
CleanupStack::PushL(charLayer);
CRichText* body = CRichText::NewL(paraLayer, charLayer);
CleanupStack::PushL(body);
store->RestoreBodyTextL(*body);
// Extract body text from CRichText
TInt len = body->DocumentLength(); //get length
HBufC *buf = HBufC::NewL( len );
TPtr ptrBuf = buf->Des();
body->Extract( ptrBuf, 0, len ); //get data
//因?yàn)椴煌南⒌拇鎯?chǔ)格式不同,還可能需要對(duì)ptrBuf進(jìn)行相應(yīng)的解碼才能正常
//顯示
delete buf;
buf = NULL;
CleanupStack::PopAndDestroy(5, entry);
}
三、關(guān)于SMS的一些特殊操作
1.讀取消息條目
//讀取發(fā)件箱消息條目索引
TMsvSelectionOrdering sort;
//全部?jī)?nèi)容排序,包括隱藏
sort.SetShowInvisibleEntries(ETrue);
//設(shè)置入口為outbox,也就是發(fā)信箱
CMsvEntry* entry = CMsvEntry::NewL(*iSession,KMsvGlobalOutBoxIndexEntryId,sort);
CleanupStack::PushL(entry);
//選擇全部?jī)?nèi)容
CMsvEntrySelection* entries = entry->ChildrenL();
CleanupStack::PushL(entries);
//讀取消息索引信息,At(0)代表首信息,取其他的可以給出相應(yīng)的index
TMsvId aEntryId = entries->At(0);
//得到消息索引的時(shí)間
TTime time;
time = entry->ChildDataL(aEntryId).iDate;
iSmsMtm->SwitchCurrentEntryL(aEntryId);//iSmsMtm是CSmsClientMtm類型的指針變量,它已經(jīng)初始化
iSmsMtm->LoadMessageL(); // load the message
CRichText& body = iSmsMtm->Body(); //sms的內(nèi)容存到CRichText控件對(duì)象中
TPtrC msg(body.Read(0));
//彈出對(duì)話框,提示短信內(nèi)容
CAknInformationNote* informationNote = new (ELeave) CAknInformationNote;
informationNote->ExecuteLD(msg);
CleanupStack::PopAndDestroy();
2.刪除消息條目
//刪除草稿箱中的消息條目
TMsvSelectionOrdering sort;
sort.SetShowInvisibleEntries(ETrue);
CMsvEntry* entry = CMsvEntry::NewL(*iSession,KMsvDraftEntryId,sort);
CleanupStack::PushL(entry);
CMsvEntrySelection* entries = entry->ChildrenL();
CleanupStack::PushL(entries);
TInt i = entries->Count();
for(TInt ncount=0;ncount<i;ncount++)
{
entry->DeleteL(entries->At(ncount));
}
// information to the user
iEikonEnv->InfoMsg(_L("DeleteAll Done!"));
CleanupStack::PopAndDestroy(2);
3.創(chuàng)建消息條目
創(chuàng)建消息條目部分在收件箱和草稿箱兩個(gè)地方是不一樣的,具體代碼如下:
//在草稿箱中創(chuàng)建短信
const TInt LEN = 12;
//轉(zhuǎn)入收件箱
iSmsMtm->SwitchCurrentEntryL(KMsvDraftEntryId);
//構(gòu)建消息索引
TMsvEntry newIndexEntry;
newIndexEntry.iDate.HomeTime();
newIndexEntry.iMtm = KUidMsgTypeSMS;
newIndexEntry.iType = KUidMsvMessageEntry;
//in 3rd edition crashes here if capabilities are wrong
newIndexEntry.iServiceId = iSmsMtm->ServiceId();
newIndexEntry.iDetails.Set(aName);//aName為收件人名稱
newIndexEntry.iDescription.Set(aContent.Left(LEN));
newIndexEntry.SetUnread(ETrue);
iSmsMtm->Entry().CreateL(newIndexEntry);
TMsvId smsId = newIndexEntry.Id();
iSmsMtm->SwitchCurrentEntryL(smsId);
//填充消息存儲(chǔ)區(qū)
iSmsMtm->AddAddresseeL(aAddr);//aAddr為收件人號(hào)碼,信息位于消息頭中
CRichText& body = iSmsMtm->Body();//消息正文
body.Reset();
body.InsertL(0, *iSmsContext);
//提交保存
iSmsMtm->SaveMessageL();
注:當(dāng)然如果要發(fā)送的短信,還需要進(jìn)行具體的設(shè)定,這部分將在發(fā)送消息里面詳述,這里只是做為普通的創(chuàng)建消息,與收件箱中消息進(jìn)行比較,具體見(jiàn)下面代碼示例。
//在收件箱中創(chuàng)建短信
const TInt LEN = 12;
//轉(zhuǎn)入收件箱
iSmsMtm->SwitchCurrentEntryL(KMsvGlobalInBoxIndexEntryId);
//構(gòu)建消息索引
TMsvEntry newIndexEntry;
//newIndexEntry.iDate.HomeTime();//收件箱短信索引頭在CSmsHeader中設(shè)置
newIndexEntry.iMtm = KUidMsgTypeSMS;
newIndexEntry.iType = KUidMsvMessageEntry;
//in 3rd edition crashes here if capabilities are wrong
newIndexEntry.iServiceId = iSmsMtm->ServiceId();
newIndexEntry.iDetails.Set(aAddr);
newIndexEntry.iDescription.Set(aContent.Left(LEN));
newIndexEntry.SetUnread(ETrue);
iSmsMtm->Entry().CreateL(newIndexEntry);
TMsvId smsId = newIndexEntry.Id();
iSmsMtm->SwitchCurrentEntryL(smsId);
//填充消息存儲(chǔ)區(qū)
//CSmsHeader& header = iSmsMtm->SmsHeader();
//header.SetFromAddressL(aAddr);//這里我采用了兩種方法,均不能正確設(shè)置發(fā)件人號(hào)碼
//iSmsMtm->AddAddresseeL(aAddr);//后來(lái)才知道這里為收件人號(hào)碼,所以必須在以后修改
CRichText& body = iSmsMtm->Body();
body.Reset();
body.InsertL(0, *iSmsContext);
//提交保存
iSmsMtm->SaveMessageL();
//完善消息頭,設(shè)置發(fā)件人號(hào)碼和發(fā)送時(shí)間
CMsvStore* messageStore = iSmsMtm->Entry().EditStoreL();
CleanupStack::PushL( messageStore );
CSmsHeader* hdr = CSmsHeader::NewL( CSmsPDU::ESmsDeliver, body );
CleanupStack::PushL( hdr );
hdr->SetFromAddressL(iNumber);
TTime nowTime;
nowTime.HomeTime();
hdr->Deliver().SetServiceCenterTimeStamp(nowTime);
hdr->StoreL(*messageStore);
messageStore->CommitL();
CleanupStack::PopAndDestroy(hdr);
CleanupStack::PopAndDestroy(messageStore);
// 修改當(dāng)前消息索引為只讀,這樣收件箱列表處瀏覽會(huì)有回復(fù)選項(xiàng)
//但是如果在之前就設(shè)置ReadOnly就會(huì)導(dǎo)致SaveMessageL出錯(cuò)
newIndexEntry.SetReadOnly(ETrue);
//消息索引提交更改
iSmsMtm->Entry().ChangeL(newIndexEntry);
4.發(fā)送消息條目
其實(shí)發(fā)送消息可以使用客戶端MTM方法,但是一般都是在活動(dòng)對(duì)象中實(shí)現(xiàn),或者實(shí)現(xiàn)Send-As API、CSendAppUi類來(lái)實(shí)現(xiàn)。以下代碼簡(jiǎn)單給出客戶端Mtm的方法,具體可以下載諾基亞論壇liuxg大大編寫的SmsSendTest例子,在這里給出其下載連接http://m.shnenglu.com/Files/franksunny/SmsSendTest.rar
//發(fā)送消息
//iMtm是在新建sms階段設(shè)定
TMsvEntry msvEntry = iMtm->Entry().Entry();
//重新設(shè)定TMsvEntry
msvEntry.iDetails.Set(iRecipient->Des()); // set recipient info in details
msvEntry.SetInPreparation(EFalse); // set inPreparation to false
msvEntry.SetSendingState(KMsvSendStateWaiting); // set the sending state (immediately)
msvEntry.iDate.HomeTime(); // set time to Home Time
// 得到sms內(nèi)容
CRichText& mtmBody = iMtm->Body();
mtmBody.Reset();
mtmBody.InsertL(0, KGDSMSTag); //插入我們的短信內(nèi)容
// 使用CSmsClientMtm類處理sms
CSmsClientMtm* smsMtm = STATIC_CAST(CSmsClientMtm*, iMtm);
smsMtm->RestoreServiceAndSettingsL();
//CSmsHeader封裝sms消息的參數(shù),像服務(wù)中心號(hào)碼和發(fā)送設(shè)定
CSmsHeader& header = smsMtm->SmsHeader();
//CSmsSettings類用來(lái)詳細(xì)設(shè)定sms Header
CSmsSettings* sendOptions = CSmsSettings::NewL();
CleanupStack::PushL(sendOptions);
sendOptions->CopyL(smsMtm->ServiceSettings());
sendOptions->SetDelivery(ESmsDeliveryImmediately);//設(shè)定立即發(fā)送
header.SetSmsSettingsL(*sendOptions);
//檢查服務(wù)中心號(hào)碼有效性
if(header.Message().ServiceCenterAddress().Length() == 0)
{
// 如果沒(méi)有設(shè)定,則查找默認(rèn)中心號(hào)碼
CSmsSettings* serviceSettings = &(smsMtm->ServiceSettings());
//中心號(hào)碼列表為空
if(!serviceSettings->NumSCAddresses())
{
// 錯(cuò)誤消息
iEikonEnv->InfoWinL(_L("No service center number"),_L("cannot send this one."));
}
else
{
// 設(shè)定為默認(rèn)服務(wù)中心號(hào)碼
CSmsNumber* sc = 0;
sc = &(serviceSettings->SCAddress(serviceSettings->DefaultSC()));
header.Message().SetServiceCenterAddressL(sc->Address());
}
}
CleanupStack::PopAndDestroy();
... ...
CMsvEntrySelection* selection = new (ELeave) CMsvEntrySelection;
CleanupStack::PushL(selection);
selection->AppendL(movedId); // 添加我們要發(fā)送的sms,movedId在省略部分有定義,是TMsvId型變量
// 調(diào)用的這個(gè)函數(shù)是用于發(fā)送的,具體的代碼后面介紹
SetScheduledSendingStateL(selection);
CleanupStack::PopAndDestroy(); // selection
return ETrue; // 到這里sms已被發(fā)送
---------------------------------------------------------------------------------------------------
---------------------------------------------------------------------------------------------------
void SetScheduledSendingStateL(CMsvEntrySelection* aSelection)
{
CBaseMtm* smsMtm = iMtm;
// 添加entry到任務(wù)列表
TBuf8<1> dummyParams;
CCommandAbsorbingControl::NewLC();
CMsvOperationWait* waiter = CMsvOperationWait::NewLC();
waiter->Start();
// 這個(gè)函數(shù)是關(guān)鍵
CMsvOperation* op= smsMtm->InvokeAsyncFunctionL(
ESmsMtmCommandScheduleCopy,
*aSelection,
dummyParams,
waiter->iStatus);
CleanupStack::PushL(op);
CActiveScheduler::Start(); //開(kāi)始時(shí)間表中任務(wù)
CleanupStack::PopAndDestroy(3); // waiter, op, CCommandAbsorbingControl
}
SMS操作補(bǔ)充
在項(xiàng)目開(kāi)發(fā)過(guò)程中會(huì)時(shí)不時(shí)爆發(fā)些問(wèn)題,為此一旦遇到問(wèn)題后得以解決都會(huì)在這里進(jìn)行知識(shí)點(diǎn)與實(shí)踐的積累
關(guān)于CSmsClientMtm::LoadMessageL
在調(diào)用CSmsClientMtm::LoadMessageL之前,必須調(diào)用SwitchCurrentEntryL(TMsvId aId)或SetCurrentEntryL(CMsvEntry* aEntry)將客戶端Mtm轉(zhuǎn)到當(dāng)前項(xiàng),然后才能順利調(diào)用,由于有時(shí)短信在Sim卡上,故調(diào)用時(shí)可能會(huì)產(chǎn)生KErrAccessDenied(-21)的錯(cuò)誤,這種情況下我們通常采用如下代碼實(shí)現(xiàn)
TInt error = KErrNone;
for (TInt i; i<KNumRetries; ++i)
{
TRAP(error, iSmsMtm->LoadMessageL());
if (error == KErrAccessDenied) // something is still busy with this entry
{
User::After(KDelayPeriod);
}
else
{
break;
}
}
User::LeaveIfError(error);
后來(lái)還發(fā)現(xiàn)一種情況,就是如果當(dāng)前的消息是彩信,則在正確使用如上代碼的時(shí)候,在調(diào)用CSmsClientMtm::LoadMessageL會(huì)遇到KErrNotFound(-1)的錯(cuò)誤,這個(gè)問(wèn)題還是需要值得注意一下的。
關(guān)于短信時(shí)間問(wèn)題
在短信中用到的時(shí)間,都是UTC時(shí)間,也即總所周知的GMT時(shí)間,系統(tǒng)的短信程序在顯示時(shí)間是會(huì)將這個(gè)UTC時(shí)間轉(zhuǎn)化為手機(jī)當(dāng)前所設(shè)時(shí)區(qū)的本地時(shí)間,在發(fā)送短信的時(shí)候,因?yàn)榇嬖诓莞逑浜桶l(fā)件箱中的短信只有一個(gè)時(shí)間,所以問(wèn)題不大,我們只要在創(chuàng)建短信時(shí)調(diào)用TMsvEntry::iDate.UniversalTime()函數(shù)即可。
在收件箱中的短信則有兩個(gè)時(shí)間,一個(gè)跟上面一樣,簡(jiǎn)單表示接受時(shí)的本地時(shí)間,而另外一個(gè)時(shí)間則在信息詳情中才能看到,這是服務(wù)中心的時(shí)間,也即短信具體何時(shí)經(jīng)服務(wù)中心發(fā)出到手機(jī)的時(shí)間。我們?cè)跍y(cè)試過(guò)程中發(fā)現(xiàn),中國(guó)移動(dòng)在09年9月份,所有139號(hào)段發(fā)過(guò)來(lái)的短信在這個(gè)服務(wù)中心時(shí)間上存在錯(cuò)誤,其GMT時(shí)間變成了北京時(shí)間,其時(shí)區(qū)變成了格林尼治的零時(shí)區(qū)。當(dāng)時(shí)因?yàn)檫@個(gè)問(wèn)題還花了一天時(shí)間來(lái)進(jìn)行測(cè)試驗(yàn)證。
另外畫蛇添足下,有一個(gè)UTC時(shí)間轉(zhuǎn)為本地時(shí)間的簡(jiǎn)單函數(shù)如下:
#include <tz.h>
#include <tzconverter.h> //tzclient.lib
void ConvertTimeToLocal(TTime& aTime)
{
RTz tzServer;
User::LeaveIfError(tzServer.Connect());
CleanupClosePushL(tzServer);
CTzConverter* tzConverter = CTzConverter::NewL(tzServer);
CleanupStack::PushL(tzConverter);
tzConverter->ConvertToLocalTime(aTime);
CleanupStack::PopAndDestroy(2);
}
關(guān)于客戶端MTM對(duì)象讀取和發(fā)送注意事項(xiàng)
在工程代碼中發(fā)現(xiàn)當(dāng)只實(shí)例化一個(gè)CSmsClientMtm對(duì)象,而要用該對(duì)象進(jìn)行讀取短信和發(fā)送短信操作時(shí),未調(diào)用讀取操作時(shí),發(fā)送正常,但是一旦調(diào)用讀取短信操作,就會(huì)在語(yǔ)句CSmsClientMtm::AddAddresseeL設(shè)置收件人號(hào)碼時(shí)報(bào)-5(KErrNotSupported)功能不支持的錯(cuò)誤提示。
具體原因是為什么暫不知道,只是看到newlc論壇有該問(wèn)題的解決方案,采用創(chuàng)建兩個(gè)客戶端MTM對(duì)象的方法來(lái)實(shí)現(xiàn),具體代碼是
iSmsMtmR = static_cast(iMtmReg->NewMtmL(KUidMsgTypeSMS)); // used for receive
iSmsMtmS = static_cast(iMtmReg->NewMtmL(KUidMsgTypeSMS)); // used for send
驗(yàn)證的確可以,如果要看原帖具體地址則在http://nioulk.newlc.com/topic-10736
致謝:本文其實(shí)本人并非原創(chuàng),只是將Beover1984的原文《Symbian OS中的消息存儲(chǔ)與常用操作》和網(wǎng)上流傳的《SMS故事》做了學(xué)習(xí)后,結(jié)合工作中的實(shí)際而整理得到,非常感謝以上兩位作者的指點(diǎn)。
本文有一個(gè)圖片沒(méi)有顯示出來(lái),但是word文檔中是有的,為此給出word文檔的下載地址:http://m.shnenglu.com/Files/franksunny/SymbianOSMessage.rar

