Georg fährt extra nach Berlin um Steve Ballmer zu treffen

31 Tage Mango | Tag #23: Das Ausführungsmodell

Dieser Artikel ist Tag #23 der Serie 31 Tage Mango von Jeff Blankenburg.

Der Originalartikel befindet sich hier: Day #23: Execution Model.

Dieser Artikel wurde in der Originalserie von Gastautor Samidip Basu geschrieben. Bei Twitter kann Samidip unter @samidip erreicht werden.

Sind wir ehrlich – unsere Smartphones können heutzutage ganz schön viel. Aber alle Emails, Integration von sozialen Diensten, Spiele und Anwendungen haben einen Preis: sie verbrauchen Kapazität der vergleichsweise kleinen Prozessoren und des Speichers und vor allem gehen sie zu Lasten der Batterielaufzeit. Es ist daher entscheidend für jedes mobile Betriebssystem und 3rd Party Anwendungen, verantwortungsvoll und schonend mit den Ressourcen des Smartphones umzugehen. Die Anwender sollen ein flüssiges Erlebnis mit ihrem Telefon haben, das zudem noch eine Weile ohne Aufladen anhält. Hier kommt das Ausführungsmodell ins Spiel. Für jeden Entwickler ist es wichtig, die verschiedenen Zustände und Ereignisse im Lebenszyklus einer Anwendung zu kennen und zu verstehen. In diesem Artikel werden wir über das Ausführungsmodell in Windows Phone reden und uns insbesondere anschauen, was sich daran seit der Veröffentlichung von Mango für die Anwender und für Entwickler geändert hat.

Der Windows Phone Weg

Während wir den ganzen Tag fröhlich zwischen verschiedenen Teilen des Windows Phone Betriebssystems und 3rd Party Anwendungen hin- und herspringen, vollzieht das Telefon eine wohldefinierte Verwaltung der Anwendungen vom Start bis zur Beendigung. Das Ausführungsmodell erlaubt dabei prinzipiell zu jeder Zeit nur die Ausführung einer Anwendung im Vordergrund. Wir werden zwar sehen, warum das für das Bedienerlebnis vorteilhaft ist, lassen Sie mich dennoch kurz das Gegenteil gedanklich durchspielen. Selbst wenn es möglich wäre, können Sie sich wirklich vorstellen, dass mehrere Anwendungen gleichzeitig im Vordergrund auf einem Windows Phone laufen? Ist der Platz auf dem Bildschirm nicht etwas zu klein für so etwas? Die Beschränkung auf eine Anwendung im Vordergrund macht einfach Sinn für die Größe des Bildschirms und um dem Anwender ein Bedienerlebnis ohne überflüssige Bedienelemente und ohne Ablenkungen zu bieten. Dort wo mehr Platz auf dem Bildschirm zur Verfügung steht, erlauben ähnliche Metro UI Betriebssysteme auch mehrere Anwendungen gleichzeitig im Vordergrund, wie bei den „Snapped Views“ in Windows 8. Aber ich schweife ab…

Die Entscheidung für immer nur eine Anwendung im Vordergrund hat natürlich Auswirkungen auf den Lebenszyklus einer Anwendung. Die meisten Windows Phone Anwender sind es gewöhnt, schnell zwischen 3rd Party Anwendungen und dem Betriebssystem selbst zu wechseln, sei es durch die Start/Zurück Buttons, durch Tiles und Toast Notifications oder sonstige Navigationsmöglichkeiten. Jede Windows Phone Anwendung durchläuft dabei mehrere Zustände, die jeweils durch entsprechende Events signalisiert werden. Details folgen.

Die Ausführungszustände

Im Wesentlichen durchläuft eine Windows Phone Anwendung die folgenden Zustände:

  • Running – in diesem Zustand ist die Anwendung auf der Bühne. Das Betriebssystem und der Anwender widmen der Anwendung die volle Aufmerksamkeit damit die Anwendung ihre Aufgaben erfüllen kann.
  • Dormant – sobald die Anwendung die Bühne verlässt, wechselt sie in den Zustand Dormant („schlafend“). Dieser Zustand ist neu in Windows Phone Mango. In diesem Zustand wird die ganze Anwendung inklusive ihres internen Zustandes und des Backstacks im Speicher gehalten. Das Ziel dabei ist, die Anwendung möglichst reaktiv zu halten, wenn der Benutzer zur Anwendung zurück wechselt. Dieses schnelle Zurückwechseln nennen wir in Windows Phone Mango Fast Application Switching (FAS).

Wollen Sie das ausprobieren? Sie brauchen dafür natürlich ein Gerät mit Mango oder den aktuellsten Emulator. Springen Sie hin und her zwischen verschiedenen 3rd Party Anwendungen und den verschiedenen Teilen des Betriebssystems und dann halten Sie den Zurück Button gedrückt… Sie werden etwas wie den folgenden Screenshot sehen:

Was Sie hier sehen, ist der Application Backstack, also alle Anwendungen, die sich im Dormant („schlafenden“) Zustand befinden. Diese sind bereit, durch ein Tippen wieder in den Vordergrund zu springen. Für den Anwender ist das Multi-Tasking. Für Sie als Entwickler ist es FAS… ein sehr schnelles Aufwecken der App ohne dass Sie etwas spezielles dafür tun müssen! Naja, abgesehen davon, dass Sie alte nicht-Mango Anwendungen mit dem neuesten Windows Phone SDK 7.1 neu kompilieren müssen. Das hat sofort zwei Auswirkungen – die alte „Resuming…“ Nachricht wird nicht mehr angezeigt, wenn die Anwendung aus dem Dormant State zurückkommt und Sie sehen einen Screenshot der Anwendung im Application Backstack. Wir werden gleich etwas Code dazu sehen.

  • Tombstoned – Wie wir gesehen haben, hält die Windows Phone Laufzeitumgebung einen Backstack von denjenigen Anwendungen im Hauptspeicher, die kürzlich im Vordergrund waren. Auch wenn die sofortige Wiederherstellung einer Anwendung natürlich eine feine Sache ist, hat jedes Smartphone nur einen begrenzten Hauptspeicher und das Betriebssystem braucht davon möglicherweise etwas um eine teure Operation durchzuführen. Was passiert in diesem Fall? Die Anwendungen werden aus dem Backstack geschmissen (die „älteste“ zuerst)! Mit anderen Worten werden sie eingefroren („tombstoned“) und verbrauchen nach diesem Zeitpunkt keine Systemressourcen mehr. Die letzte Behauptung ist technisch gesehen nicht ganz die Wahrheit, wie wir gleich sehen werden.

Wie wirkt sich das Tombstoning auf den Lebenszyklus einer Anwendung aus? Wenn der Benutzer Ihre Anwendung geöffnet hatte und dann mit ein paar ressourcenhungrigen Spielen weitergemacht hat, ist die Wahrscheinlichkeit hoch, dass Ihre Anwendung eingefroren wurde um Ressourcen freizugeben. Der Benutzer wird sich jedoch nach einer Weile entscheiden, den Backstack zurückzugehen und Ihre Anwendung fortzusetzen. Wie sollte sich die Anwendung verhalten? Können wir uns die Dinge merken, die der Benutzer zuletzt gemacht hat als er die Anwendung verlassen hat?

Die Antwort ist ja! Wir können dem Benutzer auf jeden Fall das Gefühl geben, dass er die Anwendung nie verlassen hat und er dort weitermachen kann, wo er aufgehört hat. Um diesen Effekt zu erzielen, müssen wir als Entwickler aber dafür sorgen, dass der Zustand der Anwendung über ihren gesamten Lebenszyklus richtig verwaltet wird. Im folgenden Code werden wir sehen, wie wir den Zustand der Anwendung wieder auftauen können, nachdem sie aus dem Tombstoned Modus zurückkehrt. Der Anwender bekommt dadurch den Eindruck, dass sie nie beendet wurde. Die Wahrheit ist, dass beim Einfrieren der Anwendung ein paar Daten (in Form eines Dictionary) über den Zustand der Anwendung und der Backstack der Seiten innerhalb der Anwendung vom Windows Phone Betriebssystem im Speicher abgelegt werden.

  • Terminated – Eine Anwendung in diesem Zustand verbraucht wirklich überhaupt keinen Hauptspeicher mehr. Nichts, auch keine Zustands-Dictionaries (über die wir später reden), wird aufbewahrt. Wird die Anwendung wieder verwendet, wird eine komplett neue Instanz erstellt. Eine Anwendung erreicht diesen Zustand, nachdem sie eingefroren wurde oder der Benutzer die Anwendung über den Zurück-Button verlassen hat oder die App durch eine unbehandelte Ausnahme beendet wurde.

Seiten und Ereignisse

Bevor wir uns den Ereignissen im Lebenszyklus einer Anwendung zuwenden, lassen Sie uns noch mal einen Schritt zurücktreten, damit wir sicher sind, dass die Grundlagen stimmen. Alle Windows Phone Silverlight Anwendungen bestehen aus einer Sammlung von XAML Seiten, die innerhalb des Phone Application Frame geladen werden. Solange die Anwendung im Vordergrund ist, kann der Benutzer frei zwischen den Seiten innerhalb der Anwendung navigieren. Durch Drücken des Back-Buttons traversiert der Benutzer den Backstack der Seiten innerhalb der Anwendung bis er die erste Seite der Anwendung erreicht. Drückt er jetzt noch mal den Back-Button, so wird die Anwendung beendet. Als Entwickler müssen wir für diese Funktionalität nichts machen, da das Windows Phone Betriebssystem dieses Verhalten während der Laufzeit der Anwendung automatisch bereitstellt.

Denken Sie an folgende Situation: der Benutzer gibt etwas in einer Textbox auf einer XAML Seite ein, wird abgelenkt und verlässt die Anwendung um etwas anderes zu erledigen. Wenn er zurück kommt, sind jetzt zwei Szenarien denkbar. Erstens, die Anwendung könnte nur geschlafen haben (dormant) und springt sofort wieder in den Vordergrund. Die Benutzereingaben bleiben erhalten, ohne dass Ihre Anwendung dies extra unterstützen müsste außer sicherzustellen, dass sie gerade aus dem schlafenden Zustand aufgeweckt wurde (und deshalb beim Start eben nichts zu tun ist). Zweitens, die Anwendung wurde eingefroren. In diesem Fall haben Sie ein kleines Problem. Sie müssen die Anwendung wieder zum Leben erwecken und den Zustand der XAML Seite wieder auftauen, so wie er war, als der Benutzer die App verlassen hat. Wir werden uns hierzu entsprechenden Code anschauen.

Wo wir bei den Zuständen der Seiten sind: jetzt ist eine gute Gelegenheit, über zwei Ereignisse im Lebenszyklus einer Seite zu reden:

  • OnNavigatedTo – Dieser Event wird ausgelöst, wenn die Seite in den Vordergrund zur Anzeige gebracht wird. An dieser Stelle können wir als Entwickler an die Seite übergebene Query Strings auslesen um die Controls auf der Seite wieder entsprechend mit Leben zu füllen, falls wir aus dem Tombstoned Modus zurück kommen.
  • OnNavigatedFrom – Dieser Event wird gefeuert, wenn der Anwender die Seite verlässt, um zu einer anderen Seite in der Anwendung zu wechseln, oder wenn er die Anwendung komplett verlässt. In beiden Fällen ist dieser Event eine gute Gelegenheit, Zustandsinformationen zur aktuellen Seite zu speichern. Wir werden hierfür später Codebeispiele sehen.

Daten und State Dictionaries

Lassen Sie uns jetzt anschauen, welche Daten wir sichern und wieder auftauen wollen. Meiner Meinung nach lassen sich die Daten einer Anwendung in zwei Arten einteilen:

  • Einstellungen und permanente Daten – Diese Art von Daten macht die Anwendung aus und muss über die verschiedenen Starts der Anwendung gerettet werden. Diese Daten sollten wir sofort im Isolated Storage speichern, sei es als Name/Value Paare, als Dateien/Verzeichnisse oder über SQL CE falls die Daten relational sind.
  • Seiten und flüchtige Daten – Diese Daten sind für den Anwender im Prinzip verzichtbar aber praktisch, wenn sie nicht verloren gehen. Das kann reichen von nicht gespeicherten Benutzereingaben über irgend ein ViewModel bis hin zu temporären Daten, die zum Beispiel heruntergeladen wurden. Diese Daten müssen wir achtsam behandeln, damit der Benutzer das Gefühl einer lebendigen App erhält. Wir werden hierbei jedoch nicht ganz allein gelassen. Das Windows Phone Betriebssystem bietet jeder Anwendung und jeder Seite ein Dictionary, welches verwendet werden kann, um Key-Value-Paare zu serialisieren. Dieses State Dictionary wird vom PhoneApplicationService angeboten und sollte nur zur Speicherung von flüchtigen Zustandsdaten verwendet werden. Sie sollten diese Eigenschaft nicht für die Speicherung großer Datenmengen verwenden, da es eine Beschränkung auf 2 MB für jede Seite und 4 MB für die gesamte Anwendung gibt. Wie wir im Code sehen werden, ist es dennoch hilfreich, den Zustand im Lebenszyklus der Anwendung zu bewahren. Es sind genau diese Dictionaries, die vom Windows Phone Betriebssystem persistiert werden – auch nachdem die App eingefroren wurde. Wenn die Anwendung nach dem Tombstone wieder aufgetaut wird, wird das Zustands-Dictionary mit den gespeicherten Daten wieder gefüllt. Da diese Daten im Hauptspeicher gehalten werden, können wir den Zustand ohne aufwändige Dateioperationen wiederherstellen.

Anwendungsereignisse

Da wir jetzt die ganzen Details und Abläufe kennen, lassen Sie uns anschauen, wann wir was im Lebenszyklus der Anwendung tun. Der PhoneApplicationService feuert während des Lebenszyklus‘ der Anwendung 4 Events, die uns erlauben, den Zustand zu verwalten:

  • Launching – Dieser Event wird gefeuert, wenn eine neue Instanz der Anwendung erstellt wird – sei es durch Start aus der Anwendungsliste, durch Drücken auf eine Kachel oder durch eine Toast-Notification oder durch weitere Möglichkeiten. In diesem Fall beginnen wir mit einem frischen Zustand und nicht als Fortsetzung einer vorangegangenen Instanz der Anwendung.
  • Deactivated – Dieser Event wird gefeuert, wenn der Anwender von der Anwendung weg navigiert oder ein Chooser aufgerufen wird. Dies ist die beste Gelegenheit, den Zustand der Anwendung im Zustands-Dictionary für spätere Verwendung beim Aufwecken zu speichern. Wenn die Anwendung aus dem schlafenden Zustand aufgeweckt wurde, wäre dies nicht nötig. Es ist aber absolut notwendig, wenn die Anwendung vom Tombstone aufgetaut wird und der Deactivated Event ist unsere einzige Gelegenheit zur Speicherung des Anwendungszustandes.
  • Activated – Dieser Event wird gefeuert, wenn die Anwendung in den Vordergrund zurückkehrt, entweder aus dem schlafenden (dormant) oder aus dem eingefrorenen (tombstoned) Zustand. Ein einfaches Flag IsApplicationInstancePreserved der Event Args zeigt uns an, aus welchem Zustand die Anwendung aufgeweckt wurde. Wenn das Flag true ist, hat die Anwendung im Hauptspeicher geschlafen. In diesem Fall müssen wir nichts machen, da FAS die Arbeit für uns erledigt. Wenn das Flag false ist, kehren wir aus dem eingefrorenen Zustand zurück und verwenden das Zustands-Dictionary zur Wiederherstellung des Anwendungszustands.
  • Closing – Dieser Event wird gefeuert, wenn der Benutzer den Backstack traversiert und dabei an der ersten Seite der Anwendung vorbeikommt. Keine Zustandsinformationen werden aufbewahrt und ein erneuter Start der Anwendung erzeugt eine frische Instanz der Anwendung.

Alle Events haben gemeinsam, dass hier keine aufwändigen, ressourcenhungrigen Aufgaben durchgeführt werden sollten. Das Betriebssystem wartet nur eine begrenzte Zeit auf die Beendigung unserer Methoden (10 Sekunden um genau zu sein). Um die beste Benutzererfahrung zu gewährleisten, sollten die teuren Operationen zur Bewahrung des Zustands auf Hintergrundthreads während der Laufzeit der Anwendung ausgeführt werden.

War die ganze Information oben etwas zu viel? Lassen Sie uns einen Blick werfen auf ein Diagramm aus einem MSDN Artikel zum Ausführungsmodell in Windows Phone:

(Übernommen von MSDN)

Demoanwendung

Bis jetzt war das vielleicht etwas trocken. Lassen Sie uns ein wenig Code anschauen. Wir bauen eine kleine Windows Phone Anwendung, welche die Zustandsverwaltung über den Lebenszyklus der Anwendung demonstriert. Die ganze Anwendung steht unten zum Download zur Verfügung. Wenn Sie das Windows Phone SDK 7.1 haben, brauchen Sie nur F5 drücken!

So, lassen Sie uns beginnen. File > New und wir wählen gleich die einfachste mögliche Vorlage mit nur einer XAML Seite MainPage.xaml/cs. Diese öffnen wir und fügen einige Demo-Controls ein. Am Ende sollte etwa folgendes herauskommen (klicken zum Vergrößern):

Wie Sie im Screenshot sehen, haben wir Controls für die unterschiedlichen Arten von Daten:

  • App Setting – das ist ein Beispiel für eine Anwendungseinstellung, welche über verschiedene Anwendungsinstanzen hinweg gesichert werden soll. Ein klassisches Beispiel hierfür sind Einstellungen des Benutzers.
  • App Transient Data – Diese Controls simulieren Daten auf Anwendungsebene, die nur für eine Anwendungsinstanz relevant sind aber auf allen Seiten verwendet werden sollen. Ein Beispiel hierfür wären Daten eines Webservices, die die Anwendung bei jedem Start neu lädt.
  • Page Transient Data – Diese Controls stehen für Seiten-spezifische Daten, die wir nicht verlieren möchten, wenn der Benutzer die Anwendung verlässt und über den Backstack zurückkommt.

Wir werden jeden Typ von Daten separat behandeln. Fangen wir an mit unserer App.xaml.cs – dem Ort für die Behandlung globaler Anwendungsdaten/-logik. Wir fügen einige Eigenschaften und etwas Code im Konstruktor hinzu:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;

using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace Day_24___Execution_Model
{
   public partial class App : Application
   {
      public PhoneApplicationFrame RootFrame { get; private set; }

      public bool AppSetting { get; set; }
      public string AppData { get; set; }

      public static new App Current
      {
         get { return Application.Current as App; }
      }

      // Constructor.
      public App()
      {
         // All other initialization code ..
         AppData = string.Empty;

         if (IsolatedStorageSettings.ApplicationSettings.Contains("AppSetting"))
         {
            AppSetting = (bool)IsolatedStorageSettings.ApplicationSettings["AppSetting"];
         }
      }
   }
}

Als nächstes fügen wir etwas Code in den Event Handlern für den Lebenszyklus der Anwendung ein. Beachten Sie die Verwendung des Flags IsApplicationInstancePreserved im Activated Event Handler, um zu sehen, ob die Anwendung aus dem schlafenden (dormant) oder aus dem eingefrorenen (tombstoned) Modus zurückkehrt. Ich hatte keine Bedarf für speziellen Code in den Launching/Closing Event Handlers – wenn Ihre Anwendung derartigen Code benötigt, können Sie hier natürlich Code eintragen. Beachten Sie weiterhin die Verwendung des State Dictionaries des PhoneApplicationService.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;

using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace Day_24___Execution_Model
{
   public partial class App : Application
   {
      public PhoneApplicationFrame RootFrame { get; private set; }

      public bool AppSetting { get; set; }
      public string AppData { get; set; }

      public static new App Current
      {
         get { return Application.Current as App; }
      }

      // Constructor.
      public App()
      {
         // All other initialization code ..
         AppData = string.Empty;

         if (IsolatedStorageSettings.ApplicationSettings.Contains("AppSetting"))
         {
            AppSetting = (bool)IsolatedStorageSettings.ApplicationSettings["AppSetting"];
         }
      }

      private void Application_Launching(object sender, LaunchingEventArgs e)
      {
      }

      private void Application_Activated(object sender, ActivatedEventArgs e)
      {
         if (e.IsApplicationInstancePreserved)
         {
            // Returning from Dormancy.
            // Do nothing.
         }
         else
         {
            // Returning from Tombstone.
            if (PhoneApplicationService.Current.State.ContainsKey("AppData"))
            {
               AppData = PhoneApplicationService.Current.State["AppData"].ToString();
            }
         }
      }

      private void Application_Deactivated(object sender, DeactivatedEventArgs e)
      {
         PhoneApplicationService.Current.State["AppData"] = AppData;
      }

      private void Application_Closing(object sender, ClosingEventArgs e)
      {
      }

      // Other auto-generated code here ..
   }
}

Zurück zum code-behind unserer MainPage.xaml. Wir fügen einige Event Handler ein: einen, wenn die Seite geladen hat; einen, wenn der Anwender auf den Button drückt um einige Daten zu „holen“ und einen, wenn sich der Zustand der Checkbox der Anwendungseinstellungen ändert. Wie Sie sehen, merken wir uns die Beispieldaten auf Anwendungsebene und speichern die Anwendungs-/Benutzereinstellungen direkt im Isolated Storage:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;

using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace Day_24___Execution_Model
{
   public partial class MainPage : PhoneApplicationPage
   {
      // Constructor.
      public MainPage()
      {
         InitializeComponent();
      }

      private void chkAppSetting_Tap(object sender, GestureEventArgs e)
      {
         // Store App Setting changes immediately in Isolated Storage for persistence.
         if (IsolatedStorageSettings.ApplicationSettings.Contains("AppSetting"))
         {
            IsolatedStorageSettings.ApplicationSettings.Remove("AppSetting");
         }

         if ((bool)this.chkAppSetting.IsChecked)
         {
            IsolatedStorageSettings.ApplicationSettings.Add("AppSetting", true);
         }
         else
         {
            IsolatedStorageSettings.ApplicationSettings.Add("AppSetting", false);
         }
      }

      private void Button_Click(object sender, RoutedEventArgs e)
      {
         this.txtAppData.Text = "This is some sample data ..";

         // As if we are caching the data.
         App.Current.AppData = this.txtAppData.Text;
      }

      private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
      {
         this.chkAppSetting.IsChecked = App.Current.AppSetting;
      }
   }
}

Jetzt fehlen noch die wichtigen Events für die Seitennavigation. In diesen regeln wir, wie wir die Controls mit flüchtigen Daten beleben und uns die flüchtigen Daten merken, wenn die Seite in den Vordergrund wechselt bzw. aus dem Vordergrund verschwindet:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace Day_24___Execution_Model
{
   public partial class MainPage : PhoneApplicationPage
   {
      // Constructor.
      public MainPage()
      {
         InitializeComponent();
      }

      private void chkAppSetting_Tap(object sender, GestureEventArgs e)
      {
         // Store App Setting changes immediately in Isolated Storage for persistence.
         if (IsolatedStorageSettings.ApplicationSettings.Contains("AppSetting"))
         {
            IsolatedStorageSettings.ApplicationSettings.Remove("AppSetting");
         }

         if ((bool)this.chkAppSetting.IsChecked)
         {
            IsolatedStorageSettings.ApplicationSettings.Add("AppSetting", true);
         }
         else
         {
            IsolatedStorageSettings.ApplicationSettings.Add("AppSetting", false);
         }
      }

      private void Button_Click(object sender, RoutedEventArgs e)
      {
         // Fake data fetch..
         this.txtAppData.Text = "This is some sample data ..";

         // As if we are caching the data.
         App.Current.AppData = this.txtAppData.Text;
      }

      private void PhoneApplicationPage_Loaded(object sender, RoutedEventArgs e)
      {
         // Load user setting from Isolated Storage.
         this.chkAppSetting.IsChecked = App.Current.AppSetting;
      }

      protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
      {
         base.OnNavigatedTo(e);

         // Load in App Data, if available.
         if (App.Current.AppData != null && App.Current.AppData != string.Empty)
         {
            this.txtAppData.Text = App.Current.AppData;
         }

         // Load any Page State, if available.
         if (PhoneApplicationService.Current.State.ContainsKey("PageData"))
         {
            this.txtPageData.Text = PhoneApplicationService.Current.State["PageData"].ToString();
         }
         if (PhoneApplicationService.Current.State.ContainsKey("PageSetting"))
         {
            this.chkPageSetting.IsChecked = (bool)PhoneApplicationService.Current.State["PageSetting"];
         }
      }

      protected override void OnNavigatedFrom(System.Windows.Navigation.NavigationEventArgs e)
      {
         base.OnNavigatedFrom(e);

         // Clear past state, if any.
         if (PhoneApplicationService.Current.State.ContainsKey("PageData"))
         {
            PhoneApplicationService.Current.State.Remove("PageData");
         }

         if (PhoneApplicationService.Current.State.ContainsKey("PageSetting"))
         {
            PhoneApplicationService.Current.State.Remove("PageSetting");
         }

         // Save off Page state.
         PhoneApplicationService.Current.State["PageData"] = this.txtPageData.Text;
         PhoneApplicationService.Current.State["PageSetting"] = this.chkPageSetting.IsChecked;
      }
   }
}

Das war’s! Wir haben jetzt genug Code hinzugefügt um die verschiedenen Arten von Daten und Anwendungszustand über den Lebenszyklus der Anwendung jeweils auf angemessene Weise zu verwalten. Am besten probieren Sie die Anwendung mal aus. Starten Sie die Anwendung, machen Sie eine Änderung der Einstellung, gehen Sie woanders hin und kommen zur Anwendung zurück oder beenden Sie die Anwendung und starten eine frische Instanz. Die Daten und Zustandsinformationen sollten wie erwartet gespeichert und gegebenenfalls zurückgesetzt werden. Eine typische Ausführung mit der aktivierten Einstellung sollte beispielsweise so aussehen:

Wenn Sie so fröhlich die App in den Vordergrund bringen und wieder verlassen, werden Sie sich vielleicht fragen, wie Sie wissen können, ob die App in den eingefrorenen Zustand (tombstoned) übergegangen ist. Wenn die App deaktiviert wird, geht sie in den meisten Fällen in den schlafenden (dormant) Zustand über und kommt direkt wieder zum Leben wenn sie wieder aktiviert wird. Auch wenn wir Code zur Wiederbelebung der Controls haben, falls die Anwendung aus dem Tombstoned Zustand kam, gibt es keine zuverlässige Möglichkeit, diesen Fall zu testen.

Sie müssen sich aber nicht den Kopf zerbrechen! Es gibt eine kleine Einstellung des Debuggers, die Ihre Anwendung immer sofort einfriert (also in den tombstoned Modus versetzt) sobald sie deaktiviert wird. Also auch wenn die App theoretisch nur schlafen gelegt werden könnte, wird sie durch diese Einstellung immer eingefroren. Das ist für unsere Tests natürlich sehr hilfreich.

Zusammenfassung

Für das Verständnis des Ablaufmodells hilft es ungemein, in jeden der entsprechenden Event Handlers einen Breakpoint zu setzen und dann zu schauen, wann und unter was für Umständen die verschiedenen Events feuern. Wenn Sie das alles verstanden haben, steht Ihnen nichts mehr im Weg, eine Anwendung zu entwickeln, die den Benutzern stets lebendig erscheinen wird.

Ergebnis: zufriedene Anwender und letztendlich mehr Zufriedenheit für Sie! Adios!

Um ein komplettes Windows Phone Projekt mit den Konzepten dieses Artikels herunterzuladen, drücken Sie auf den Download Code Button.

Morgen wird Chris Koenig über ein nützliches Werkzeug im Visual Studio 2010 berichten: das Silverlight Performance Analysis Tool.

Bis dahin!

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.