將版本追蹤數據從 Xamarin.Forms 應用程式遷移至 .NET MAUI 應用程式
Xamarin.Essentials 和 .NET 多平臺應用程式 UI (.NET MAUI) 都有一個 VersionTracking
類別,可讓您檢查應用程式的版本和組建編號,以及其他資訊,例如,它是否是第一次啟動應用程式。 不過,在 Xamarin.Essentials 中,版本追蹤數據會儲存在具有 名稱 {your-app-package-id}.xamarinessentials.versiontracking
的平臺特定喜好設定容器中,而在 .NET MAUI 中,版本追蹤數據會儲存在具有 名稱 {your-app-package-id}.microsoft.maui.essentials.versiontracking
的平臺特定喜好設定容器中。
將使用 VersionTracking
類別的 Xamarin.Forms 應用程式移轉至 .NET MAUI 時,您必須處理此喜好設定容器命名差異,為使用者提供順暢的升級體驗。 本文說明如何使用 LegacyVersionTracking
類別和協助程序類別來處理喜好設定容器。 類別 LegacyVersionTracking
可讓您在 Android、iOS 和 Windows 上使用 .NET MAUI 應用程式,讀取使用舊版 Xamarin.Forms 應用程式所建立的版本追蹤數據。
重要
若要讓 LegacyVersionTracking
類別正常運作,您的 .NET MAUI 應用程式必須具有高於 Xamarin.Forms 應用程式版本號碼的版本號碼。 您可以使用 和 $(ApplicationDisplayVersion)
建置屬性,在 .NET MAUI 應用程式的專案檔$(ApplicationVersion)
中設定版本號碼。
如需 Xamarin.Essentials 中 類別 VersionTracking
的詳細資訊,請參閱 Xamarin.Essentials:版本追蹤。 如需 .NET MAUI 中 類別 VersionTracking 的詳細資訊,請參閱 版本追蹤。
存取舊版追蹤數據
下列程式代碼顯示 類別 LegacyVersionTracking
,可讓您存取 Xamarin.Forms 應用程式所建立的版本追蹤資料:
注意
若要使用此程式碼,請將它新增至 .NET MAUI 應用程式項目中名為 LegacyVersionTracking
的類別。
namespace MigrationHelpers;
public static class LegacyVersionTracking
{
const string versionsKey = "VersionTracking.Versions";
const string buildsKey = "VersionTracking.Builds";
static readonly string sharedName = LegacyPreferences.GetPrivatePreferencesSharedName("versiontracking");
static Dictionary<string, List<string>> versionTrail;
static string LastInstalledVersion => versionTrail[versionsKey].LastOrDefault();
static string LastInstalledBuild => versionTrail[buildsKey].LastOrDefault();
public static string VersionsKey => versionsKey;
public static string BuildsKey => buildsKey;
public static string SharedName => sharedName;
public static bool IsFirstLaunchEver { get; private set; }
public static bool IsFirstLaunchForCurrentVersion { get; private set; }
public static bool IsFirstLaunchForCurrentBuild { get; private set; }
public static string CurrentVersion => AppInfo.VersionString;
public static string CurrentBuild => AppInfo.BuildString;
public static string PreviousVersion => GetPrevious(versionsKey);
public static string PreviousBuild => GetPrevious(buildsKey);
public static string FirstInstalledVersion => versionTrail[versionsKey].FirstOrDefault();
public static string FirstInstalledBuild => versionTrail[buildsKey].FirstOrDefault();
public static IEnumerable<string> VersionHistory => versionTrail[versionsKey].ToArray();
public static IEnumerable<string> BuildHistory => versionTrail[buildsKey].ToArray();
public static bool IsFirstLaunchForVersion(string version) => CurrentVersion == version && IsFirstLaunchForCurrentVersion;
public static bool IsFirstLaunchForBuild(string build) => CurrentBuild == build && IsFirstLaunchForCurrentBuild;
static LegacyVersionTracking()
{
InitVersionTracking();
}
internal static void InitVersionTracking()
{
IsFirstLaunchEver = !LegacyPreferences.ContainsKey(versionsKey, sharedName) || !LegacyPreferences.ContainsKey(buildsKey, sharedName);
if (IsFirstLaunchEver)
{
versionTrail = new Dictionary<string, List<string>>
{
{ versionsKey, new List<string>() },
{ buildsKey, new List<string>() }
};
}
else
{
versionTrail = new Dictionary<string, List<string>>
{
{ versionsKey, ReadHistory(versionsKey).ToList() },
{ buildsKey, ReadHistory(buildsKey).ToList() }
};
}
IsFirstLaunchForCurrentVersion = !versionTrail[versionsKey].Contains(CurrentVersion) || CurrentVersion != LastInstalledVersion;
if (IsFirstLaunchForCurrentVersion)
{
// Avoid duplicates and move current version to end of list if already present
versionTrail[versionsKey].RemoveAll(v => v == CurrentVersion);
versionTrail[versionsKey].Add(CurrentVersion);
}
IsFirstLaunchForCurrentBuild = !versionTrail[buildsKey].Contains(CurrentBuild) || CurrentBuild != LastInstalledBuild;
if (IsFirstLaunchForCurrentBuild)
{
// Avoid duplicates and move current build to end of list if already present
versionTrail[buildsKey].RemoveAll(b => b == CurrentBuild);
versionTrail[buildsKey].Add(CurrentBuild);
}
}
static string GetPrevious(string key)
{
var trail = versionTrail[key];
return (trail.Count >= 2) ? trail[trail.Count - 2] : null;
}
static string[] ReadHistory(string key) => LegacyPreferences.Get(key, null, sharedName)?.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries) ?? new string[0];
}
類別 LegacyVersionTracking
會 LegacyPreferences
使用 類別,其可讓您從 Xamarin.Forms 應用程式存取 Xamarin.Essentials Preferences
類別所儲存的版本追蹤數據:
注意
若要使用此程式碼,請將它新增至 .NET MAUI 應用程式項目中名為 LegacyPreferences
的類別。
#if ANDROID || IOS || WINDOWS
namespace MigrationHelpers;
public static partial class LegacyPreferences
{
internal static string GetPrivatePreferencesSharedName(string feature) => $"{AppInfo.PackageName}.xamarinessentials.{feature}";
public static bool ContainsKey(string key, string sharedName) => PlatformContainsKey(key, sharedName);
public static void Remove(string key, string sharedName) => PlatformRemove(key, sharedName);
public static string Get(string key, string defaultValue, string sharedName) => PlatformGet<string>(key, defaultValue, sharedName);
}
#endif
類別 LegacyPreferences
是一個 partial
類別,其其餘實作是平臺特定的。
Android
在 Android 上,類別 LegacyPreferences
會提供喜好設定容器實作,以從共用喜好設定擷取數據。 下列程式碼顯示 LegacyPreferences
類別:
注意
若要使用此程式代碼,請將它新增至 .NET MAUI 應用程式專案的 Platform\Android 資料夾中名為 LegacyPreferences
的類別。
using System.Globalization;
using Android.Content;
using Android.Preferences;
using Application = Android.App.Application;
namespace MigrationHelpers;
public static partial class LegacyPreferences
{
static readonly object locker = new object();
static bool PlatformContainsKey(string key, string sharedName)
{
lock (locker)
{
using (var sharedPreferences = GetSharedPreferences(sharedName))
{
return sharedPreferences.Contains(key);
}
}
}
static void PlatformRemove(string key, string sharedName)
{
lock (locker)
{
using (var sharedPreferences = GetSharedPreferences(sharedName))
using (var editor = sharedPreferences.Edit())
{
editor.Remove(key).Apply();
}
}
}
static T PlatformGet<T>(string key, T defaultValue, string sharedName)
{
lock (locker)
{
object value = null;
using (var sharedPreferences = GetSharedPreferences(sharedName))
{
if (defaultValue == null)
{
value = sharedPreferences.GetString(key, null);
}
else
{
switch (defaultValue)
{
case int i:
value = sharedPreferences.GetInt(key, i);
break;
case bool b:
value = sharedPreferences.GetBoolean(key, b);
break;
case long l:
value = sharedPreferences.GetLong(key, l);
break;
case double d:
var savedDouble = sharedPreferences.GetString(key, null);
if (string.IsNullOrWhiteSpace(savedDouble))
{
value = defaultValue;
}
else
{
if (!double.TryParse(savedDouble, NumberStyles.Number | NumberStyles.AllowExponent, CultureInfo.InvariantCulture, out var outDouble))
{
var maxString = Convert.ToString(double.MaxValue, CultureInfo.InvariantCulture);
outDouble = savedDouble.Equals(maxString) ? double.MaxValue : double.MinValue;
}
value = outDouble;
}
break;
case float f:
value = sharedPreferences.GetFloat(key, f);
break;
case string s:
// the case when the string is not null
value = sharedPreferences.GetString(key, s);
break;
}
}
}
return (T)value;
}
}
static ISharedPreferences GetSharedPreferences(string sharedName)
{
var context = Application.Context;
return string.IsNullOrWhiteSpace(sharedName) ?
#pragma warning disable CS0618 // Type or member is obsolete
PreferenceManager.GetDefaultSharedPreferences(context) :
#pragma warning restore CS0618 // Type or member is obsolete
context.GetSharedPreferences(sharedName, FileCreationMode.Private);
}
}
iOS
在 iOS 上,類別 LegacyPreferences
會提供從 擷取數據的 NSUserDefaults
喜好設定容器實作。 下列程式碼顯示 LegacyPreferences
類別:
注意
若要使用此程式碼,請將它新增至 .NET MAUI 應用程式專案的 Platform\iOS 資料夾中名為 LegacyPreferences
的類別。
using Foundation;
using System.Globalization;
namespace MigrationHelpers;
public static partial class LegacyPreferences
{
static readonly object locker = new object();
static bool PlatformContainsKey(string key, string sharedName)
{
lock (locker)
{
return GetUserDefaults(sharedName)[key] != null;
}
}
static void PlatformRemove(string key, string sharedName)
{
lock (locker)
{
using (var userDefaults = GetUserDefaults(sharedName))
{
if (userDefaults[key] != null)
userDefaults.RemoveObject(key);
}
}
}
static T PlatformGet<T>(string key, T defaultValue, string sharedName)
{
object value = null;
lock (locker)
{
using (var userDefaults = GetUserDefaults(sharedName))
{
if (userDefaults[key] == null)
return defaultValue;
switch (defaultValue)
{
case int i:
value = (int)(nint)userDefaults.IntForKey(key);
break;
case bool b:
value = userDefaults.BoolForKey(key);
break;
case long l:
var savedLong = userDefaults.StringForKey(key);
value = Convert.ToInt64(savedLong, CultureInfo.InvariantCulture);
break;
case double d:
value = userDefaults.DoubleForKey(key);
break;
case float f:
value = userDefaults.FloatForKey(key);
break;
case string s:
// the case when the string is not null
value = userDefaults.StringForKey(key);
break;
default:
// the case when the string is null
if (typeof(T) == typeof(string))
value = userDefaults.StringForKey(key);
break;
}
}
}
return (T)value;
}
static NSUserDefaults GetUserDefaults(string sharedName)
{
if (!string.IsNullOrWhiteSpace(sharedName))
return new NSUserDefaults(sharedName, NSUserDefaultsType.SuiteName);
else
return NSUserDefaults.StandardUserDefaults;
}
}
Windows
在 Windows 上,類別 LegacyVersionTracking
提供從 擷取數據的 ApplicationDataContainer喜好設定容器實作。 下列程式碼顯示 LegacyPreferences
類別:
注意
若要使用此程式碼,請將它新增至 .NET MAUI 應用程式專案的 Platform\Windows 資料夾中名為 LegacyPreferences
的類別。
using Windows.Storage;
namespace MigrationHelpers;
public static partial class LegacyPreferences
{
static readonly object locker = new object();
static bool PlatformContainsKey(string key, string sharedName)
{
lock (locker)
{
var appDataContainer = GetApplicationDataContainer(sharedName);
return appDataContainer.Values.ContainsKey(key);
}
}
static void PlatformRemove(string key, string sharedName)
{
lock (locker)
{
var appDataContainer = GetApplicationDataContainer(sharedName);
if (appDataContainer.Values.ContainsKey(key))
appDataContainer.Values.Remove(key);
}
}
static T PlatformGet<T>(string key, T defaultValue, string sharedName)
{
lock (locker)
{
var appDataContainer = GetApplicationDataContainer(sharedName);
if (appDataContainer.Values.ContainsKey(key))
{
var tempValue = appDataContainer.Values[key];
if (tempValue != null)
return (T)tempValue;
}
}
return defaultValue;
}
static ApplicationDataContainer GetApplicationDataContainer(string sharedName)
{
var localSettings = ApplicationData.Current.LocalSettings;
if (string.IsNullOrWhiteSpace(sharedName))
return localSettings;
if (!localSettings.Containers.ContainsKey(sharedName))
localSettings.CreateContainer(sharedName, ApplicationDataCreateDisposition.Always);
return localSettings.Containers[sharedName];
}
}
取用舊版版本追蹤數據
類別 LegacyVersionTracking
可用來擷取使用您應用程式先前 Xamarin.Forms 版本所建立的 Android、iOS 和 Windows 版本追蹤資料:
#if ANDROID || IOS || WINDOWS
using MigrationHelpers;
...
string isFirstLaunchEver = LegacyVersionTracking.IsFirstLaunchEver.ToString();
string currentVersionIsFirst = LegacyVersionTracking.IsFirstLaunchForCurrentVersion.ToString();
string currentBuildIsFirst = LegacyVersionTracking.IsFirstLaunchForCurrentBuild.ToString();
string currentVersion = LegacyVersionTracking.CurrentVersion.ToString();
string currentBuild = LegacyVersionTracking.CurrentBuild.ToString();
string firstInstalledVer = LegacyVersionTracking.FirstInstalledVersion.ToString();
string firstInstalledBuild = LegacyVersionTracking.FirstInstalledBuild.ToString();
string versionHistory = String.Join(',', LegacyVersionTracking.VersionHistory);
string buildHistory = String.Join(',', LegacyVersionTracking.BuildHistory);
string previousVersion = LegacyVersionTracking.PreviousVersion?.ToString() ?? "none";
string previousBuild = LegacyVersionTracking.PreviousBuild?.ToString() ?? "none";
#endif
此範例示範如何使用 類別 LegacyVersionTracking
來讀取舊版版本追蹤數據。 不過,無法將此數據指派給 .NET MAUI 的 VersionTracking 類別,因為無法設定其屬性。 相反地,數據可以使用 方法寫入 .NET MAUI 喜好設定 WriteHistory
:
void WriteHistory(string key, IEnumerable<string> history)
{
Preferences.Default.Set(key, string.Join("|", history), $"{AppInfo.Current.PackageName}.microsoft.maui.essentials.versiontracking");
}
#if ANDROID || IOS || WINDOWS
WriteHistory(LegacyVersionTracking.VersionsKey, LegacyVersionTracking.VersionHistory);
WriteHistory(LegacyVersionTracking.BuildsKey, LegacyVersionTracking.BuildHistory);
#endif
將舊版版本追蹤數據寫入 .NET MAUI 喜好設定之後,就可以由 .NET MAUI 的 VersionTracking 類別取用:
var mauiVersionHistory = VersionTracking.Default.VersionHistory;
var mauiBuildHistory = VersionTracking.Default.BuildHistory;
然後,您可以從裝置移除舊版追蹤數據:
#if ANDROID || IOS || WINDOWS
LegacyPreferences.Remove(LegacyVersionTracking.VersionsKey, LegacyVersionTracking.SharedName);
LegacyPreferences.Remove(LegacyVersionTracking.BuildsKey, LegacyVersionTracking.SharedName);
#endif