450 likes | 583 Views
Application Lifecycle and State Management. Charles Petzold www.charlespetzold.com. Agenda. Launching and closing Isolated storage Activation and deactivation Tombstoning Page state and application state Obscuration Idle detection. Application Lifecycle. Phones run one app at a time
E N D
Application Lifecycle and State Management Charles Petzold www.charlespetzold.com
Agenda • Launching and closing • Isolated storage • Activation and deactivation • Tombstoning • Page state and application state • Obscuration • Idle detection
Application Lifecycle • Phones run one app at a time • Apps not in foreground are closed or deactivated • Either way, process is terminated (usually) • PhoneApplicationService fires lifecycle events • Launching and Closing • Activated and Deactivated • State is not maintained when app is closed or deactivated unless you take steps to maintain it • Except in certain situations (more later)
Launching and Closing • Application is closed when: • Application's first page is displayed, and… • User presses the phone's Back button • The only way to close an application! • Process is terminated; all state is lost • One exception: if Back button is pressed quickly, WP7 may reconnect to still-running process • But don't count on it
Launching and Closing Events // App.xaml <shell:PhoneApplicationService Launching="Application_Launching" Closing="Application_Closing" ... /> // App.xaml.cs void Application_Launching(object sender, LaunchingEventArgse) { } void Application_Closing(object sender, ClosingEventArgse) { }
Back Button • Every Windows phone has a Back button • Page fires BackKeyPress events when the Back button is pressed • From the UI Design and Interaction Guide for Windows Phone 7: Developers should only implement Back Button behaviors that navigate back or dismiss context menus or modal dialog boxes. All other implementations are prohibited.
Prompting Before Exiting BackKeyPress += new EventHandler<CancelEventArgs>(OnBackKeyPress); . . . private void OnBackKeyPress(object sender, CancelEventArgse) { MessageBoxResult result = MessageBox.Show("Are you sure?", "Confirm", MessageBoxButton.OKCancel); e.Cancel = !(result == MessageBoxResult.OK); }
Isolated Storage • System.IO.IsolatedStoragenamespace contains classes that provide access to virtual file system • IsolatedStorageFile • IsolatedStorageException • IsolatedStorageSettings • Stored in memory on the phone • Permanently deleted if app is uninstalled • Not subject to quotas
IsolatedStorageFile • Provides methods for: • Opening and closing isolated storage stores • Also known as "isostores" • Creating, opening, copying, enumerating, moving, renaming, and deleting isolated storage files • Creating, opening, enumerating, moving, renaming, and deleting isolated storage folders • AvailableFreeSpace property provides information about free space
GetUserStoreForApplication • IsolatedStorageFile method for accessing per-application stores Marketplace App 1 App 2 App 1 Store Phone App 2 Store
Writing to Isolated Storage using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("Settings.xml", FileMode.Create, store)) { using (StreamWriter writer = new StreamWriter(stream)) { // TODO: Write to stream with StreamWriter } } }
Reading from Isolated Storage using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { if (store.FileExists("Settings.xml")) { using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("Settings.xml", FileMode.Open, store)) { using (StreamReader reader = new StreamReader(stream)) { // TODO: Read from stream with StreamReader } } } }
Deleting an Isolated Storage File using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { if (store.FileExists("Settings.xml")) store.DeleteFile("Settings.xml"); }
Application Settings • Dictionary stored in isolated storage • Application scope • Accessed through IsolatedStorageSettings.ApplicationSettings property • Simplifies task of storing user preferences and other simple settings • For example, language preferences • Private to each application
Using ApplicationSettings // Write to the ApplicationSettings dictionary IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings; settings["Culture"] = "fr-fr"; // Read from the ApplicationSettings dictionary string culture; IsolatedStorageSettings settings = IsolatedStorageSettings.ApplicationSettings; if (settings.TryGetValue<string>("Culture", out culture)) HtmlPage.Window.Alert(culture);
Determining Available Space using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { Int64 available = store.AvailableFreeSpace; ... }
Isolated Storage Performance • Performance of CreateFile, OpenFile, and other methods degrades with large numbers of files • Rule of thumb: Segregate files into folders, with no more than 128 files per folder • "How Many Files are Too Many Files?" • http://appangles.com/blogs/mickn/wp7/?p=6 • Block reads and writes are more performant than lots of little reads and writes
Creating an Isolated Storage Folder using (IsolatedStorageFile store = IsolatedStorageFile.GetUserStoreForApplication()) { store.CreateDirectory("\XML Stuff"); using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream("\XML Stuff\Settings.xml", FileMode.Create, store)) { ... } }
Activation and Deactivation • Application is deactivated when: • User presses the phone's Start button • App launches a launcher or chooser • Device time-out locks the screen • Unless you specify otherwise • Deactivation usually means process is terminated • Notable exception: When certain choosers such as PhotoChooserTask are launched • Termination means all state is lost
Activated and Deactivated Events // App.xaml <shell:PhoneApplicationService Activated="Application_Activated" Deactivated="Application_Deactivated" ... /> // App.xaml.cs void Application_Activated(object sender, ActivatedEventArgs e) { } void Application_Deactivated(object sender, DeactivatedEventArgse) { }
Tombstoning • Occurs when app is deactivated and terminated • To restore app to same state when reactivated, persist state in application state or page state • Or use isolated storage if volume of data exceeds limits on application state and page state • Isolated storage is 30% to 50% slower • Use page's OnNavigatedFrom and OnNavigatedTo methods to save and restore state • App is allowed 10 seconds to tombstone data
Application State • Data store for tombstoningglobal data • Limited to approx. 1.5 MB per app • Dictionary accessed through PhoneApplicationService.State property • Available between Activated and Deactivated events, inclusive • Data must be serializable
Saving Application State void Application_Deactivated(object sender, DeactivatedEventArgse) { PhoneApplicationService.Current.State["x1"] = x1; PhoneApplicationService.Current.State["y1"] = y1; PhoneApplicationService.Current.State["x2"] = x2; PhoneApplicationService.Current.State["y2"] = y2; }
Restoring Application State void Application_Activated(object sender, ActivatedEventArgse) { if (PhoneApplicationService.Current.State.ContainsKey("x1")) x1 = (double)PhoneApplicationService.Current.State["x1"]; if (PhoneApplicationService.Current.State.ContainsKey("y1")) y1 = (double)PhoneApplicationService.Current.State["y1"]; if (PhoneApplicationService.Current.State.ContainsKey("x2")) x2 = (double)PhoneApplicationService.Current.State["x2"]; if (PhoneApplicationService.Current.State.ContainsKey("y2")) y2 = (double)PhoneApplicationService.Current.State["y2"]; }
Page State • Data store for tombstoning per-page data • Limited to 2 MB per page and 4 MB per app • Dictionary accessed through PhoneApplicationPage.Stateproperty • Available between OnNavigatedTo and OnNavigatedFrom methods, inclusive • Data must be serializable
Saving Page State protected override void OnNavigatedFrom(NavigationEventArgs e) { this.State["x1"] = x1; this.State["y1"] = y1; this.State["x2"] = x2; this.State["y2"] = y2; }
Restoring Page State protected override void OnNavigatedTo(NavigationEventArgs e) { if (this.State.ContainsKey("x1")) x1 = (double)this.State["x1"]; if (this.State.ContainsKey("y1")) y1 = (double)this.State["y1"]; if (this.State.ContainsKey("x2")) x2 = (double)this.State["x2"]; if (this.State.ContainsKey("y2")) y2 = (double)this.State["y2"]; }
Tombstoning a ListBox Control protected override void OnNavigatedFrom(NavigationEventArgse) { // Save the ListBoxcontrol's SelectedIndex in page state this.State["Index"] = ListBoxControl.SelectedIndex; } protected override void OnNavigatedTo(NavigationEventArgse) { // Restore the ListBoxcontrol's SelectedIndex if (this.State.ContainsKey("Index")) ListBoxControl.SelectedIndex= (int)this.State["Index"]; }
Tombstoning a Pivot Control // This doesn't work protected override void OnNavigatedFrom(NavigationEventArgse) { // Save the Pivot control's SelectedIndex in page state this.State["Index"] = PivotControl.SelectedIndex; } protected override void OnNavigatedTo(NavigationEventArgse) { // Restore the Pivot control's SelectedIndex if (this.State.ContainsKey("Index")) PivotControl.SelectedIndex = (int)this.State["Index"]; }
Tombstoning a Pivot Control, Cont. // This works protected override void OnNavigatedFrom(NavigationEventArgse) { // Save the Pivot control's SelectedIndex in page state this.State["Index"] = PivotControl.SelectedIndex; } // Handler for Pivot.Loaded event void OnPivotControlLoaded(object sender, RoutedEventArgs e) { // Restore the Pivot control's SelectedIndex if (this.State.ContainsKey("Index")) _index = (int)this.State["Index"]; }
Tombstoning a Panorama Control // This doesn't work protected override void OnNavigatedFrom(NavigationEventArgse) { // Save the Panorama control's SelectedIndex in page state this.State["Index"] = PanoramaControl.SelectedIndex; } protected override void OnNavigatedTo(NavigationEventArgse) { // Restore the Panorama control's SelectedIndex if (this.State.ContainsKey("Index")) PanoramaControl.SelectedIndex= (int)this.State["Index"]; }
Tombstoning a Panorama, Cont. // This works protected override void OnNavigatedFrom(NavigationEventArgse) { // Save the Panorama control's SelectedIndex in page state this.State["Index"] = PanoramaControl.SelectedIndex; } protected override void OnNavigatedTo(NavigationEventArgse) { // Restore the Panorama control's SelectedIndex if (this.State.ContainsKey("Index")) PanoramaControl.DefaultItem= PanoramaControl.Items[(int)State["Index"]]; }
Obscured and Unobscured Events • Fired by PhoneApplicationFrame • Obscured – Shell comes to the foreground • Unobscured – App returns to the foreground • Common causes: • Phone receives an incoming call • Screen times out and lock screen appears • If app has disabled idle detection • Some apps must handle these events to pass certification requirements
Handling Obscuration Events (Application.Current as App).RootFrame.Obscured += OnObscured; (Application.Current as App).RootFrame.Unobscured += OnUnobscured; . . . private void OnObscured(object sender, ObscuredEventArgs e) { // Application is obscured by shell (possibly an incoming call) VibrateController.Default.Stop(); // Sample action } private void OnUnobscured(object sender, EventArgs e) { // Application has returned to the foreground }
Idle Detection • By default, apps are deactivated when screen locks • Apps can continue running when screen locks by disabling ApplicationIdleDetectionMode • Primarily for apps that are slow to reactivate • Also for apps that need to run under lock (e.g., audio) • Obscured and Obscured events replace Activated and Deactivated events • Apps can also disable UserIdleDetectionMode to prevent screen from locking automatically
Disabling Application Idle Detection // Allow the app to run when screen is locked. Once disabled, // ApplicationIdleDetectionMode cannot be reenabledwhile the // app is running. PhoneApplicationService.Current.ApplicationIdleDetectionMode = IdleDetectionMode.Disabled;
Disabling User Idle Detection // Prevent screen from locking while this app runs PhoneApplicationService.Current.UserIdleDetectionMode= IdleDetectionMode.Disabled;
Detecting a Locked Screen (Application.Current as App).RootFrame.Obscured += OnObscured; (Application.Current as App).RootFrame.Unobscured += OnUnobscured; . . . private void OnObscured(object sender, ObscuredEventArgs e) { if (e.IsLocked) { // Screen is locked } } private void OnUnobscured(object sender, EventArgs e) { // Screen is no longer locked }
Questions? Charles Petzold www.charlespetzold.com