zoukankan      html  css  js  c++  java
  • symbian mtm操作

    symbian mtm操作

    MTM ==> Message Type Modules

      一、消息存储基本知识:

      SymbianOS提供的消息传送架构也是基于Client/Server机制,Server端负责管理手机上的各种消息,在进行消息相关操作之前我们需要了解SymbianOS是如何组织和存储消息的。

      手机中的各种消息都是以数据项(Entry)形式供程序操作,数据项有4种类型,SymbianOS为每种数据项提供了相应的常量标识UID,这些UID保存在msvuids.h文件中:

      1、文件夹类型,对应常量UID为KUidMsvFolderEntry,和PC上的文件夹系统一样,每个文件夹可以包含其它数据项也可能是其它数据项的子数据项。

      2、消息类型,对应常量UID为KUidMsvMessageEntry,它表示数据项是一条消息。

      3、附件类型,对应常量UID为KUidMsvAttachmentEntry,它表示某条消息的附件。

      4、服务类型,对应常量UID为KUidMsvServiceEntry,服务数据项包含某个消息服务的配置信息,在一般情况还拥有通过该服务收发的消息数据项。

      除了上面提到的四种类型UID还有常用到的UID是KUidMsvRootEntry(msvids.h),它指的是根数据项,根数据项包含了4个标准文件夹数据项,分别是收件箱(KMsvGloballnBoxIndexEntryld),发件箱(KMsvgGlobalOutBoxIndexEntryld)、草稿箱(KMsvDraftEntryld)和已发送项(KMsvSentEntryld),另外根数据项下面还包含有各种消息服务项.

      SymbianOS中的消息服务器负责保存各种类型的数据项,这里有两个基本概念需要了解:消息存储和消息索引。消息存储保存了数据项的数据,保存的数据格式取决于消息服务,服务数据项使用消息存储保存服务配置信息,文件夹数据项不使用消息存储(即存储为空),Symbian提供了CMsvStore类来访问数据项的消息存储;为了节省内存和快速检索消息,消息服务器把数据项的一些概要信息(标题,日期,类型,ID等)写到消息索引中,当消息服务器启动时索引装载到RAM中直到消息服务器关闭,Symbian提供了TMsvEntry类表示数据项索引。

      操作消息常用的类和数据类型:

      CMsvSession:该类代表客户端(客户端MTM、用户接口MTM或者客户端消息应用程序)与消息服务器端的通讯通道即C/S架构中客户端与消息服务器的回话。每一个客户端线程对应一个该类的实例,使用它获得下面将要提到的CMsvEntry上下文对象。一个消息客户端应用必须在正常使用任何MTM或CMsvEntry对象前,使用OpenSyncL()或者OpenASyncL()来新建立一个Session对象。

      接上页

      CClientMtmRegistry:Registry掌握了客户端所有目前可用的MTM有关的细节,消息客户端可以使用该类获得CBaseMtm继承的对象。

      CBaseMtm:这个类主要用来操作消息条目,比如可以新建、修改、发送消息条目。

      TMsvId:它只是一个TInt32的Typedef,消息服务器为每个数据项分配一个唯一的数据作为标识,除了上面提到的几个固定标识,其它标识都是动态分配的。想要对某个消息进行操作必须先得到他的ID,Symbian中消息相关的大部分函数都会用到TMsvId。

      CMsvEntrySelection,是一个可以存储TMsvId的数组,在使用CMsvEntry(CMsvServerEntry)的许多操作中都会作为参数或者返回对象。

      TMsvEntry:上面提到过它表示数据项的索引,只包含消息的一些概要信息,主要会用到Id()成员函数得到数据项的标识ID和公有数据成员iDetails、iDesciption和iDate,前面两个成员可以用来获取和设置索引的概要信息,iDate成员可以获取和设置数据项的日期及时间。

      CMsvEntry和CMsvServerEntry:可以理解为数据项的上下文(context),这两个类非常类似,只不过CMsvEntry用户客户端,CMsvServerEntry用户实现消息的服务端。它包含两个部分的功能:一是可以允许访问与这个Entry关联的,不同类型的数据(比如可以根据指定ID定位数据项、获取消息存储和消息索引);二是运行访问它的子entry和父entry(当然对新的条目又可以进行一的功能)。

      CMsvStore:上面提到过表示数据项的存储,可以通过CMsvEntry(CMsvServerEntry)的EditStoreL(),ReadStoreL()函数取得可编辑存储或只读存储

      二、数据项常用操作

      因为消息处理建立在C/S架构上,所以在消息操作之前,先要进行一些预处理,大致步骤如下:

      1、通过消息会话类CMsvSession连接到消息服务器,建立会话。因为通常连接都采用异步方式,所以为了或许连接的事件通知,实例化CMsvSession对象的类需要继承自MMsvSessionObserver。

      2、构造客户端MTM注册对象(通过CClientMtmRegistry::NewL(CMsvSession &aMsvSession)来实现)。

      3、构造具体的客户端MTM对象(通过CClientMtmRegistry::NewMtmL(TUid aMtmTypeUid)这里的MtmTypeUID和TMsvEntry内的消息类型ID是一致的)。

      4、接下来的操作就是根据具体需求创建、编辑、验证、发送消息条目(如果只是创建和编辑消息条目,则不用如上这么复杂,可省略MTM对象构造)。

      接上页

      下面的消息操作使用了一个CMsvEntry或CMsvServerEntry的指针对象,这两个类提供的基本功能一样,但有一部分函数名会不一样,可以查一下SDK。

      1.获得当前数据项索引和ID

      TMsvEntry oldEntry = iServerEntry->Entry(); //这里iServerEntry应该是CMsvServerEntry

      TMsvId oldContext = oldEntry.Id(); //如果使用CMsvEntry可以直接使用Id()

      2.定位到指定数据项

      在更换当前数据项之前通常先保存当前数据项索引ID,更换数据项并完成相关操作后再更换回原来的数据项,这可以避免影响其它函数,是一个很好习惯。

      TMsvId oldContext = iServerEntry->Entry().Id();

      //使用SetEntry()更换当前数据项到root

      iServerEntry->SetEntry(KMsvRootIndexEntryId);

      //具体操作后更换回原来数据项

      iServerEntry->SetEntry(oldContext);

      3.查找数据项

      下面的三个CMsvEntry成员函数都能完成在当前数据项下进行查找的功能:

      CMsvEntrySelection* ChildrenWithMtmL(TUid aMtm) const;

      根据消息服务(MTM)进行查找,查找消息索引对象(TMsvEntry)的成员iMtm等于aMtm的数据项ID。

      CMsvEntrySelection* ChildrenWithServiceL(TMsvId aId) const;

      根据消息服务ID进行查找,查找消息索引对象(TMsvEntry)的成员iServiceId等于aId的数据项ID。

      CMsvEntrySelection* ChildrenWithTypeL(TUid aEntryType) const;

      根据数据项类型进行查找,查找消息索引对角的(TMsvEntry)的成员iType等于aEntryType的数据项ID。

      CMsvServerEntry与之相对应的三个函数为GetChildrenWithMtm(), GetChildrenWithService(), GetChildrenWithType(),注意的一点是CMsvEntry的三个函数都返回一个CMsvEntrySelection对象的指针,使用完之后我们要负责释放,使用CMsvServerEntry的三个函数需要事先构造一个CMsvEntrySelection对象,用完之后也需要释放。

      找出POP3邮箱个数的代码:

      iMsvEntry->SetEntryL( KUidMsgTypePop3 );

      CMsvEntrySelection* sel = NULL;

      sel = entry->ChildrenWithMtmL( KPkiSmtpTechnologyTypeUid );

      TInt cnt = sel->Count(); //获得集合中数据项的个数

      接上页

      delete sel;

      4.更改消息索引

      TMsvEntry entry = iMsvEntry->Entry();

      entry.iDetails.Set( _L( “New details” ) );

      iMsvEntry->ChangeL( entry ); //把更改后的数据项索引写回消息索引中去(相当于commit提交)

      这里最后的ChangeL很重要,如果不进行该步调用,那么未将修改的消息索引写回到消息存储中。

      5.数据项的读写

      在进行数据项的读写之前需要使用EditStoreL(),ReadStoreL()函数得到相应的存储CMsvStore通过它提供的接口进行操作。

      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

      //因为不同的消息的存储格式不同,还可能需要对ptrBuf进行相应的解码才能正常

      //显示

      delete buf;

      buf = NULL;

      CleanupStack::PopAndDestroy(5, entry);

      }

      三、关于SMS的一些特殊操作

      1.读取消息条目

      //读取发件箱消息条目索引

      TMsvSelectionOrdering sort;

      //全部内容排序,包括隐藏

      sort.SetShowInvisibleEntries(ETrue);

      //设置入口为outbox,也就是发信箱

      接上页

      CMsvEntry* entry = CMsvEntry::NewL(*iSession,KMsvGlobalOutBoxIndexEntryId,sort);

      CleanupStack::PushL(entry);

      //选择全部内容

      CMsvEntrySelection* entries = entry->ChildrenL();

      CleanupStack::PushL(entries);

      //读取消息索引信息,At(0)代表首信息,取其他的可以给出相应的index

      TMsvId aEntryId = entries->At(0);

      //得到消息索引的时间

      TTime time;

      time = entry->ChildDataL(aEntryId).iDate;

      iSmsMtm->SwitchCurrentEntryL(aEntryId);//iSmsMtm是CSmsClientMtm类型的指针变量,它已经初始化

      iSmsMtm->LoadMessageL(); // load the message

      CRichText& body = iSmsMtm->Body(); //sms的内容存到CRichText控件对象中

      TPtrC msg(body.Read(0));

      //弹出对话框,提示短信内容

      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< p>

      {

      entry->DeleteL(entries->At(ncount));

      }

      // information to the user

      iEikonEnv->InfoMsg(_L("DeleteAll Done!"));

      CleanupStack::PopAndDestroy(2);

      3.创建消息条目

      创建消息条目部分在收件箱和草稿箱两个地方是不一样的,具体代码如下:

      //在草稿箱中创建短信

      const TInt LEN = 12;

      //转入收件箱

      iSmsMtm->SwitchCurrentEntryL(KMsvDraftEntryId);

      接上页

      //构建消息索引

      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);

      //填充消息存储区

      iSmsMtm->AddAddresseeL(aAddr);//aAddr为收件人号码,信息位于消息头中

      CRichText& body = iSmsMtm->Body();//消息正文

      body.Reset();

      body.InsertL(0, *iSmsContext);

      //提交保存

      iSmsMtm->SaveMessageL();

      注:当然如果要发送的短信,还需要进行具体的设定,这部分将在发送消息里面详述,这里只是做为普通的创建消息,与收件箱中消息进行比较,具体见下面代码示例。

      //在收件箱中创建短信

      const TInt LEN = 12;

      //转入收件箱

      iSmsMtm->SwitchCurrentEntryL(KMsvGlobalInBoxIndexEntryId);

      //构建消息索引

      TMsvEntry newIndexEntry;

      //newIndexEntry.iDate.HomeTime();//收件箱短信索引头在CSmsHeader中设置

      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);

      //填充消息存储区

      接上页

      //CSmsHeader& header = iSmsMtm->SmsHeader();

      //header.SetFromAddressL(aAddr);//这里我采用了两种方法,均不能正确设置发件人号码

      //iSmsMtm->AddAddresseeL(aAddr);//后来才知道这里为收件人号码,所以必须在以后修改

      CRichText& body = iSmsMtm->Body();

      body.Reset();

      body.InsertL(0, *iSmsContext);

      //提交保存

      iSmsMtm->SaveMessageL();

      //完善消息头,设置发件人号码和发送时间

      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);

      // 修改当前消息索引为只读,这样收件箱列表处浏览会有回复选项

      //但是如果在之前就设置ReadOnly就会导致SaveMessageL出错

      newIndexEntry.SetReadOnly(ETrue);

      //消息索引提交更改

      iSmsMtm->Entry().ChangeL(newIndexEntry);

      4.发生消息条目

      其实发送消息可以使用客户端MTM方法,但是一般都是在活动对象中实现,或者实现Send-As API、CSendAppUi类来实现。以下代码简单给出客户端Mtm的方法:

      //发送消息

      //iMtm是在新建sms阶段设定

      TMsvEntry msvEntry = iMtm->Entry().Entry();

      //重新设定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内容

      CRichText& mtmBody = iMtm->Body();

      mtmBody.Reset();

      mtmBody.InsertL(0, KGDSMSTag); //插入我们的短信内容

      // 使用CSmsClientMtm类处理sms

      CSmsClientMtm* smsMtm = STATIC_CAST(CSmsClientMtm*, iMtm);

      smsMtm->RestoreServiceAndSettingsL();

      //CSmsHeader封装sms消息的参数,像服务中心号码和发送设定

      CSmsHeader& header = smsMtm->SmsHeader();

      //CSmsSettings类用来详细设定sms Header

      CSmsSettings* sendOptions = CSmsSettings::NewL();

      CleanupStack::PushL(sendOptions);

      sendOptions->CopyL(smsMtm->ServiceSettings());

      sendOptions->SetDelivery(ESmsDeliveryImmediately);//设定立即发送

      header.SetSmsSettingsL(*sendOptions);

      //检查服务中心号码有效性

      if(header.Message().ServiceCenterAddress().Length() == 0)

      {

      // 如果没有设定,则查找默认中心号码

      CSmsSettings* serviceSettings = &(smsMtm->ServiceSettings());

      //中心号码列表为空

      if(!serviceSettings->NumSCAddresses())

      {

      // 错误消息

      iEikonEnv->InfoWinL(_L("No service center number"),_L("cannot send this one."));

      }

      else

      {

      // 设定为默认服务中心号码

      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); // 添加我们要发送的sms,movedId在省略部分有定义,是TMsvId型变量

      // 调用的这个函数是用于发送的,具体的代码后面介绍

      SetScheduledSendingStateL(selection);

      接上页

      CleanupStack::PopAndDestroy(); // selection

      return ETrue; // 到这里sms已被发送

      ---------------------------------------------------------------------------------------------------

      ---------------------------------------------------------------------------------------------------

      void SetScheduledSendingStateL(CMsvEntrySelection* aSelection)

      {

      CBaseMtm* smsMtm = iMtm;

      // 添加entry到任务列表

      TBuf8<1> dummyParams;

      CCommandAbsorbingControl::NewLC();

      CMsvOperationWait* waiter = CMsvOperationWait::NewLC();

      waiter->Start();

      // 这个函数是关键

      CMsvOperation* op= smsMtm->InvokeAsyncFunctionL(ESmsMtmCommandScheduleCopy,

      *aSelection, dummyParams, waiter->iStatus);

      CleanupStack::PushL(op);

      CActiveScheduler::Start(); //开始时间表中任务

      CleanupStack::PopAndDestroy(3); // waiter, op, CCommandAbsorbingControl

      }

  • 相关阅读:
    springboot 环境搭建
    Maven工程下构建ssh项目配置
    java动态代理
    java 代理
    java反射
    Java缓存流
    java 输出流
    Java输入数据流
    使用微软 AppFabric 遇到问题
    百度文本编辑器 Ueditor for net 使用七牛存储附件的实现
  • 原文地址:https://www.cnblogs.com/zziss/p/2110562.html
Copyright © 2011-2022 走看看