Windows Phone Silverlight: Безопасная работа с задачами

    • Silverlight
    • Windows Phone 7
    • Windows Phone
    • Tasks
  • modified:
  • reading: 3 minutes

При работе с задачами из пространства имен Microsoft.Phone.Tasks существуют две стандартные ошибки: разработчики не думают о том, что на время, когда запущена задача, ваше приложение может перейти в tombstone mode. Проще говоря – это когда ваше приложение убьют, и сохранят только State страницы и State приложения, которые вам нужно будет потом восстановить. Вторая стандартная ошибка скрывается в том, что при вызове метода Show у задач, если данная страница уже не активна, то есть, например, происходит навигация, то метод бросит исключение InvalidOperationException, который убьёт ваше приложение (если вы не перехватываете исключение), и при возврате из задачи – вы попадете на главный экран самой операционной системы.

Итак, давайте напишем наипростейшее приложение из разряда, как выбрать картинку из библиотеки и отобразить ее на контроле. User Interface у нас будет такой:

А код такой:

Итак, сколько проблем вы здесь видите?

Проблема 1. Tombstone mode.

Если внимательно пройтись по документации в MSDN, например, прочитать внимательно информацию по классу PhotoChooserTask, то можно заметить там Important Note, который как раз и говорит о том, о чем я упоминал. Полагаться на то, что ваше приложение никогда не перейдет в Tombstone mode, не правильно. А тестировать это очень легко, нужно в настройках проекта перейти на вкладку Debug и включить галочку:

Запустите приложение и вы увидите проблему: после того, как вы выберите картинку, ничего не произойдет. Так происходит потому, что все ваше приложение было уничтожено при переходе на задачу, и восстановлено заново после завершения задачи. Единственное, что сохранится – это State самого приложения и страниц. MSDN предлагает решить эту проблему так: нужно пересоздавать саму задачу в конструкторе страницы, тогда при восстановлении страницы инфраструктура Windows Phone Silverlight сама вызовет верно обработчик Completed в тот момент, когда вы снова на него подпишитесь.

Конечно, чаще всего нам нужно так же ассоциировать что-нибудь с этой выбранной картинкой, скорее всего – мы не просто хотим ее показать, а так же привязать что-то к ней, или сделать что-то с ней на основе каких-то данных, к которым мы пришли перед запуском задачи. В этом случае нужно обратиться за знаниями к информации о том, как работать со State страницы: How to: Preserve and Restore Page State for Windows Phone. В общем-то, решение будет очень простым: а) в методе OnNavigatedFrom сохраняем то, на что мы будем ориентироваться в State страницы б) на OnNavigatedTo читаем из State то, что нужно, и подписываемся на событие Completed. Подписываться на Completed нужно будет теперь именно на OnNavigatedTo, так как, если вы подпишитесь в конструкторе класса страницы, то попросту вы еще не успеете прочитать из State то, что вам нужно до того момента, как будет вызван обработчик события Completed, а к State можно обращаться только после вызова метода OnNavigatedTo. Сделать все это можно так:

Проблема 2. Метод Show задач и исключения.

Методы Show задач могут бросать исключения. Я с этим столкнулся. Воспроизвести очень просто – сделайте double click на кнопке из предыдущего примера. После того, как вы выберите картинку – вы не увидите ваше приложение, вместо него вы увидите главный экран Windows Phone. Проблема очень простая: на первый click вы уже запустили задачу, на второй click метод Show выбросит исключение "System.InvalidOperationException: Not allowed to call Show() multiple times before a Chooser returns". Нужно отлавливать исключения при вызове метода Show. Чтобы не плодить этот код, и не забывать ловить исключения, я решил эту проблему раз и навсегда для себя, написал несколько Wrappers для задач, код, которых вы можете посмотреть в моем репозитории.

При использовании моих wrappers самая простая логика с ChooserTask будет выглядеть так:

А вызов задач, которые не наследуются от базового класса ChooserTask, будет выглядеть так:

Поменьше вам багов! Пример с моими Wrappers можно как всегда взять с моего репозитория.

See Also