Windows 应用商店应用程序和 Windows Phone 应用商店应用程序之间的漫游数据
与数十年前的桌面应用程序相比,现在的应用程序的标准以及人们的预期完全不同。其中一个预期是应用程序可以跨设备运行并共享数据。如果用户在台式计算机和便携式计算机上安装了相同的应用程序,他们希望两个应用程序保持相同的配置,并使用相同的数据集。进一步而言,如果同一应用程序可以在多台设备上使用,则用户希望能够跨设备共享数据。
对于数据驱动的应用程序,您可以主要在后端数据库中处理这些预期。多台设备上的同一应用程序可以查询同一个远程数据库,且用户可以访问相同的数据。但是,支持应用程序访问远程数据库会在体系结构、开发和维护方面产生巨大开销。并非所有应用程序都需要数据库。即使是需要数据库的应用程序,可能也无需支持访问针对多用途设计的数据库中的应用程序特定信息。
借助漫游数据这一概念,Windows 应用商店和 Windows Phone 应用商店应用程序解决了这一需求。每个应用程序可为每个用户自动接收少量云存储。用户可以据此保存有关应用程序的信息,并在此应用程序的多个安装上共享。Windows Phone 应用商店应用程序和 Windows 应用商店应用程序更进一步,可允许用户在不同设备的应用程序之间共享数据。本文将讨论在应用程序中使用漫游数据以及如何跨设备高效使用此数据。
本文将使用通用应用程序来展示跨设备共享漫游数据,不过这些技术可适用于单个 Windows Phone 应用商店和 Windows 应用商店应用程序。漫游数据同样可以跨基于 HTML 和 XAML 的应用程序正常运行。
使用漫游数据
在应用程序中使用漫游数据的较好的一面是其自动可用,且无需配置或其他设置。只需使用可用的漫游数据 API,即可让应用程序利用漫游数据。
漫游的内容:大多数开发人员询问的首个问题是哪些类型的数据适用于漫游设置。请记住,可以漫游的数据量大小有严格限制。即您需要提前规划。
要漫游的最常见的内容包括用户定义的首选项和设置,如颜色、字体和选项等方面。如果在 Windows 应用商店应用程序和 Windows Phone 应用商店应用程序之间共享,这些值可能会具有不同的含义,但在平台之间提供相似的体验是应用程序的重大胜利。
应用程序中的当前导航是一项强大的功能。如果用户从 Windows Phone 中打开一个报告,然后登录到正在运行的计算机,该应用程序的 Windows 应用商店版本为什么不直接跳转到同一报告?如果用户正在台式计算机上观看视频,Windows Phone 版本为什么不跳转到同一视频?
临时数据可以作为漫游的另一备选。如果用户正在便携式计算机上键入电子邮件,他们应该能够在其台式计算机上继续完成同一电子邮件。
通常,大型数据集不作为漫游数据的备选。但是,您可以在漫游数据中为数据集设置密钥,然后根据共享的密钥将该大型数据集提取到新客户端。
像这样的例子不胜枚举,但其目的都是相同的。漫游数据应该让用户感到始终与应用程序保持连接。
启用漫游数据:应用程序要成功地在设备之间同步数据,有两个前提条件。第一,用户必须使用 Microsoft 帐户登录设备。漫游设置与应用程序和 Microsoft 用户帐户相关联。如果用户不使用 Microsoft 帐户,则数据缺少部分密钥。
第二,用户未禁用设备的漫游数据功能,这一点很重要。用户可以手动操作或者系统管理员可能应用某个设备策略。如果禁用了漫游数据,则数据无法同步。
尽管设备上未启用漫游数据,但数据在本地仍然可用。因此,您的应用程序无须担心确认漫游数据是否正在同步并使用其他工作流。
探索 API
您可以在 Windows.Storage.ApplicationData 对象中找到漫游数据的 API。每个应用程序将保留您可以使用静态 Current 属性引用的 ApplicationData 的单个实例:
var appData = Windows.Storage.ApplicationData.Current;
该 API 不包含强制漫游数据进行同步的机制。强制同步的过程由设备自行管理。
您可以使用两种类型的漫游数据。第一种类型位于 ApplicationData 的 RoamingSettings 属性中,为管理键/值对的 ApplicationDataContainer。这些设置可在 RoamingSettings.Values 属性中进行管理,且可以作为字符串索引数组访问。键可以为任意字母数字字符串,最多包含 255 个字符。只要值是受支持的 Windows 运行时数据类型,都可以成为对象。表示您不能在漫游设置中存储自定义对象。
您可以通过索引的 Values 属性来访问漫游设置。可通过将键索引的 Values 属性更改为新值来添加或更新设置。使用 Values.Remove 方法来删除设置。以下代码展示了创建、读取和删除漫游设置的示例:
var roamingSettings = ApplicationData.Current.RoamingSettings;
// Create setting
roamingSettings.Values["setting1"] = "My First Setting";
// Read setting
var settings1 = roamingSettings.Values["setting1"];
// Remove setting
roamingSettings.Values.Remove("setting1");
// Clear all settings
roamingSettings.Values.Clear();
存储简单的 Windows 运行时数据类型将适用于某些实例。但是,有时存储整个对象才有意义。虽然有几个方法可以用来在漫游数据中存储类,但您可以使用 ApplicationDataCompositeValue 在 RoamingSettings 中存储更复杂的对象。
ApplicationDataCompositeValue 是存储在一起的键/值对的集合。这是对作为一个单元保持同步的项目进行分组的好方法。以下代码展示了创建 ApplicationDataCompositeValue 并将其添加到 RoamingSettings 的示例:
var compositeValue = new ApplicationDataCompositeValue();
compositeValue["firstName"] = "Tony";
compositeValue["lastName"] = "Champion";
compositeValue["age"] = 38;
roamingSettings.Values["personalInfo"] = compositeValue;
此方法的一个缺点是没有自动转到 ApplicationDataCompositeValue,或从复杂对象转到 ApplicationDataCompositeValue 的机制。解决此问题的一个方法是为处理转换的类创建 helper 函数。图 1 显示了包含两个静态方法(ToCompositeSetting 和 FromCompositeSetting)的 Person 类。这些方法将上一示例中存储的数据转换为 Person 对象,使数据绑定之类的操作变得简单。
图 1 将 ApplicationDataCompositeValue 转换成 Person 类
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
public static Person
FromComposite(ApplicationDataCompositeValue composite)
{
return new Person()
{
FirstName = (string)composite["firstName"],
LastName = (string)composite["lastName"],
Age = (int)composite["age"]
};
}
public static ApplicationDataCompositeValue ToComposite(Person person)
{
var composite = new ApplicationDataCompositeValue();
composite["firstName"] = person.FirstName;
composite["lastName"] = person.LastName;
composite["age"] = person.Age;
return composite;
}
}
图 2 使用新 Person 类来获取个人信息并存储到 RoamingSettings 中。
图 2 获取个人信息并存储到 RoamingSettings 中
var person = new Person()
{
FirstName = "Tony",
LastName = "Champion",
Age = 38
};
roamingSettings.Values["personalInfo"] = Person.ToComposite(person);
if (roamingSettings.Values.ContainsKey("personalInfo"))
{
var composite =
(ApplicationDataCompositeValue)roamingSettings.Values["personalInfo"];
var roamingPerson = Person.FromComposite(composite);
}
需立即同步的数据可以使用漫游设置中的专用密钥。向任何设置中添加 HighPriority 将尽可能快地进行同步。此功能对以下项目非常有用:书本的当前页数、暂停的视频帧以及其他任何可以帮助在设备之间提供连接体验的内容等。例如,如果用户正在 Windows 应用商店应用程序中观看电影,您可以为 Windows Phone 应用商店应用程序提供包含与用户在电影中的当前位置匹配的支持数据:
var roamingSettings = ApplicationData.Current.RoamingSettings;
var composite = new ApplicationDataCompositeValue();
composite["movieId"] = myVidPlayer.MovieId;
composite["position"] = myVidPlayer.CurrentTime;
roamingSettings.Values["HighPriority"] = composite;
第二种漫游数据类型是文件。ApplicationData 对象包含返回 RoamingFolder 实例的 RoamingFolder 属性,而您的应用程序可在该实例中读写要同步的文件。
实际上,您可以向 RoamingFolder 中添加任何类型的文件。不过,文件名必须符合特定的规范。首先,文件名和扩展名的最大长度为 256 个字符。文件名也不能包含前导空格。此外,有一组 Unicode 字符不允许使用。
还有数种文件类型也不允许使用,因为他们的作用类似于文件夹(如 .zip 和 .cab)。如果您向 RoamingFolder 中添加的文件不满足上述要求,该文件将无法同步,但仍可在本地访问。
RoamingFolder 可用于存储复杂对象。采用同一个 Person 对象、对其序列化并将其写入 RoamingFolder 中的文件的示例如下:
var roamingFolder = ApplicationData.Current.RoamingFolder;
// Create file and open a stream to write to
StorageFile personFile = await roamingFolder.CreateFileAsync(
"personInfo.txt", CreationCollisionOption.ReplaceExisting);
var writeStream = await personFile.OpenStreamForWriteAsync();
// JSON serialize object
var serializer = new DataContractJsonSerializer(typeof(Person));
serializer.WriteObject(writeStream, person);
// Flush the stream
await writeStream.FlushAsync();
出于大小方面的考虑,将在 XML 上使用 JSON 序列化。由于漫游数据有大小限制,因此每个字节都很重要。
您可以使用反向逻辑从 RoamingFolder 检索对象。以下代码展示了读取同一文件并返回 Person 对象:
// Create file and open a stream to write to
var readStream = await
roamingFolder.OpenStreamForReadAsync("personInfo.txt");
// JSON deserialize object
var serializer = new DataContractJsonSerializer(typeof(Person));
var roamingPerson = (Person)serializer.ReadObject(readStream);
现在,您可以读写要在设备之间同步的漫游数据,但您如何得知这些数据何时进行更新?这可以由 ApplicationData 中的 DataChanged 事件实现。设备接收到新漫游数据时,会触发 DataChanged 事件,并传递到更新的 ApplicationData 对象。当数据发生更改时,这可以让您对应用程序进行调整。没有相应的事件可以让您得知何时从设备中推送数据。以下代码展示了如何侦听 DataChanged 事件:
private void HandleEvents()
{
ApplicationData.Current.DataChanged += Current_DataChanged;
}
void Current_DataChanged(ApplicationData sender, object args)
{
// Update app with new settings
}
如果应用程序正在使用 DataChanged 事件,则 ApplicationData 中的 SignalDataChanged 方法可以派上用场。您在本地更新任何漫游数据时,可以调用该方法,该方法将触发 DataChanged 事件,并允许运行您所需的所有更新处理程序:
// Update settings locally and raise DataChanged event
roamingSettings.Values["favoriteColor"] = "Black";
ApplicationData.Current.SignalDataChanged();
记录要漫游的数据量很重要。对于可在设备之间同步的数据量,每台设备都上限,当前是 100KB。可能的问题是这是一个全有或全无型方法。如果您要同步的数据总量超出该限制,则设备之间将不同步任何数据。ApplicationData 类包含的 RoamingStorageQuota 属性将返回允许同步的数据总大小(以千字节为单位)。但是,ApplicationData 不包含确定您当前使用的数据量的任何机制,因此目前只能由您进行记录。
应用程序的新版本可能表示设置为新设置或基于上一版本发生了更改,且不再需要上一版本的数据。要解决此问题,Windows 运行时允许您对漫游数据进行版本控制。为应用程序设置的当前漫游数据版本位于 ApplicationData.Current.Version 中。该版本号独立于应用程序版本号,默认设置为 0。应用程序将同步与其版本号匹配的数据。这样您可以创建新的数据结构,而无需担心破坏低版本。
您可以通过 ApplicationData.SetVersionAsync 方法来更改应用程序版本。此方法包含两个参数:新版本号和 ApplicationDataSetVersionHandler,以便您编写基于新版本更改应用程序所需的代码。
处理程序包含一个 SetVersionRequest 参数。可通过 CurrentVersion 属性提供当前版本,并通过 DesiredVersion 属性提供新版本。应用程序可以使用这两个值来处理基于迭代方法的所有迁移。它还包含 GetDeferralMethod,其可以在迁移完成之前使线程一直保持开启状态。这样如果有任何异步调用(例如读取或写入文件),您可以在版本更改过程完成之前执行相关函数。图 3 展示了如何迁移到新版本。
图 3 更新漫游数据的版本
void SetVersionHandler(SetVersionRequest request)
{
SetVersionDeferral deferral = request.GetDeferral();
if (request.CurrentVersion < 1)
{
// Handle upgrade from 0 to 1
}
if (request.CurrentVersion < 2)
{
// Handle upgrade from 1 to 2
}
deferral.Complete();
}
async void SetVersion()
{
var appData = ApplicationData.Current;
if (appData.Version < 2)
{
await appData.SetVersionAsync(
2, new ApplicationDataSetVersionHandler(SetVersionHandler));
}
}
在 Windows 应用商店和 Windows Phone 应用商店应用程序之间共享
现在,您已完成 Windows 应用商店和 Windows Phone 应用商店应用程序中的漫游设置,下一逻辑步骤是使两个伴生应用程序能够共享漫游数据。
从技术方面而言,为了使 Windows Phone 应用商店应用程序与 Windows 应用商店应用程序共享漫游数据,它们必须含有相同的包系列名称。此字段将基于包名称生成。因此,两个应用程序必须含有相同的包名称。通过在其各自的应用商店中将应用程序与名称建立关联,您可以找到这些值。
在任何一个应用商店中提交应用程序的首要步骤是为应用程序指定名称。您可以创建新名称,或使应用程序与现有名称关联。现有名称列表是您所在应用商店中保留的名称以及您在其他应用商店中的应用程序列表的组合。选择其他应用商店中的应用程序将链接您的应用程序,为其指定相同的名称,并允许应用程序共享漫游数据。您可以访问 bit.ly/10Pl2Xi,详细了解链接 Windows 应用商店和 Windows Phone 应用商店应用程序的意义。
由于应用商店之间仍然是独立的,因此链接应用程序的体验也稍有不同。如果您已有 Windows 应用商店应用程序,则可以在“应用程序信息”部分选择 Windows 应用商店应用程序来链接 Windows Phone 应用商店应用程序(如图 4 所示)。您还可以将 Windows 应用商店应用程序链接到“应用程序名称”部分中的现有 Windows Phone 应用商店应用程序(如图 5 所示)。
图 4 将 Windows Phone 应用商店应用程序链接到 Windows 应用商店应用程序
图 5 将 Windows 应用商店应用程序链接到 Windows Phone 应用商店应用程序
创建和链接应用商店中的应用程序后,最后一步是将 Visual Studio 中的每个应用程序与这些名称相关联。在主要 Windows 应用商店和 Windows Phone 应用商店解决方案上右键单击,并选择“应用商店 | 将应用程序与应用商店关联”。然后按照向导提示操作,并选择正确的名称。这将使用在应用商店中输入的信息更新 Package.appxmanifest。
此时,您的应用程序即可共享漫游数据。请记住,记录要漫游的数据大小很重要。如果不同平台的存储配额不同,您需要以两个配额中较小的配额作为限制来进行规划。
调试漫游数据
测试漫游设置很简单。同样,这也是全有或全无型情况。不管漫游数据是否已在设备之间同步,在测试同步过程时,锁定设备将强制应用程序尝试同步其数据。如果未同步,则需要考虑以下事项:
- 数据未同步最常见的原因是应用程序已超出漫游存储配额。如果总大小超出限制,则应用程序将不会尝试同步其漫游数据。
- 如果数据存储在文件中,请确保已关闭所有文件处理程序。使文件保持打开状态将保持对文件的锁定,同时阻止同步。
总结
在应用程序中使用漫游数据可为用户提供持续的、始终为连接状态的体验。通过共享某台设备上的设置、首选项和当前应用程序状态并传递到其他设备,会让用户感到不管是什么设备,应用程序都是相同的。增加在 Windows 应用商店和 Windows Phone 应用商店应用程序之间共享数据的功能会增强这种体验,并提供更多机会。
Microsoft MVP Tony Champion现任 Champion DS 总裁,同时也是社区中活跃的演说家、博主和作者。他的博客位于 tonychampion.net。请通过 tony@tonychampion.net 与其联系。
衷心感谢以下 Microsoft 技术专家对本文的审阅:Robert Green