День #7. Некромантия. Или tombstoning в WP7

Posted on Ноябрь 20, 2010

4


Добрый вечер!

Это 7 статья из цикла Windows Phone 7 для начинающих. Вчера мы говорили про данные и правильную работу с ними. Сегодня мы с Вами поговорим про оживление мертвецов. Звучит слегка необычно, правда?

Итак, немного теории для начала.

Multitasking и Windows Phone 7

В современной жизни мы довольно часто встречаемся с задачей многозадачности. На компьютерах это сплошь и рядом. Ведь удобно же писать статью, иногда при этом переключаясь к книге или к видео, при этом прослушивая любимую музыку. Или играть в игру, свернув ее прочитать почту и ответить кому-то в чате и вновь вернуться к игре. Это стало настолько обыденным, что мы не представляем как же обойтись без всего этого. С прогрессом, телефоны стали обладать всеми этими возможностями  и мы всегда хотим что бы телефон умел это все делать.

А для разработчиков — это сплошная головная боль. Асинхронные вызовы, операции в разных потоках, синхронизация. Бррр.

Windows Phone 7 подходит к решению вопроса с многозадачностью со своей, особой, стороны. Почему же особой? Да потому что, многозадачность в устройстве все же есть! Просто не в нашем, классическом понимании. Что бы понять, необходимо понять что же значит «Некромантия». Для меня это понятие связано с зомбированием. В Windows Phone 7 многозадачность организованна с помощью стека. Это как старый браузер без табов и кнопки вперед. Но с кнопкой назад и кнопкой старт, которая позволяет переходить к главному экрану и запускать программы.

Рассмотрим два сценария.

Сценарий первый.

Обычная история. Вы запускаете калькулятор, складываете 2394334 + 32847294, записываете результат и жмете кнопку назад, выходя при этом из программы и переходя на главный экран.

Сценарий второй.

Итак, представьте, что вы просматриваете в браузере отчет и тут обнаруживаете что вам необходимо пересчитать два числа, вы жмете кнопку Старт, переходите к главному экрану и заходите в калькулятор, подсчитав, вы решаете перепроверить числа и нажимаете на кнопку Старт, что бы зайти в Excel. Проверив числа вы отвлекаетесь и садитесь немного поиграть в игру на телефоне. Немного поиграв, вы вспоминаете об отчете в браузере и начинаете нажимать кнопку Назад.

При этом вы возвращаетесь в Excel, где отображаются те же данные, потом в калькулятор, в котором отображается результат вашей проверки и в браузер. Итак стек почти пуст. Если вы еще раз нажмете назад, то стек останется пустым.

Теперь понятно, что концепт стека очень простой: кнопка старт толкает приложение в стек, при этом пытаясь уменьшить деятельность приложения. Т.е., по сути, убивает его. А кнопка Назад — зомбирует приложение. Вероятно вы смотрели много фильмов, в которые были ожившие мертвецы. Не все они были симпатичны! У многих отваливались руки или ноги. Вот и у приложений, которые мы зомбируем бывают те же проблемы.

Наша главная задача сделать так, что бы ожившее приложение выглядело ТОЧНО ТАК ЖЕ как и убитое. Так что пользователь не должен даже понять, что имеет дело, с абсолютно новой сущностью приложения. Не для всех приложений оживление проходит хорошо. Иногда это допустимо, например если мы пролистали браузере вниз,а потом вернулись, мы окажемся вверху страницы, а иногда это ОЧЕНЬ не желательно, тот же пример, но если мы 15 минут заполняли форму, уточнили телефон и вернулись к пустой форме…

У приложения существует 4 состояния. Вот они:

  1. Запущено(launched). При запуске с главного экрана.
  2. Закрыто(closed). При выходе с помощью кнопки Назад.
  3. Неактивно(deactivated). При нажатии во время работы с приложением на кнопку Старт.
  4. Активировано(activated). При зомбировании приложения.
Создадим небольшое приложение на основе вчерашнего приложения, которое будет менять цвет при прикосновении, а так же подсчитывать кол-во нажатий на экран. Вот текст файла MainPage.xaml.cs:
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 Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace day_7_tombstoning
{
	public partial class MainPage : PhoneApplicationPage
	{
		Random rand = new Random();
		int numTaps = 0;

		public MainPage()
		{
			InitializeComponent();
			UpdatePageTitle(numTaps);
		}
		protected override void OnManipulationStarted(ManipulationStartedEventArgs args)
		{
			ContentPanel.Background =
			new SolidColorBrush(Color.FromArgb(255, (byte)rand.Next(256),
			(byte)rand.Next(256),
			(byte)rand.Next(256)));
			UpdatePageTitle(++numTaps);
			args.Complete();
			base.OnManipulationStarted(args);

		}
		void UpdatePageTitle(int numTaps)
		{
			PageTitle.Text = String.Format("{0} taps total", numTaps);
		}
	}
}

Приложение очень простое. Построим и запустим его на эмуляторе, расположив при этом окно Output в студии так, что бы мы видели его за эмулятором. Потыкав по приложению жмем кнопку Старт. В Visual Studio можно увидеть два потока:  program end и the program has terminated, но на телефоне программа стала Неактивной(умерла, но ждет оживления).

Теперь нажмите кнопку назад и увидите “Resuming…”. Программа продолжила свое выполнение, хотя и не сохранила результат вашего долго труда. Что бы сохранить данные программы хорошо подходят методы OnNavigatedTo и OnNavigatedFrom.
Вот немного измененный код программы.

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 Microsoft.Phone.Controls;
using Microsoft.Phone.Shell;

namespace day_7_tombstoning
{
	public partial class MainPage : PhoneApplicationPage
	{
		Random rand = new Random();
		int numTaps = 0;
		PhoneApplicationService appService = PhoneApplicationService.Current;
		public MainPage()
		{
			InitializeComponent();
			UpdatePageTitle(numTaps);
		}
		protected override void OnManipulationStarted(ManipulationStartedEventArgs args)
		{
			ContentPanel.Background =
			new SolidColorBrush(Color.FromArgb(255, (byte)rand.Next(256),
			(byte)rand.Next(256),
			(byte)rand.Next(256)));
			UpdatePageTitle(++numTaps);
			args.Complete();
			base.OnManipulationStarted(args);

		}
		void UpdatePageTitle(int numTaps)
		{
			PageTitle.Text = String.Format("{0} taps total", numTaps);
		}
		protected override void OnNavigatedFrom(NavigationEventArgs args)
		{
			appService.State["numTaps"] = numTaps;
			if (ContentPanel.Background is SolidColorBrush) { appService.State["backgroundColor"] = (ContentPanel.Background as SolidColorBrush).Color; }
			base.OnNavigatedFrom(args);
		}
		protected override void OnNavigatedTo(NavigationEventArgs args)
		{
			// Load numTaps
			if (appService.State.ContainsKey("numTaps"))
			{
				numTaps = (int)appService.State["numTaps"];
				UpdatePageTitle(numTaps);
			}
			// Load background color
			object obj;
			if (appService.State.TryGetValue("backgroundColor", out obj)) ContentPanel.Background = new SolidColorBrush((Color)obj);
			base.OnNavigatedTo(args);
		}
	}
}

В поле appService установлено значение PhoneApplicationService.Current. Это для удобства.

Сохранение данных в словарь состояний(State типа dictionary) происходит намного легче чем складывание их туда. Код:

appService.State[«numTaps»] = numTaps;

Если такая запись существует, она заменяется. Если нет, то создается новая. С цветом фона немного сложнее, потому что он еще бывает null. Потому и проверяем на null перед сохранением.

Для получения данных используется схожий синтаксис. Но если таких состояний нету( А их нет при запуске приложение) вы получите исключение. Метод OnNavigatedTo показывает сразу два метода для извлечения данных: проверяем наличие ключа и TryGetValue, который вернет true если ключ есть.

Итак, запустив приложение и попробовав повыходить из него, вы увидите что все ваши ценные данные восстанавливаются.

Код сегодняшнего дня можно скачать тут.

День #8. Изолированное хранилище.

Реклама