異步儲存程式設定
正如您在第6章“按鈕單擊”中發現的那樣,您可以将程式設定儲存在Application類維護的名為Properties的字典中。您在“屬性”字典中放置的任何内容都将在程式進入睡眠狀态時儲存,并在程式恢複或重新啟動時恢複。有時在更改時儲存此字典中的設定很友善,有時候等到在App類中調用OnSleep方法很友善。
還有另一種選擇:Application類有一個名為SavePropertiesAsync的方法,它允許您的程式在儲存程式設定時發揮更積極的作用。這允許程式随時儲存程式設定。如果程式稍後崩潰或通過Visual Studio或Xamarin Studio調試程式終止,則會儲存設定。
根據建議的做法,SavePropertiesAsync方法名稱上的Async字尾将此辨別為異步方法。它使用Task對象快速傳回,并将設定儲存在輔助執行線程中。
名為SaveProgramSettings的程式示範了這種技術。 XAML檔案包含四個Switch視圖和四個Label視圖,将Switch視圖視為二進制數字的數字:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:toolkit=
"clr-namespace:Xamarin.FormsBook.Toolkit;assembly=Xamarin.FormsBook.Toolkit"
x:Class="SaveProgramSettings.SaveProgramSettingsPage">
<ContentPage.Resources>
<ResourceDictionary>
<toolkit:BoolToStringConverter x:Key="boolToString"
FalseText="Zero"
TrueText="One" />
<Style TargetType="Label">
<Setter Property="FontSize" Value="Large" />
<Setter Property="HorizontalTextAlignment" Value="Center" />
</Style>
<Style TargetType="Switch">
<Setter Property="HorizontalOptions" Value="Center" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Grid VerticalOptions="CenterAndExpand">
<Label Text="{Binding Source={x:Reference s3},
Path=IsToggled,
Converter={StaticResource boolToString}"
Grid.Column="0" />
<Label Text="{Binding Source={x:Reference s2},
Path=IsToggled,
Converter={StaticResource boolToString}"
Grid.Column="1" />
<Label Text="{Binding Source={x:Reference s1},
Path=IsToggled,
Converter={StaticResource boolToString}"
Grid.Column="2" />
<Label Text="{Binding Source={x:Reference s0},
Path=IsToggled,
Converter={StaticResource boolToString}"
Grid.Column="3" />
</Grid>
<Grid x:Name="switchGrid"
VerticalOptions="CenterAndExpand">
<Switch x:Name="s3" Grid.Column="0"
Toggled="OnSwitchToggled" />
<Switch x:Name="s2" Grid.Column="1"
Toggled="OnSwitchToggled" />
<Switch x:Name="s1" Grid.Column="2"
Toggled="OnSwitchToggled" />
<Switch x:Name="s0" Grid.Column="3"
Toggled="OnSwitchToggled" />
</Grid>
</StackLayout>
</ContentPage>
Label元素上的資料綁定允許它們跟蹤Switch視圖的值:
儲存和檢索程式設定在代碼隐藏檔案中處理。 注意配置設定給Switch元素的Toggled事件的處理程式。 該處理程式的唯一目的是将設定存儲在屬性字典中 - 并且隻要其中一個Switch元素更改狀态,就可以使用SavePropertiesAsync儲存屬性字典本身。 字典鍵是Grid的Children集合中Switch的索引:
public partial class SaveProgramSettingsPage : ContentPage
{
bool isInitialized = false;
public SaveProgramSettingsPage()
{
InitializeComponent();
// Retrieve settings.
IDictionary<string, object> properties = Application.Current.Properties;
for (int index = 0; index < 4; index++)
{
Switch switcher = (Switch)(switchGrid.Children[index]);
string key = index.ToString();
if (properties.ContainsKey(key))
switcher.IsToggled = (bool)(properties[key]);
}
isInitialized = true;
}
async void OnSwitchToggled(object sender, EventArgs args)
{
if (!isInitialized)
return;
Switch switcher = (Switch)sender;
string key = switchGrid.Children.IndexOf(switcher).ToString();
Application.Current.Properties[key] = switcher.IsToggled;
// Save settings.
foreach (View view in switchGrid.Children)
view.IsEnabled = false;
await Application.Current.SavePropertiesAsync();
foreach (View view in switchGrid.Children)
view.IsEnabled = true;
}
}
本練習的目的之一是首先強調,使用await并不能完全解決異步操作所涉及的問題,其次,使用await可以幫助解決這些潛在的問題。
這是問題:每次Switch改變狀态時都會調用Toggled事件處理程式。可能是使用者很快就連續切換了幾個Switch視圖。也可能是SavePropertiesAsync方法很慢的情況。也許它比四個布爾值節省了更多的資訊。因為這種方法是異步的,是以當它仍在努力儲存以前的設定集合時,可能會再次調用它。
SavePropertiesAsync是否可以重入?當它仍在工作時能否安全地再次調用它?我們不知道,最好假設它不是。是以,處理程式在調用SavePropertiesAsync之前禁用所有Switch元素,然後在完成後重新啟用它們。因為SavePropertiesAsync傳回Task而不是Task ,是以沒有必要使用await(或ContinueWith)從方法中擷取值,但是如果要在方法完成後執行某些代碼,則必須使用它。
實際上,SavePropertiesAsync在這種情況下運作得如此之快,以至于很難判斷這種禁用和啟用Switch視圖是否正常工作!為了測試這樣的代碼,Task類的靜态方法非常有用。嘗試在SavePropertiesAsync調用之後插入此語句:
await Task.Delay(3000);
Switch元素被禁用另外3,000毫秒。 當然,如果異步操作确實需要很長時間才能完成,并且在此期間禁用了使用者界面,則可能需要顯示ActivityIndicator或ProgressBar。
Task.Delay方法可能看起來讓人想起許多年前在某些.NET代碼中可能使用的Thread.Sleep方法。 但是這兩種靜态方法是非常不同的。 Thread.Sleep方法挂起目前線程,在這種情況下,它将是使用者界面線程。 這正是你不想要的。 但是,Task.Delay調用模拟在指定時間段内運作的無操作輔助線程。 使用者界面線程未被阻止。 如果省略await運算符,Task.Delay似乎根本不會對程式産生任何影響。 與await運算符一起使用時,調用Task.Delay的方法中的代碼将在指定的時間段後恢複。