Wiley.Symbian.OS.C.plus.plus.for.Mobile.Phones.Aug.2007 (779890), страница 46
Текст из файла (страница 46)
Arguably, losing access to a stream is more seriousthan a memory leak, since a persistent file store outlives the applicationthat created it. The stream store API contains a tool, whose central classis CStoreMap, to assist with stream cleanup.STORES231You can find more information on these topics in the SDK. For morecomplex data to be saved to file using stores requires more complicatedsource code. For this reason, Symbian OS provides database APIs in theDBMS component and the SQL RDBMS (see Chapter 22).Embedded StoresNot all stores are as simple as the ones we have described so far.As illustrated in Figure 7.4, a store may, in fact, contain an arbitrarilycomplex network of streams. Any stream may contain another stream – byincluding its ID – and a stream may itself contain an embedded store.Figure 7.4 A more complex storeIt may be useful to store a collection of streams in an embedded store:from the outside, the embedded store appears as a single stream and itcan, for example, be copied or deleted as a whole, without the need toconsider its internal complexities.
An embedded store cannot be modifiedand thus behaves like a direct file store – which means that you can’tembed a permanent file store.The following code illustrates writing a permanent file store thatcontains an embedded file store.void COandXAppUi::WriteEmbeddedFileStoreL(RFs& aFs, TDesC& aFileName){CFileStore* mainStore = CPermanentFileStore::ReplaceLC(aFs,aFileName, EFileWrite);mainStore->SetTypeL(KPermanentFileStoreLayoutUid);RStoreWriteStream hostStream;TStreamId embeddedStoreId = hostStream.CreateLC(*mainStore);// construct the embedded storeCPersistentStore* embeddedStore = CEmbeddedStore::NewLC(hostStream);232FILES AND THE FILE SYSTEMRStoreWriteStream subStream;TStreamId id = subStream.CreateLC(*embeddedStore);TInt16 i = 0x1234;subStream << i;subStream.CommitL() ;CleanupStack::PopAndDestroy() ; // subStreamembeddedStore->SetRootL(id);embeddedStore->CommitL() ;CleanupStack::PopAndDestroy() ; // embeddedStorehostStream.CommitL() ;CleanupStack::PopAndDestroy() ; // hostStreamRStoreWriteStream mainStream;TStreamId mainId = mainStream.CreateLC(*mainStore);TInt16 j = 0x3456;mainStream << j;mainStream.CommitL() ;CleanupStack::PopAndDestroy() ; //mainStreamRStoreWriteStream rootStream;TStreamId rootId = rootStream.CreateLC(*mainStore);TInt16 k = 0x5678;rootStream << k;rootStream << mainId;rootStream << embeddedStoreId;rootStream.CommitL() ;CleanupStack::PopAndDestroy() ; //rootStreammainStore->SetRootL(rootId);mainStore->CommitL() ;CleanupStack::PopAndDestroy() ; // mainStore}To create the embedded store, you first create a stream in the mainstore as normal:RStoreWriteStream hostStream;TStreamId embeddedStoreId = hostStream.CreateLC(*mainStore);Then you create the embedded store hosted in that stream:// construct the embedded storeCPersistentStore* embeddedStore = CEmbeddedStore::NewLC(hostStream);You create and write streams within the embedded store, and set theroot stream, in exactly the same way as for any other store:RStoreWriteStream subStream;TStreamId id = subStream.CreateLC(*embeddedStore);TInt16 i = 0x1234;subStream << i;subStream.CommitL() ;STORES233CleanupStack::PopAndDestroy() ; // subStreamembeddedStore->SetRootL(id);embeddedStore->CommitL() ;The IDs of the other streams are stored directly in the root stream’sdata, rather than using a stream dictionary:RStoreWriteStream rootStream;TStreamId rootId = rootStream.CreateLC(*mainStore);TInt16 k = 0x5678;rootStream << k;rootStream << mainId;rootStream << embeddedStoreId;rootStream.CommitL() ;CleanupStack::PopAndDestroy() ; //rootStreamThe following code reads the file that was written in the previousexample:void COandXAppUi::ReadEmbeddedFileStoreL(RFs& aFs, TDesC& aFileName){CFileStore* store = CPermanentFileStore::OpenLC(aFs,aFileName, EFileRead);RStoreReadStream rootStream;rootStream.OpenLC(*store, store->Root() );TInt16 k;TStreamId mainId;TStreamId embeddedStoreId;rootStream >> k;rootStream >> mainId;rootStream >> embeddedStoreId;CleanupStack::PopAndDestroy() ; // rootStreamRStoreReadStream mainStream;mainStream.OpenLC(*store, mainId);TInt16 j;mainStream >> j;CleanupStack::PopAndDestroy() ; // mainStreamRStoreReadStream hostStream;hostStream.OpenLC(*store, embeddedStoreId);CPersistentStore* embeddedStore = CEmbeddedStore::FromLC(hostStream);RStoreReadStream subStream;subStream.OpenLC(*embeddedStore, embeddedStore->Root() );TInt16 i;subStream >> i;CleanupStack::PopAndDestroy(3); // subStream, embeddedStore// and hostStreamCleanupStack::PopAndDestroy() ; // store}234FILES AND THE FILE SYSTEMAfter opening the file, we open the root stream and read its data,including the IDs of the other two streams:RStoreReadStream rootStream;rootStream.OpenLC(*store, store->Root() );TInt16 k;TStreamId mainId;TStreamId embeddedStoreId;rootStream >> k;rootStream >> mainId;rootStream >> embeddedStoreId;CleanupStack::PopAndDestroy() ; // rootStreamTo read the embedded store, open the host stream using the ID fromthe root stream:RStoreReadStream hostStream;hostStream.OpenLC(*store, embeddedStoreId);Open the embedded store it contains, using the FromLC() functionof CEmbeddedStore:CPersistentStore* embeddedStore = CEmbeddedStore::FromLC(hostStream);Then we can open the embedded store’s root stream and read it likeany other stream:RStoreReadStream subStream;subStream.OpenLC(*embeddedStore, embeddedStore->Root() );TInt16 i;subStream >> i;Stores and the Application ArchitectureIn UIQ, the application architecture provides support for an applicationto save its data in a direct file store via the base class CEikDocument.The majority of S60 applications don’t need to access an applicationdocument file and this access is disabled.
If necessary, you can enable itin an S60 application by supplying an implementation of the applicationdocument’s OpenFileL() function, as shown in the following documentclass definition:class COandXDocument : public CAknDocument{...public:...// override CAknDocumentSTORES235virtual CFileStore* OpenFileL(TBool aDoOpen,const TDesC& aFilename, RFs& aFs);...};The OpenFileL() function simply calls CEikDocument’s OpenFileL():CFileStore* COandXDocument::OpenFileL(TBool aDoOpen,const TDesC& aFilename, RFs& aFs){return CEikDocument::OpenFileL(aDoOpen, aFilename, aFs);}When support for the application’s document file is enabled, all youneed to supply are implementations of the document’s StoreL() andRestoreL() functions, which are prototyped as:public:void StoreL(CStreamStore& aStore, CStreamDictionary& aStreamDic) const;void RestoreL(const CStreamStore& aStore,const CStreamDictionary& aStreamDic);The default implementations are virtual and empty; an application mayimplement these functions as:void COandXDocument::StoreL(CStreamStore& aStore,CStreamDictionary& aStreamDic) const{TStreamId id = iAppUi->StoreL(aStore);aStreamDic.AssignL(KUidOandXApp, id);}void COandXDocument::RestoreL(const CStreamStore& aStore,const CStreamDictionary& aStreamDic){TStreamId id = aStreamDic.At(KUidOandXApp);iAppUi->RestoreL(aStore, id);}The responsibility for storing and restoring the application’s data istransferred to the application UI, which uses code similar to that describedin Section 7.4:TStreamId COandXAppUi::StoreL(CStreamStore& aStore) const{RStoreWriteStream stream;TStreamId id = stream.CreateLC(aStore);stream << *this; // alternatively, use ExternalizeL(stream)stream.CommitL() ;236FILES AND THE FILE SYSTEMCleanupStack::PopAndDestroy() ;return id;}void COandXAppUi::RestoreL(const CStreamStore& aStore,TStreamId aStreamId){RStoreReadStream stream;stream.OpenLC(aStore, aStreamId);stream >> *this; // alternatively use InternalizeL(stream)CleanupStack::PopAndDestroy() ;}These, in turn, use the Application UI’s InternalizeL() andExternalizeL() functions, which were described in Section 7.4:void COandXAppUi::ExternalizeL(RWriteStream& aStream) const{for (TInt i = 0; i<KNumberOfTiles; i++){aStream.WriteInt8L(iTileState[i]);}aStream.WriteInt8L(iGameStatus);aStream.WriteInt8L(iCrossTurn);aStream.WriteInt8L(iAppView->IdOfFocusControl() );}void COandXAppUi::InternalizeL(RReadStream& aStream){for (TInt i = 0; i<KNumberOfTiles; i++){iTileState[i] = aStream.ReadInt8L() ;}iGameStatus = aStream.ReadInt8L() ;iCrossTurn = aStream.ReadInt8L() ;SetNavigationPaneL(iCrossTurn);iAppView->MoveFocusTo(aStream.ReadInt8L() );}The application architecture takes care of almost everything else,including creating the store when the application is run for the first timeand reading the file when the application is run on subsequent occasions.In addition to creating the root stream and the stream containingthe stream dictionary, it also adds an application-identifier stream.
Thisstream contains the application’s UID and the application file name(for example, OANDX.EXE) and can be used to verify that the file isa valid one for the application to open and read. The applicationidentifier stream is identified in the stream dictionary by a UID ofKUidAppIdentifierStream and is created by code of the form:void WriteAppIdentifierL(CFileStore* aStore,CStreamDictionary* aDict, TUid aAppUid, TDesC& aAppName)STORES237{TApaAppIdentifier appIdent(aAppUid, aAppName);RStoreWriteStream stream;TStreamId id = stream.CreateLC(*aStore);stream << appIdent;stream.CommitL() ;CleanupStack::PopAndDestroy() ; // streamaDict->AssignL(KUidAppIdentifierStream, id);}In S60 applications, you need to ensure that the file is updated whenthe application is closed.