Windows Phone 8.1 - 操作 ApplicationData

Windows Phone 8.1 - 操作 ApplicationData

在 8.1 開始,Silverlight App 中使用的 IsolatedStorage 已不支援(例如:IsolatedStorageSettings.ApplicationSettings)

很多操作的方式將全面改用 StorageFile / StorageFolder,針對可操作的目錄與資料設定均有不同。

前幾篇介紹了 WP 8.1 的 Lifecycle 之後,接下來該篇要介紹如何操作 App 中的 ApplicationData

協助在 Lifecycle 各階段裡怎麼保存與還原資料。

 

ApplicationData class

    提供存取 application data store。application data store 包括:檔案、設定與目錄(local, roaming, temporary);

using Windows.Storage;
 
// 利用 ApplicationData.Current 來取得要操作的對象
ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;

 

重要的屬性、方法與事件:

類型 名稱 說明
Events DataChanged Occurs when roaming application data is synchronized.
     
Methods ClearAsync() Removes all application data from the local, roaming, and temporary app data stores.
  ClearAsync(ApplicationDataLocality) Removes all application data from the specified app data store.
Member Value Description
Local | local 0 The data resides in the local application data store.
Roaming | roaming 1 The data resides in the roaming application data store.
Temporary | temporary 2 The data resides in the temporary application data store.
LocalCache | localCache 3 The data resides in the local cache for the application data store.
  SetVersionAsync Sets the version number of the application data in the app data store.
  SignalDataChanged Sends a DataChanged | datachanged event to all registered event handlers.
     
Properties Current Read-only, Provides access to the app data store associated with the app's app package.
  LocalFolder Read-only, Gets the root folder in the local app data store.
  LocalSettings Read-only, Gets the application settings container in the local app data store.
  RoamingFolder Read-only, Gets the root folder in the roaming app data store.
  RoamingSettings Read-only, Gets the application settings container in the roaming app data store.
  RoamingStorageQuota Read-only, Gets the maximum size of the data that can be synchronized to the cloud from the roaming app data store.
  TemporaryFolder Read-only, Gets the root folder in the temporary app data store.
  Version Read-only, Gets the version number of the application data in the app data store.

 

利用 ApplicationData.Current 取得 instance 其中分成幾個可操作的目錄與設定:

a. ApplicationData.LocalFolderApplicationData.LocalSettings

    儲存在本機設備上的資料,即使 App 更新之後仍然存在;

b. RoamingFolderRoamingSettings

    保存在此的資料可共享在同一個帳號下安裝相同 App 的不同設備中;

c. TemporaryFolder

    可以由系統在任何時候被刪除;

 

上述這些儲存體均可以搭配「Version」與「SetVersionAsync」來指定搭配那一個 App 的版本要合併或移動既有的資料。

其概念則是:

1. ApplicationData 預設版為 0;

2. 如果升級之後,想要更動 ApplicationData 的版本為  1 時,則要設定 SetVersionAsync ,並給于處理的 Handler,如下:

private async void VersionCheck()
{
    // 取得目前的版本
    var version = ApplicationData.Current.Version;
 
    // 比對版本與預期的版本是否一致,如果不一樣,代表是升級的 App,則進行註冊的 ApplicationDataSetVersionHandler
    if (version != 1)
    {
        await ApplicationData.Current.SetVersionAsync(1, ApplicationDataSetVersionHandler);
    }
}
 
void ApplicationDataSetVersionHandler(SetVersionRequest e)
{
    // 撰寫需要做資料更動或格式轉換的邏輯
}

3. 這樣一來,下一個版本(可能是 2)就以相同的方式去撰寫升級的邏輯在對應的處理 handler 即可。

 

 

以下針對這幾個部分進行說明。

 

》Local app data

A. ApplicationData.LocalFolder

        代表 App 可操作本地儲存區的 root folder。取得後為 StorageFolder。可透過 protocol 的方式來取得連接 local store 內的資料:

"ms-appdata:///local/"」,例如:「<img src="ms-appdata:///local/myFile.png" alt="" />」。

如果需要取得 App 安裝的目錄可藉由:「Windows.ApplicationModel.Package.Current.InstalledLocation」。

可發現,ApplicationData 被歸類在 Windows.Storage 下,而安裝目錄是放在 Windows.ApplicationModel 下,二者是不同的。

 

透過下方的範例說明如何操作:

private async void btnWriteLocalFolder_Click(object sender, RoutedEventArgs e)
{
    StorageFolder localFolder = ApplicationData.Current.LocalFolder;
    
    // 建立一個檔案,設定 CreationCollisionOption 如果既有檔案就覆寫
    StorageFile sampleFile = await localFolder.CreateFileAsync("sample.txt", CreationCollisionOption.ReplaceExisting);
    
    String content = "sample data, write to local folder.";
    
    // 利用 FileIO.WriteTextAsync 將內容寫入
    await FileIO.WriteTextAsync(sampleFile, content);
}
 
private async void ReadLocalFolderFile()
{
    try
    {
        StorageFolder localFolder = ApplicationData.Current.LocalFolder;
        StorageFile sampleFile = await localFolder.GetFileAsync("sample.txt");
        // 利用 FileIO.ReadTextAsync 取得內容
        String content = await FileIO.ReadTextAsync(sampleFile);
    }
    catch (Exception)
    {
        // 檔案不存在
    }
}

上述利用了 StorageFolder 的 Windows.Storage.StorageFolder.CreateFileAsync | createFileAsync 建立檔案,

其中設定了 CreationCollisionOption enumeration 值讓在建立檔案時如遇到檔案已存在就覆寫 (ReplaceExisting),

利用 Windows.Storage.FileIO.WriteTextAsync | writeTextAsync 將內容寫入,搭配 Windows.Storage.FileIO.ReadTextAsync | readTextAsync 取回內容;

 

Windows.Storage.FileIO

     提供操作(Read / Write) IStorageFile 類型物件的方法。常用的方法如下:

Method Description
ReadBufferAsync Reads the contents of the specified file and returns a buffer.
ReadTextAsync(IStorageFile) Reads the contents of the specified file and returns text.
WriteBufferAsync Writes data from a buffer to the specified file.
WriteBytesAsync Writes an array of bytes of data to the specified file.
WriteTextAsync(IStorageFile, String) Writes text to the specified file.

更多詳細說明可以參考<File access and permissions in Windows Store apps>與<Quickstart: Reading and writing a file>。

Windows.Storage.FileIO  簡化了過去在操作 StorageFile 需要花時間建立 Stream、轉換 DataReader / DataWriter …等,讓整個程式撰寫更加簡易。

   

[注意]

a. 在 Windows Store App 裡面可以請求 Windows indexes 為 local store 中的資料建立索引,方法:

    1) 先在 Local Folder 下先建立一個「Indexed 目錄」;

    2) 將需要被建立索引的檔案放在「Indexed 目錄」;

    3) Windows indexes 將會把 「Indexed 目錄」下的檔案與子目錄下的檔案取得並建立對應的 metadata;

   

    

B. ApplicationData.LocalSettings

         取得 local app data store 中的 application settings container。LocalSettings 是 ApplicationDataContainer

透過 ApplicationDataContainer.Values | values 取得 localsettings container 中的設定值。 而這個 Values 是 ApplicationDataContainerSettings

該內容採用 IDictionary<String, Object> 建立,所以資料是放在這裡。ApplicationDataContainer 它只是個容器,但容器裡還可以子容器喔。

 

由於該設定的組合非常特別,將花一點解釋操作 LocalSettings 需要了解的類別與作法。

 

ApplicationDataContainer

    代表應用程式設定的容器。此類別的屬性與方法支援創建、 刪除、列舉和瀏覽容器層次結構。

該類別是非常特別的,因為過去操作的「IsolatedStorageSettings.ApplicationSettings」只是一個單純 Key/Value 的容器,

ApplicationDataContainer 還可以建立子容器來建立需要的資料。透過下列常用的方法與屬性來說明:

類型 名稱 說明
Method CreateContainer 在目前 settings container 開啟或建立指定的 settings container。
在建立或開啟 containers 有一個額外列舉參數:ApplicationDataCreateDisposition 需要注意。

ApplicationDataCreateDisposition
    指定 application data containers 建立的選項。有二個列舉值:
   
Member Value Description
Always |always 0 永遠啟用該容器,如果容器不存在則自動建立。
Existing |existing 1 只有在容器存在時才啟用該容器。
  DeleteContainer 刪除指定的 settings container,它的 subcontainers、application settings 將全部被刪除。
Property Containers Read-only,取得目前 application settings container 下的 child application settings containers。
  Locality Read-only,取得目前 settings container 關聯於 app data store 代表的類型,例如:local 或 roaming。
  Name Read-only,取得目前 settings container 的名稱。
  Values Read-only,取得目前 settings container 的 settings (ApplicationDataContainerSettings)。

 

範例程式:

private void btnAddNewContainer_Click(object sender, RoutedEventArgs e)
{
    ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
    // 設定新的 container 如果不存在就自動建立
    ApplicationDataContainer newContainer = localSettings.CreateContainer(
                                    "newContainer", ApplicationDataCreateDisposition.Always);
    if (newContainer.Values.ContainsKey("key1"))
    {
        newContainer.Values["key1"] = "Second Key1";
    }
    else
    {
        newContainer.Values.Add("key1", "First Key1");
    }
 
    String value = newContainer.Values["key1"].ToString();
 
    // 刪除 container
    localSettings.DeleteContainer("newContainer");
}

 

ApplicationDataContainerSettings 

    提供存取 settings container中的設定值。所以 LocalSettings | localSettings 屬性得到的是 ApplicationDataContainer 物件,

透過 ApplicationDataContainerSettings 來操作,如下程式範例:

var localSettings = Windows.Storage.ApplicationData.Current.LocalSettings;
 
// Create a simple setting
 
localSettings.Values["exampleSetting"] = "Hello Windows";
 
// Read data from a simple setting
 
Object value = localSettings.Values["exampleSetting"];
 
if (!value)
{
    // No data
}
else
{
    // Access data in value
}
 
// Delete a simple setting
 
localSettings.Values.Remove("exampleSetting");

[注意]

a. ApplicationDataContainerSettings 設定的 setting name 最長 255 字元

b. 每個 setting value 最大 8K bytes,每個複合型 setting value 最大 64K bytes

c. 注意可放入的資料類型:Windows Runtime base data types

 

ApplicationDataCompositeValue

    Represents related app settings that must be serialized and deserialized atomically. 該類別實作 IDictionary<String, Object> 介面,

屬於集合性物件,在操作  ApplicationDataContainerSettings 時,可以新增一個新的 setting 用類型的物件來儲存以擴充內容(最大 64K bytes)。

該物件被保存時會自動序列化裡面的內容,取出去會再反序列化回來,搭配 Key/Value 的方式來操作。

如果 Value 是給予自定義的類別要記得宣告需要或不可以被序列化的屬性,以免出現 Exception。

操作方式如下:

private void btnAddDataLocalSetting_Click(object sender, RoutedEventArgs e)
{
    ApplicationDataContainer localSettings = ApplicationData.Current.LocalSettings;
    // 建立一個 ApplicationDataCompositeValue  物件
    ApplicationDataCompositeValue composite = new ApplicationDataCompositeValue();
    composite.Add("name", "pou");
    composite.Add("age", "90");
    if (localSettings.Values.ContainsKey("com1"))
    {
        localSettings.Values["com1"] = composite;
    }
    else
    {
        localSettings.Values.Add("com1", composite);
    }
    localSettings.Values.Remove("com1");
}

 

 

 

》Roaming app data

    在介紹 Roaming Folder 與 Roaming Settings 前,先來了解一下 Roaming data 的用途:

在 App 裡使用 Roaming data (檔案或設定),用戶可以簡單地保留 App data,並且同步至不同的設備之中」。

如果用戶安裝您的 App 在第二台設備以上,系統會自動將 app data 進行同步,減少用戶在安裝 App 後需要額外設定的工作,

但前提是您的 App 本身也要處理 Roaming data 的變動與事件。

有了 Roaming 同步多設備中 App 的資料,用戶就可以在手機還沒有玩完的 game 換到 PC 又可以繼續接著,或是任務繼續延續。

 

要達到這樣的功能,需要幾個條件:

1. 同一個 App;

    =>Windows Store App 與 Windows Phone App 需要在 Dev center 註冊相同的名稱,這樣才能同步 roaming data,

        可參考<Windows Phone 8.1 - App 封裝方式>;

2. 同一個 Microsoft Account 所購買;

3. 同步的設備為 Windows Store 與 Windows Phone 的 App;

 

Windows 在支援 roaming data 有一些規範,在儲存資料至該目錄或設定時要特別注意:

1. 檔案與目錄的命名不可以有前導空格 (leading whitespace);

2. 限制每一個 App 可以被 Roaming 的大小 (藉由 ApplicationData.RoamingStorageQuota | roamingStorageQuota 來得知);

    =>如果 App 的用量超過這個限制,將不會再有資料會被複製到雲端,需等到 App 的總 roamed app data 低於限制,才會再次啟動

        尤於這樣,要做為 roamed app data 的類型最好是一些 user preferences、links、small data files

 

通常 App 不會希望自己的 app data 被 App 以外的事情來改變。但是 roaming app data 可能隨時被改變,所以需要註冊 ApplicationData

DataChanged | datachanged 事件,在每次 roaming app data 改變時,同步自己 app 中的對應資料。

 

如果 App 已安裝再透過用戶更新了新的版本,那 App 中的 app data 會被複製到 cloud。

系統不會更新到其他設備,需等到使用者更 新 App ,以及這些設備上的 App 上的 app data。

 

至於複制到雲端的 roaming app data (RoamingFolder)會被保存多久呢?目前是 30 天(time interval)。但有可能隨著政策而改變。

roaming data 提供 App 有能力在雲端保存的有限期間內將資料同步至不同的設備中。

如果用戶超過有限時間都沒有再回到 App,那雲端保存的資料將會被刪除

如果用戶移除了 App,雲端資料不會自動移除,等到有限期間到達才會自動移除,所以這之間用戶重新安裝仍可以取得資料。

 

同步的時機點呢?系統處理 roaming app data 不保證立即同步。使用者在離線使用 App 或網路連線速度不夠時,可能就會延遲。

注意,如果需要高優先順序的同步,希望能夠經常性同步的話,可以透過 RoamingSettings 增加一個命名為「HighPriority」的

設定它可以是複合的(composite)設定值,不過大小只有 8KB。如果該設定值超過了 8 KB,其系統不強制執行限制空間

 

更多使用 roaming app data 的說明可參考<Guidelines for roaming app data>。

 

A. RoamingFolder

     取得 roaming app data store 中的根目錄。透過「ms-appdata:///roaming/」protocol 來操作檔案或目錄。

操作方式:

private async void HandleRoamingFolder()
{
    StorageFolder roamingFolder = ApplicationData.Current.RoamingFolder;
 
    // 單位:KB
    ulong maxSize = ApplicationData.Current.RoamingStorageQuota;
 
    // 建立一個檔案,設定 CreationCollisionOption 如果既有檔案就覆寫
    StorageFile roamingFile = await roamingFolder.CreateFileAsync("roamingFile.txt", CreationCollisionOption.ReplaceExisting);
 
    String content = "Roaming data, write to Roaming folder.";
 
    // 利用 FileIO.WriteTextAsync 將內容寫入
    await FileIO.WriteTextAsync(roamingFile, content);
}
 
async Task ReadTimestamp()
{
    StorageFolder roamingFolder = ApplicationData.Current.RoamingFolder;
    try
    {
        StorageFile roamingFile = await roamingFolder.GetFileAsync("roamingFile.txt");
        String content = await FileIO.ReadTextAsync(roamingFile);
    }
    catch (Exception)
    {
        // 檔案不存在
    }
}

註冊 DataChanged | datachanged 事件:

public App()
{
   this.InitializeComponent();
   this.Suspending += this.OnSuspending;            
 
   // 註冊要處理 Back Button 的事件邏輯。
   Windows.Phone.UI.Input.HardwareButtons.BackPressed += HardwareButtons_BackPressed;
 
   this.Resuming += App_Resuming;
   ApplicationData.Current.DataChanged += Current_DataChanged;
}
 
void Current_DataChanged(ApplicationData sender, object args)
{
   // 從 sender 中取得更新的資料;            
}

 

 

B. RoamingSettings

     取得 roaming app data store 中的 application settings containers。它與 LocalSettings 的使用完全相同,本身也是一個 ApplicationDataContainer

程式範例:

private void HandleRoamingSettings()
{
    ApplicationDataContainer roamingSettings = ApplicationData.Current.RoamingSettings;
    // 設定新的 container 如果不存在就自動建立
    ApplicationDataContainer newContainer = roamingSettings.CreateContainer(
                                    "newContainer", ApplicationDataCreateDisposition.Always);
    if (newContainer.Values.ContainsKey("key1"))
    {
        newContainer.Values["key1"] = "Second Key1";
    }
    else
    {
        newContainer.Values.Add("key1", "First Key1");
    }
 
    String value = newContainer.Values["key1"].ToString();
 
    // 刪除 container
    roamingSettings.DeleteContainer("newContainer");
}

 

 

 

》Temporary app data

    Temporary app data 就像是 cache。它的檔案不會被 roam,而且可能隨時被清除。清除可能來自:

(1) 系統自動清除 (System Maintenance task);

(2) 用戶手動透過工作清除 (Disk Cleanup);

所以通常被用來保存臨時計算用的檔案或資料,用完即丟的目的。

 

A. TemporaryFolder

     取得在 temporary app data store 中的根目錄。該目錄下所建立的檔案或目錄,有可以隨時會被系統清除。

可透過 protocol 的方式進行存取:「<img src="ms-appdata:///temp/myFile.png" alt="" />」。操作方式如下:

private async void HandleTemporaryFolder()
{
    StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
 
    // 建立一個檔案,設定 CreationCollisionOption 如果既有檔案就覆寫
    StorageFile sampleFile = await tempFolder.CreateFileAsync("sample.txt", CreationCollisionOption.ReplaceExisting);
 
    String content = "temporary data, write to temporary folder.";
 
    // 利用 FileIO.WriteTextAsync 將內容寫入
    await FileIO.WriteTextAsync(sampleFile, content);
}
 
async void TemporaryFolder()
{
    try
    {
        StorageFolder tempFolder = ApplicationData.Current.TemporaryFolder;
        StorageFile sampleFile = await tempFolder.GetFileAsync("sample.txt");
        String content = await FileIO.ReadTextAsync(sampleFile);
    }
    catch (Exception)
    {
        // 檔案不存在
    }
}

 

 

ApplicationData.LocalCacheFolder

    僅 Windows Phone 支援的目錄。該目錄儲存的檔案與目錄將不會被備份與還原。

 

 

[補充]

a. WP 8.1 與 Store App 的操作有些不同,可參考<File access and permissions (Windows Runtime apps)>的說明,如下:

    InternetClient
    InternetClientServer

‧And, if applicable, the domain credentials capability:
    EnterpriseAuthentication

You must add File Type Associations to your app manifest that declare specific file types that your app can access in this location.
Location Capability Windows.Storage API
Documents
(Windows Store app only)
DocumentsLibrary
You must add File Type Associations to your app manifest that declare specific file types that your app can access in this location.

Use this capability if your app:
‧Facilitates cross-platform offline access to specific OneDrive content using valid OneDrive URLs or Resource IDs

‧Saves open files to the user’s OneDrive automatically while offline
KnownFolders.DocumentsLibrary
Download
(Windows Store app only)
By default, your app can only access files and folders in the user's Downloads folder that your app created. DownloadsFolder
Music MusicLibrary
For more info about accessing media libraries from a Windows Phone app, seeAccess media libraries in Windows Phone apps.
KnownFolders.MusicLibrary
Pictures

PicturesLibrary

For more info about accessing media libraries from a Windows Phone app, seeAccess media libraries in Windows Phone apps.

KnownFolders.PicturesLibrary
Videos VideosLibrary
For more info about accessing media libraries from a Windows Phone app, seeAccess media libraries in Windows Phone apps.
KnownFolders.VideosLibrary
Removable devices RemovableDevices
You must add File Type Associations to your app manifest that declare specific file types that your app can access in this location.

For more info about accessing an SD card from a Windows Phone app, seeAccess the SD card in Windows Phone apps.
KnownFolders.RemovableDevices
Homegroup libraries
(Windows Store app only)
At least one of the following capabilities is needed.
MusicLibrary
PicturesLibrary
VideosLibrary
KnownFolders.HomeGroup
Media Server devices (DLNA)
(Windows Store app only)
At least one of the following capabilities is needed.
MusicLibrary
PicturesLibrary
VideosLibrary
KnownFolders.MediaServerDevices
Universal Naming Convention (UNC) folders
(Windows Store app only)
A combination of the following capabilities is needed.

‧The home and work networks capability:
    PrivateNetworkClientServer

‧And at least one internet and public networks capability:
Retrieve a folder using:
StorageFolder.GetFolderFromPathAsync

Retrieve a file using:
StorageFile.GetFileFromPathAsync

 

======

以上是介紹操作 ApplicationData 的幾個重要的類別與規範。希望對入門 WP 8.1 開發的人有所幫助。

其中我覺得如果您有開發 Universal app 的專案,Roaming app 真的是不錯的用途,雖然可用的資料量不大,

但隨著設備的進化我相信還是夠用來同步一些 App 的設定與操作。

 

References:

Working with data and files (XAML)

Managing app data (XAML) (重要)

File access and permissions in Windows Store apps (重要)

Packaging your Store app by using Visual Studio

Developing apps (XAML)

Windows Phone Store APP Lifecycle and Background Processing

Launching, resuming, and multitasking (XAML)

Quickstart: Local app data (XAML)

How to index app local data (Windows Store app Only)

Quickstart: Roaming app data (XAML)

Quickstart: Temporary app data (XAML)

How to roam data between a Windows Store app and a Windows Phone Store app (重要)

Accessing data and files (XAML)

Sharing and exchanging data (XAML)

Transferring a file from a network resource (XAML)

Encrypting data and working with certificates (XAML)

Accessing app data with the Windows Runtime

Windows.Storage namespace

Windows.ApplicationModel namespace

Windows Phone 8.1 for Developers – Application Data (重要)

 

Dotblogs Tags: ,