Windows 8 `ApplicationData.RoamingSettings` 與 `RoamingFolder` 讓應用資料自動同步至雲端 (OneDrive 背景同步),並下發至使用者登入相同 Microsoft 帳號的其他 Windows 裝置。**核心承諾**:使用者在裝置 A 設定偏好、暫停遊戲、閱讀至第 5 段落,換到裝置 B 繼續使用時,狀態自動恢復。本文完整解析 API、配額限制、同步機制、衝突處理與實戰模式。
| 特性 | RoamingSettings | RoamingFolder |
|---|---|---|
| 資料型態 | 鍵值對 (字串、數值、布林、日期、ApplicationDataCompositeValue、容器) | 檔案/資料夾 (任意二進位、文字、資料庫) |
| API 類別 | `ApplicationDataContainer` | `StorageFolder` |
| 同步觸發 | 資料變更後系統排程同步 (非即時) | 檔案建立/修改/刪除後排程同步 |
| 配額上限 | **共用 100 KB** (含 Settings + Folder 所有檔案總大小) | |
| 單一設定值上限 | 8 KB (含鍵名) | 無單檔上限 (但受總配額限制) |
| HighPriority 設定 | 專用 8 KB 單元 (`HighPriority` 容器),更頻繁同步 | 不適用 |
| 離線行為 | 本地快取,上線後合併同步 | 本地快取,上線後合併同步 |
| 資料保留 | 使用者 30 天未啟動應用 → 雲端資料刪除 (重新安裝可恢復) | 同左 |
void InitRoaming() {
var roamingSettings = ApplicationData.Current.RoamingSettings;
var roamingFolder = ApplicationData.Current.RoamingFolder;
// 當雲端資料同步下發時觸發 (含其他裝置修改、首次登入同步)
ApplicationData.Current.DataChanged += OnRoamingDataChanged;
}
private void OnRoamingDataChanged(ApplicationData sender, object args) {
// UI 執行緒更新
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {
RefreshUIFromRoamingSettings();
RefreshUIFromRoamingFiles();
});
}
var settings = ApplicationData.Current.RoamingSettings;
// 寫入
settings.Values["UserTheme"] = "Dark";
settings.Values["FontSize"] = 14;
settings.Values["LastSyncTime"] = DateTime.UtcNow;
// 讀取
object themeObj = settings.Values["UserTheme"];
string theme = themeObj?.ToString() ?? "Light"; // 預設值處理
int fontSize = (int)(settings.Values["FontSize"] ?? 12);
// 寫入複合值 (單一設定項,原子同步)
var composite = new ApplicationDataCompositeValue();
composite["ServerUrl"] = "https://api.example.com";
composite["ApiKey"] = "abc123";
composite["AutoRefresh"] = true;
composite["RefreshIntervalMin"] = 15;
settings.Values["ApiConfig"] = composite;
// 讀取
if (settings.Values["ApiConfig"] is ApplicationDataCompositeValue cfg) {
string url = (string)cfg["ServerUrl"];
bool auto = (bool)cfg["AutoRefresh"];
}
// 建立/取得容器
var container = settings.CreateContainer("UserProfile", ApplicationDataCreateDisposition.Always);
if (settings.Containers.ContainsKey("UserProfile")) {
var profile = settings.Containers["UserProfile"].Values;
profile["DisplayName"] = "John Chen";
profile["AvatarHash"] = "a1b2c3";
profile["NotificationEnabled"] = true;
}
settings.Values.Remove("ObsoleteKey");
settings.Containers["OldContainer"].Values.Remove("OldValue");
// 或刪除整個容器
settings.DeleteContainer("DeprecatedContainer");
var folder = ApplicationData.Current.RoamingFolder;
// 寫入檔案 (非同步)
async Task WriteRoamingFileAsync(string filename, string content) {
StorageFile file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
await FileIO.WriteTextAsync(file, content);
}
// 讀取檔案
async Task ReadRoamingFileAsync(string filename) {
try {
StorageFile file = await folder.GetFileAsync(filename);
return await FileIO.ReadTextAsync(file);
} catch (FileNotFoundException) {
return null; // 或預設內容
}
}
// 刪除檔案
await (await folder.GetFileAsync("old.dat")).DeleteAsync();
**超過配額 → 同步停止**,直到總大小降回 100 KB 以下。必須在設計階段預算:
| 項目 | 預估大小 | 說明 |
|---|---|---|
| RoamingSettings 簡單鍵值 | ~2 KB | 主題、語言、字體、布林開關等 |
| RoamingSettings 複合值 (API 設定、使用者偏好) | ~5 KB | 結構化設定原子化 |
| HighPriority 容器 | 8 KB (固定保留) | 關鍵狀態:最後閱讀位置、遊戲關卡、購物車 ID |
| RoamingFolder 檔案 (小型 JSON 設定檔、極小圖示) | ~85 KB | **嚴禁存放圖片、資料庫、大型日誌** |
async Task SafeWriteRoamingFileAsync(string filename, byte[] content) {
// 1. 預估新增大小
ulong newSize = (ulong)content.Length;
// 2. 計算目前使用量 (同步呼叫,快速)
// 注意:ApplicationData.Current.RoamingStorageQuota 僅提供總配額 (100KB)
// 需自行維護目前使用量估算,或捕捉寫入失敗例外
try {
var file = await folder.CreateFileAsync(filename, CreationCollisionOption.ReplaceExisting);
await FileIO.WriteBytesAsync(file, content);
} catch (Exception ex) when (ex.HResult == 0x80070070) { // ERROR_DISK_FULL / 配額超限
// 降級策略:刪除舊快取、壓縮內容、改存 LocalFolder
await FallbackToLocalAsync(filename, content);
}
}
// 核心類別
public sealed class RoamingManager {
private readonly ApplicationDataContainer _settings = ApplicationData.Current.RoamingSettings;
private readonly StorageFolder _folder = ApplicationData.Current.RoamingFolder;
// 1. 高優先級狀態 (即時性要求高)
public async Task SaveReadingPositionAsync(string articleId, double scrollOffset) {
var hp = _settings.CreateContainer("HighPriority", ApplicationDataCreateDisposition.Always);
var composite = new ApplicationDataCompositeValue {
["ArticleId"] = articleId,
["ScrollOffset"] = scrollOffset,
["Timestamp"] = DateTime.UtcNow
};
hp.Values["ReadingPosition"] = composite; // 自動觸發高頻同步
}
// 2. 一般偏好設定
public void SetTheme(string theme) => _settings.Values["Theme"] = theme;
public string GetTheme() => _settings.Values["Theme"]?.ToString() ?? "Light";
// 3. 小型結構化資料 (JSON 檔案)
public async Task SaveFilterPresetAsync(string name, FilterPreset preset) {
string json = JsonSerializer.Serialize(preset);
await FileIO.WriteTextAsync(
await _folder.CreateFileAsync($"filter_{name}.json", CreationCollisionOption.ReplaceExisting),
json);
}
// 4. DataChanged 事件處理 (UI 更新)
public event Action RoamingDataUpdated;
public RoamingManager() {
ApplicationData.Current.DataChanged += (s, e) => RoamingDataUpdated?.Invoke();
}
}
漫遊資料是 **「輕量偏好 + 關鍵續用狀態」 的跨裝置橋樑**,而非「全量資料同步倉庫」。嚴守 **100 KB 配額**、**非即時同步**、**最後寫入者勝出** 三大鐵律,善用 `HighPriority` 8 KB 黃金空間處理即時性需求,配合 `LocalFolder`/`雲端儲存` 分流大型/敏感/高頻資料,即能打造「換裝置無感知、體驗無斷層」的現代應用體驗。