Статьи

Windows Phone 8: кратко: плитки, уведомления и многозадачность

В этом уроке мы сосредоточимся на живых приложениях. Живые приложения являются одной из основных концепций при разработке Windows Phone, и для правильного создания качественного интерфейса необходимо учитывать множество факторов, таких как уведомления, агенты и плитки.

Как мы видели в жизненном цикле приложений, обсужденном ранее в этой серии, приложения приостанавливаются, когда они не находятся на переднем плане. Каждый запущенный процесс завершается, поэтому приложение не может выполнять операции в фоновом режиме.

Есть три способа преодолеть это ограничение:

  • Push-уведомления , которые отправляются удаленной службой по каналу HTTP. Этот подход используется для отправки уведомлений пользователям, обновления плитки или предупреждения пользователей о том, что что-то произошло.
  • Фоновые агенты — это сервисы, подключенные к нашему приложению, которые могут время от времени запускаться в определенных условиях Эти сервисы также можно использовать для сценариев push-уведомлений — в этом случае удаленные сервисы не задействованы — но они также могут выполнять другие задачи, если они используют поддерживаемые API.
  • Сигналы тревоги и напоминания , которые отображают напоминания пользователю в определенные даты и время.

Давайте посмотрим подробно, как они работают.

Push-уведомления — это сообщения, отправляемые на телефон, которые могут реагировать различными способами в зависимости от типа уведомления. Существует три типа push-уведомлений:

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

В архитектуру push-уведомлений вовлечены три фактора:

  • Приложение Windows Phone, которое выступает в роли клиента для получения уведомлений.
  • Серверное приложение, которое может быть веб-приложением или службой, которая отправляет уведомления. Обычно сервер хранит список всех устройств, которые зарегистрированы для получения уведомлений.
  • Служба push-уведомлений Microsoft (MPNS) — облачная служба, предлагаемая Microsoft, которая может получать уведомления от серверного приложения и направлять их клиентам Windows Phone.
Архитектура push-уведомлений Microsoft

Каждое приложение Windows Phone получает push-уведомления, используя канал , который идентифицируется уникальным URI. Серверное приложение отправит уведомления зарегистрированным клиентам, отправив строку XML на этот URI с помощью команды POST. MPNS позаботится о маршрутизации запросов на соответствующие устройства.

Вот пример URI, который представляет канал:

1
http://sn1.notify.live.net/throttledthirdparty/01.00/AAhsLicyiJgtTaidbJoSgm-

Примечание: использование MPNS бесплатное, но ограничено 500 уведомлениями в день на одно устройство. Если вам необходимо превысить это ограничение, вам необходимо приобрести цифровой сертификат TLS, который вам необходимо будет предоставить в процессе сертификации, а также поставить цифровую подпись на приложении вашего сервера. Таким образом, вы также сможете поддерживать SSL для шифрования канала уведомления.

Как уже упоминалось, уведомления отправляются по каналу HTTP с командой POST. Преимущество заключается в том, что он опирается на стандартную технологию, поэтому вы сможете создавать серверные приложения на любой платформе разработки.

HTTP-запрос, представляющий уведомление, имеет следующие функции:

  • Он определяется с использованием XML, поэтому тип содержимого запроса должен быть text/xml .
  • Пользовательский заголовок с именем X-WindowsPhone-Target , который содержит тип уведомления (toast, Tile или raw).
  • Пользовательский заголовок с именем X-NotificationClass , который является приоритетом уведомления (мы обсудим это более подробно позже).

Давайте посмотрим, как различные push-уведомления структурированы в деталях.

В следующем примере показан XML, необходимый для отправки всплывающего уведомления:

1
2
3
4
5
6
7
8
<?xml version=»1.0″ encoding=»utf-8″?>
<wp:Notification xmlns:wp=»WPNotification»>
  <wp:Toast>
    <wp:Text1>Title</wp:Text1>
    <wp:Text2>Text</wp:Text2>
    <wp:Param>/MainPage.xaml?ID=1</wp:Param>
  </wp:Toast>
</wp:Notification>

Есть три параметра для установки:

  • wp:Text1wp:Text1 уведомления.
  • wp:Text2 — текст уведомления.
  • wp:Param — дополнительная ссылка для уведомления; если этот параметр установлен, приложение автоматически открывается на указанной странице с одним или несколькими параметрами строки запроса, которые можно использовать для определения контекста уведомления.

Когда вы готовите запрос для отправки по HTTP, заголовок X-WindowsPhone-Target должен быть установлен в toast , а заголовок X-NotificationClass поддерживает следующие значения:

  • 2 чтобы отправить уведомление немедленно.
  • 12 отправить уведомление через 450 секунд.
  • 22 отправить уведомление через 900 секунд.
Уведомление о тостах

Уведомления плитки используются для обновления основной плитки или одной из дополнительных плиток приложения. Мы не будем здесь описывать XML, необходимый для отправки уведомления: плитки более сложны, чем другие типы уведомлений, поскольку Windows Phone 8 поддерживает множество шаблонов и размеров. Мы рассмотрим XML, который описывает уведомления Tile, позже в разделе Tiles этой статьи.

Чтобы отправить уведомление Tile, заголовок X-WindowsPhone-Target HTTP-запроса должен быть установлен на tile , тогда как заголовок X-NotificationClass поддерживает следующие значения:

  • 1 чтобы отправить уведомление немедленно.
  • 11 отправить уведомление через 450 секунд.
  • 21 отправить уведомление через 900 секунд.

Необработанные уведомления не имеют конкретного определения XML, поскольку они могут доставлять любые данные, поэтому мы можем включить наше собственное определение.

Для отправки необработанного уведомления заголовок X-WindowsPhone-Target HTTP-запроса должен быть установлен в raw , тогда как заголовок X-NotificationClass поддерживает следующие значения:

  • 3 чтобы отправить уведомление немедленно.
  • 13 отправить уведомление через 450 секунд.
  • 23 отправить уведомление через 900 секунд.

В следующем примере кода показан пример отправки HttpWebRequest уведомления с использованием класса HttpWebRequest , одного из базовых классов .NET Framework для выполнения сетевых операций:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
string toastNotificationPayloadXml = «<?xml version=\»1.0\» encoding=\»utf-8\»?>» +
                                        «<wp:Notification xmlns:wp=\»WPNotification\»>» +
                                        «<wp:Toast>» +
                                        «<wp:Text1> title </wp:Text1>» +
                                        «<wp:Text2> text </wp:Text2>» +
                                        «</wp:Toast> » +
                                        «</wp:Notification>»;
 
byte[] payload = Encoding.UTF8.GetBytes(toastNotificationPayloadXml);
 
var pushNotificationWebRequest = (HttpWebRequest)WebRequest.Create(«http://sn1.notify.live.net/throttledthirdparty/01.00/AAEqbi-clyknR6iysF1QNBFpAgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMkUxREQ»);
 
pushNotificationWebRequest.Method = «POST»;
pushNotificationWebRequest.ContentType = «text/xml»;
 
var messageId = Guid.NewGuid();
 
pushNotificationWebRequest.Headers.Add(«X-MessageID», messageId.ToString());
pushNotificationWebRequest.Headers.Add(«X-WindowsPhone-Target», «toast»);
pushNotificationWebRequest.Headers.Add(«X-NotificationClass», «2»);
 
pushNotificationWebRequest.ContentLength = payload.Length;
 
using (var notificationRequestStream = pushNotificationWebRequest.GetRequestStream())
{
    notificationRequestStream.Write(payload, 0, payload.Length);
}
 
using (var pushNotificationWebResponse = (HttpWebResponse)pushNotificationWebRequest.GetResponse())
{
    //Check the status of the response.
}

Определение XML просто хранится в string . Мы собираемся изменить только значения узлов, в которых хранятся заголовок и текст уведомления. Затем мы начинаем готовить HTTP-запрос с помощью класса HttpWebRequest . Мы добавляем пользовательские заголовки, определяем длину и тип содержимого ( text/xml ) и указываем используемый метод ( POST ).

В конце, используя метод GetRequestStream() , мы получаем местоположение потока для записи содержимого запроса, которое является XML-уведомлением. Затем мы отправляем его, вызывая метод GetResponse() , который возвращает статус запроса. Анализируя ответ, мы можем определить, была ли операция успешной.

Анализ ответа включает в себя код состояния и три пользовательских заголовка:

  • Код состояния ответа возвращает общую информацию, которая сообщает вам, был ли получен запрос. Он основан на стандартных кодах статуса HTTP. Например, 200 OK означает, что запрос был успешно принят, а 404 Not Found означает, что URI был недействительным.
  • Заголовок X-NotificationStatus сообщает вам, получил ли MPNS запрос с использованием значений Received , Dropped , QueueFull и Supressed .
  • Заголовок X-DeviceConnectionStatus возвращает состояние устройства при отправке запроса: Connected , Inactive , Disconnected или TempDisconnected .
  • Заголовок X-SubscriptionStatus возвращается, если канал все еще действителен ( Active ) или нет ( Expired ) Во втором случае мы не должны пытаться отправить его снова, так как он больше не существует.

Сочетание этих параметров поможет вам понять реальное состояние операции. Документация MSDN содержит описания всех возможных комбинаций .

Важно правильно управлять уведомлениями, потому что MPNS не предлагает никакого механизма автоматической повторной попытки. Если уведомление не доставлено, MPSN не будет пытаться отправить его снова, даже если операция не удалась по временной причине (например, устройство не подключено к Интернету). Это зависит от вас, чтобы реализовать механизм повторных попыток на основе ответа.

Как видите, отправка push-уведомлений немного сложна, так как требует ручного задания заголовков, XML-строк и т. Д. Некоторые разработчики работали над оболочками, которые скрывают сложность ручного определения уведомлений, предоставляя высокоуровневые API, чтобы Вы можете работать с классами и объектами.

Одна из самых интересных оболочек называется PushSharp , которую можно просто установить на ваш серверный проект с помощью NuGet . Самые большие преимущества этой библиотеки:

  • Это универсальная библиотека .NET, которая поддерживает не только Windows Phone, но и наиболее распространенные платформы, использующие push-уведомления, такие как приложения Магазина Windows, iOS, Android и Blackberry. Если у вас есть кроссплатформенное приложение, оно облегчит вашу жизнь в управлении приложением с одним сервером, которое может отправлять уведомления на различные виды устройств.
  • Он полностью совместим с Windows Phone 8, поэтому поддерживает не только обычные и необработанные уведомления, но и все новые шаблоны и размеры листов.

В следующем примере показано, как просто отправить всплывающее уведомление с помощью этой библиотеки:

1
2
3
4
5
6
7
8
WindowsPhoneToastNotification notification = new WindowsPhoneToastNotification();
notification.Text1 = «Title»;
notification.Text2 = «Text»;
notification.EndPointUrl = «http://sn1.notify.live.net/throttledthirdparty/01.00/AQHcej5duTcJRqnn779soTA1AgAAAAADAQAAAAQUZm52OkJCMjg1QTg1QkZDMkUxREQFBkxFR0FDWQ»;
notification.NotificationClass=BatchingInterval.Immediate;
PushBroker broker = new PushBroker();
broker.RegisterWindowsPhoneService();
broker.QueueNotification(notification);

Каждый тип уведомления представлен определенным классом, который предоставляет свойство для каждой функции уведомления. В предыдущем примере класс WindowsPhoneToastNotification предлагает свойства для установки заголовка уведомления, текста и глубокой ссылки.

Расположение URI канала для отправки уведомления задается в свойстве EndPointUrl . Когда все настроено, вы можете отправить его, создав объект PushBroker , который представляет диспетчер, который заботится об отправке уведомлений. Во-первых, вы должны зарегистрироваться для того типа уведомлений, которые вы хотите отправить. Поскольку мы работаем с Windows Phone, мы используем метод RegisterWindowsPhoneService() . Затем мы можем QueueNotification() уведомление в очередь, просто передав его QueueNotification() . Он будет автоматически отправлен с установленным вами приоритетом.

Подход тот же, если вы хотите отправить плитку. У вас есть три различных класса на основе шаблона плитки: WindowsPhoneCycleTileNotification , WindowsPhoneFlipTileNotification и WindowsPhoneIconicTileNotification ; или WindowsPhoneRawNotification для необработанного уведомления.

В конце класс PushBroker предоставляет множество событий для управления жизненным циклом уведомлений, например OnNotificationSent который запускается при успешной отправке уведомления, или OnNotificationFailed который запускается при OnNotificationFailed операции отправки.

Базовый класс, который идентифицирует канал push-уведомлений, называется HttpNotificationChannel и предоставляет множество методов и событий, которые запускаются, когда происходит что-то, связанное с каналом.

Примечание. Для получения push-уведомлений необходимо включить функцию ID_CAP_PUSH_NOTIFICATION в файле манифеста.

Каждое приложение имеет один уникальный канал, идентифицируемый по ключевому слову. По этой причине его следует создавать только при первом подписке приложения на получение уведомлений; если вы попытаетесь создать канал, который уже существует, вы получите исключение. Чтобы избежать этого сценария, класс HttpNotificationChannel предлагает метод Find() , который возвращает ссылку на канал.

1
2
3
4
5
6
7
8
9
private void OnRegisterChannelClicked(object sender, RoutedEventArgs e)
{
    HttpNotificationChannel channel = HttpNotificationChannel.Find(«TestChannel»);
    if (channel == null)
    {
        channel = new HttpNotificationChannel(«TestChannel»);
        channel.Open();
    }
}

В предыдущем примере канал создается только в случае сбоя метода Find() и возврата нулевого объекта. Класс HttpNotificationChannel предоставляет множество методов для начала взаимодействия с push-уведомлениями; их следует вызывать, только если канал еще не существует. В примере мы видим метод Open() который должен вызываться для эффективного создания канала и который автоматически подписывается на необработанные уведомления.

Если мы хотим получать уведомления toast и Tile, нам нужно использовать два других метода, предлагаемых классом: BindToShellToast() и BindToShellTile() . В следующем примере показана полная инициализация:

01
02
03
04
05
06
07
08
09
10
11
private void OnRegisterChannelClicked(object sender, RoutedEventArgs e)
{
    HttpNotificationChannel channel = HttpNotificationChannel.Find(«TestChannel»);
    if (channel == null)
    {
        channel = new HttpNotificationChannel(«TestChannel»);
        channel.Open();
        channel.BindToShellToast();
        channel.BindToShellTile();
    }
}

Помимо предоставления методов, класс HttpNotificationChannel также предлагает некоторые события для управления различными условиями, которые могут запускаться в течение жизненного цикла канала.

Наиболее важный из них называется ChannelUriUpdated , который запускается, когда операция создания канала завершена и MPNS вернул URI, который его идентифицирует. Это событие, при котором в обычном приложении мы отправляем URI приложению сервера, чтобы оно могло сохранить его для дальнейшего использования. Важно подписаться на это событие независимо от того, был ли канал только что создан или уже существует и был получен с помощью метода Find() . Время от времени срок действия URI, который идентифицирует канал, может истечь. В этом случае событие ChannelUriUpdated запускается снова, чтобы вернуть новый URI.

В следующем примере показана полная инициализация клиента:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private void OnRegisterChannelClicked(object sender, RoutedEventArgs e)
{
    HttpNotificationChannel channel = HttpNotificationChannel.Find(«TestChannel»);
    if (channel == null)
    {
        channel = new HttpNotificationChannel(«TestChannel»);
        channel.Open();
        channel.BindToShellToast();
        channel.BindToShellTile();
    }
 
    channel.ChannelUriUpdated += channel_ChannelUriUpdated;
}
 
void channel_ChannelUriUpdated(object sender, NotificationChannelUriEventArgs e)
{
    MessageBox.Show(e.ChannelUri);
}

Как видите, событие ChannelUriUpdated возвращает параметр со свойством ChannelUri , который содержит необходимую нам информацию. В предыдущем примере мы просто отображали канал URI для пользователя.

Есть два других события, предлагаемых классом HttpNotificationChannel которые могут быть полезны:

  • HttpNotificationReceived запускается, когда приложение получает необработанное уведомление.
  • ShellToastNotificationReceived запускается, когда приложение получает ShellToastNotificationReceived уведомление, пока оно открыто. По умолчанию уведомления о тостах не отображаются, если соответствующее приложение находится на переднем плане.

Событие HttpNotificationReceived получает в параметрах объект, который идентифицирует уведомление. Содержимое хранится в свойстве Body , которое является потоком, поскольку необработанные уведомления могут хранить данные любого типа. В следующем примере мы предполагаем, что необработанное уведомление содержит текст, и отображаем его при получении:

1
2
3
4
5
6
7
8
void Channel_HttpNotificationReceived(object sender, HttpNotificationEventArgs e)
{
    using (StreamReader reader = new StreamReader(e.Notification.Body))
    {
        string message = reader.ReadToEnd();
        Dispatcher.BeginInvoke(() => MessageBox.Show(message));
    }
}

ShellNotificationReceived событие ShellNotificationReceived возвращает в параметрах объект Collection , который содержит все узлы XML, являющиеся частью уведомления. В следующем примере показано, как извлечь заголовок и описание уведомления и как отобразить их пользователю:

1
2
3
4
5
6
void Channel_ShellToastNotificationReceived(object sender, NotificationEventArgs e)
{
    string title = e.Collection[«wp:Text1»];
    string message = e.Collection[«wp:Text2»];
    Dispatcher.BeginInvoke(() => MessageBox.Show(title + » » + message));
}

Если что-то пойдет не так, когда вы открываете канал уведомлений, вы можете подписаться на событие HttpNotificationChannel класса HttpNotificationChannel чтобы узнать, что произошло.

Событие возвращает параметр, который содержит информацию об ошибке, например ErrorType , ErrorCode , ErrorAdditionalData и Message .

В следующем списке приведены наиболее распространенные условия, которые могут привести к сбою во время открытия канала:

  • Чтобы сохранить срок службы аккумулятора и производительность, Windows Phone ограничивает максимальное количество одновременно работающих каналов. Если предел достигнут и вы пытаетесь открыть новый канал, вы получите значение ErrorType как ErrorType .
  • Полученное уведомление может содержать сообщение, которое плохо отформатировано; в этом случае ErrorType будет MessageBadContent .
  • Вы можете отправить слишком много уведомлений одновременно; в этом случае они отклоняются с ошибкой NotificationRateTooHigh .
  • Чтобы сохранить заряд батареи, уведомления могут быть получены, только если батарея не критична; в этом случае вы получите ошибку PowerLevelChanged .

Свойство ErrorAdditionalData может содержать дополнительную информацию об ошибке. Например, если вы получили ошибку PowerLevelChanged , вы будете проинформированы о текущем уровне заряда батареи (низкий, критический или нормальный).

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
void channel_ErrorOccurred(object sender, NotificationChannelErrorEventArgs e)
{
    if (e.ErrorType == ChannelErrorType.PowerLevelChanged)
    {
        ChannelPowerLevel level = (ChannelPowerLevel) e.ErrorAdditionalData;
        switch (level)
        {
            case ChannelPowerLevel.LowPowerLevel:
                MessageBox.Show(«Battery is low»);
                break;
            case ChannelPowerLevel.CriticalLowPowerLevel:
                MessageBox.Show(«Battery is critical»);
                break;
        }
    }
}

Push-уведомления — это лучший способ взаимодействия с пользователем, когда приложение не запущено, поскольку они обеспечивают наилучшую работу и в то же время сохраняют срок службы батареи. Тем не менее, опыт ограничивается уведомлениями: вы не можете выполнять никакие другие операции, такие как извлечение данных из веб-службы или чтение файла из локального хранилища. Более того, для определенных сценариев, в которых вам не нужны мгновенные уведомления, создание необходимой серверной инфраструктуры может быть слишком дорогим. Подумайте, например, о погодном приложении: не критично, что прогноз обновляется сразу после изменения прогноза.

Для всех этих сценариев в Windows Phone 7.5 введены фоновые агенты, которые представляют собой специальные службы, периодически выполняемые Windows Phone, даже когда приложение не запущено. Существует два типа периодических фоновых агентов: периодические и аудио. В разделе « Новый проект » Visual Studio вы найдете множество шаблонов для всех поддерживаемых типов агентов. В этом разделе мы увидим, как периодические агенты работают подробно.

Совет. Даже если фоновый агент является отдельным проектом Visual Studio, он разделяет те же ресурсы с приложением переднего плана. Например, они находятся в одном и том же локальном хранилище, поэтому вы можете читать данные, созданные приложением в агенте, и наоборот.

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

Первое ограничение касается поддерживаемых API : в фоновом агенте может использоваться только ограниченное количество API. По сути, все API, связанные с пользовательским интерфейсом, запрещены, поскольку агенты не могут взаимодействовать с интерфейсом приложения. Вы можете найти полный список неподдерживаемых API в документации MSDN .

Второе ограничение касается памяти : фоновый агент не может использовать более 11 МБ памяти, иначе он будет прерван. Важно подчеркнуть, что в процессе тестирования (когда подключен отладчик Visual Studio) ограничение памяти будет отключено, и фоновый агент не будет завершен, если он использовал более 11 МБ. Вам придется протестировать его в реальной среде, если вы хотите убедиться, что лимит не достигнут.

Третье и последнее ограничение касается времени : фоновый агент автоматически отключается через 14 дней после его инициализации подключенным приложением. Есть два способа преодолеть это ограничение:

  • Пользователь продолжает использовать приложение; Агент может продлеваться еще на 14 дней каждый раз, когда открывается приложение.
  • Агент используется для отправки уведомлений для обновления плитки основного приложения или экрана блокировки; каждый раз, когда агент отправляет уведомление, оно автоматически продлевается еще на 14 дней.

Важно помнить, что если выполнение фонового агента дважды приводит к сбою (поскольку оно превысило предел памяти или вызвало неуправляемое исключение), оно автоматически отключается; приложение должно будет включить его при запуске.

Периодические агенты используются, когда вам нужно часто выполнять небольшие операции. Обычно они выполняются каждые 30 минут (интервал выполнения может иногда сокращаться до каждых 10 минут, чтобы совпадать с другими фоновыми процессами для экономии заряда батареи), и они могут работать до 25 секунд . Пользователи могут управлять периодическими агентами с панели « Настройки» и отключать те, которые им не нужны. Периодические агенты автоматически отключаются, если телефон работает в режиме экономии заряда батареи; они будут автоматически восстановлены при наличии достаточного заряда батареи.

Периодические агенты идентифицируются классом PeriodicTask , который принадлежит пространству имен Microsoft.Phone.Scheduler .

Ресурсоемкие агенты были созданы для противоположного сценария: долгосрочные задачи, которые выполняются время от времени. Они могут работать до 10 минут, но только если телефон подключен к сети Wi-Fi и внешнему источнику питания.

Эти агенты идеально подходят для таких задач, как синхронизация данных. На самом деле они обычно выполняются ночью, когда телефон заряжается. На самом деле, кроме предыдущих условий, телефон не должен использоваться. Экран блокировки должен быть активирован, и никакие другие операции (например, телефонные звонки) не должны выполняться.

Ресурсоемкие агенты идентифицируются ResourceIntensiveTask , который также является частью пространства имен Microsoft.Phone.Scheduler .

Как уже упоминалось, фоновые агенты определяются в проекте отдельно от интерфейсного приложения. Периодические агенты используют один и тот же шаблон и архитектуру, и приложение Windows Phone решит зарегистрировать их как объекты PeriodicTask или ResourceIntensiveTask .

Чтобы создать фоновый агент, вам нужно добавить новый проект в решение, которое содержит ваше приложение для Windows Phone. В окне « Добавить новый проект» вы найдете шаблон « Windows Phone Scheduled Task Agent Windows Phone» в разделе « Windows Phone ».

Проект уже содержит класс, который будет управлять агентом; он называется ScheduledAgent и наследуется от класса ScheduledTaskAgent . Класс уже реализует метод и обработчик событий.

Метод, называемый OnInvoke() , является наиболее важным. Это метод, который запускается при выполнении фонового агента, поэтому он содержит логику, которая выполняет необходимые нам операции. В следующем примере показано, как отправить всплывающее уведомление из фонового агента:

1
2
3
4
5
6
7
8
9
protected override void OnInvoke(ScheduledTask task)
{
    ShellToast toast = new ShellToast();
    toast.Title = «Title»;
    toast.Content = «Text»;
    toast.Show();
 
    NotifyComplete();
}

Важно выделить метод NotifyComplete() , который следует вызывать, как только агент завершит все операции. Он уведомляет операционную систему о том, что задача выполнила свою задачу и что следующая запланированная задача может быть выполнена. Метод NotifyComplete() определяет статус задачи. Если он не вызывается в назначенное время — 25 секунд для периодических задач или 10 минут для ресурсоемких задач — выполнение прерывается.

Есть еще один способ завершить выполнение агента: Abort() . Этот метод вызывается, когда что-то идет не так (например, не выполняются необходимые условия для выполнения агента), и пользователю необходимо открыть приложение, чтобы устранить проблему.

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

В предыдущем примере показано, как отправлять локальные уведомления о тостах. Тост-уведомление идентифицируется классом ShellToast . Вам просто нужно установить все поддерживаемые свойства ( Title , Content и, опционально, NavigationUri , который является глубокой ссылкой). В конце вы должны вызвать метод Show() для его отображения.

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

Фоновый агент определен в отдельном проекте, но зарегистрирован в приложении. Регистрация должна быть сделана при запуске приложения или на странице настроек, если мы даем пользователям возможность включить или отключить его в приложении.

Базовый класс для использования с фоновыми агентами — ScheduledActionService , который представляет планировщик телефона. Он заботится о регистрации всех фоновых агентов и их обслуживании в течение их жизненного цикла.

Первый шаг — определить, какой тип агента вы хотите использовать. Как упоминалось ранее, архитектура фонового агента всегда одинакова; тип (периодический или ресурсоемкий) определяется приложением.

В первом случае вам потребуется создать объект PeriodicTask , а во втором — объект задачи ResourceIntensive . Независимо от типа важно установить свойство Description , которое будет отображаться для пользователей на странице настроек. Он используется для объяснения назначения агента, чтобы пользователи могли решать, стоит ли его включать.

1
2
3
4
5
PeriodicTask periodicTask = new PeriodicTask(«PeriodicTask»);
periodicTask.Description = «This is a periodic task»;
 
ResourceIntensiveTask resourceIntensiveTask = new ResourceIntensiveTask(«ResourceIntensiveTask»);
resourceIntensiveTask.Description = «This is a resource intensive task»;

В обоих случаях фоновые агенты идентифицируются по имени, которое передается в качестве параметра класса. Это имя должно быть уникальным для всех задач, зарегистрированных с PhoneApplicationService класса PhoneApplicationService ; в противном случае вы получите исключение.

Основная операция по добавлению задачи очень проста:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
public void ScheduleAgent()
{
    ScheduledAction action = ScheduledActionService.Find(«Agent»);
    if (action == null || !action.IsScheduled)
    {
        if (action != null)
        {
            ScheduledActionService.Remove(«Agent»);
        }
 
        PeriodicTask task = new PeriodicTask(«Agent»);
        task.Description = «This is a periodic agent»;
        ScheduledActionService.Add(task);
        #if DEBUG
        ScheduledActionService.LaunchForTest(«Agent», TimeSpan.FromSeconds(10));
                #endif
     }
}

Первая операция проверяет, запланирован ли агент уже с помощью метода Find() класса ScheduledActionService , для которого требуется уникальное имя задачи. Эта операция необходима, если мы хотим продлить срок службы агента. Если агент еще не существует или не запланирован (свойство IsScheduled имеет значение false), мы сначала удаляем его из планировщика, а затем добавляем его, поскольку класс ScheduledActionService не предлагает метод простого обновления зарегистрированной задачи. Операция добавления выполняется с использованием метода Add() , который принимает объект PeriodicTask или ResourceIntensiveTask .

Теперь задача запланирована и будет выполнена, когда будут выполнены соответствующие условия. Если вы находитесь на этапе тестирования, вы найдете метод LaunchForTest() полезным; это заставляет казнить агента через фиксированный промежуток времени. В предыдущем примере агент, идентифицируемый по имени PeriodicTask , запускается через пять секунд. Метод LaunchForTest() также можно выполнить в OnInvoke() внутри фонового агента, что позволяет легко имитировать несколько выполнений.

В предыдущем примере вы можете видеть, что мы использовали условную компиляцию для выполнения LaunchForTest() только тогда, когда приложение запускается в режиме отладки. Таким образом, мы гарантируем, что при компиляции приложения в режиме выпуска для публикации в Магазине Windows метод не будет выполнен; в противном случае вы получите исключение, если метод вызывается приложением, установленным из Магазина.

Фоновые агенты являются хорошими примерами философии Windows Phone:

  • Пользователи всегда контролируют ситуацию; они могут отключить любые фоновые агенты, которые им не интересны, через страницу настроек.
  • Производительность и срок службы батареи являются двумя решающими факторами; Windows Phone ограничивает максимальное количество зарегистрированных фоновых агентов.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public void ScheduleAgent()
{
    ScheduledAction action = ScheduledActionService.Find(«Agent»);
    if (action == null || !action.IsScheduled)
    {
        if (action != null)
        {
            ScheduledActionService.Remove(«Agent»);
        }
 
        try
        {
            PeriodicTask task = new PeriodicTask(«Agent»);
            task.Description = «This is a periodic agent»;
            ScheduledActionService.Add(task);
        }
        catch (InvalidOperationException exception)
        {
            if (exception.Message.Contains(«BNS Error: The action is disabled»))
            {
                // No user action required.
            }
 
            if (exception.Message.Contains(«BNS Error: The maximum number of ScheduledActions of this type have already been added.»))
            {
                // No user action required.
            }
 
        }
    }
}

Разница в предыдущем примере заключается в том, что операция Add() выполняется внутри блока try / catch . Таким образом, мы готовы отловить ошибку InvalidOperationException которая может возникнуть.

Мы можем определить сценарий по сообщению об исключении:

  • Ошибка BNS: действие отключено . Пользователь отключил агента, подключенного к нашему приложению, на странице настроек. В этом случае мы должны предупредить пользователя о необходимости включить его снова на странице настроек, прежде чем пытаться зарегистрировать его.
  • Ошибка BSN: максимальное количество запланированных действий этого типа уже добавлено . Пользователь достиг максимально допустимого количества установленных на телефоне агентов. В этом случае нам не нужно ничего делать; Windows Phone отобразит правильное предупреждающее сообщение.
Фоновый агент отключен пользователем на странице настроек

Кроме того, класс ScheduledTask (который является базовым классом, от которого наследуются PeriodicTask и ResourceIntensiveTask ) предлагает некоторые свойства для понимания статуса последнего выполнения, такие как LastScheduledTime который содержит дату и время последнего выполнения, и LastExitReason котором хранится последнее статус исполнения.

В частности, LastExitReason очень полезен для определения того, успешно ли завершилось последнее выполнение ( Completed ), превысило ли оно ограничение памяти ( MemoryQuotaExceeded ) или ограничение по времени ( ExecutionTimeExceeded ), или произошло необработанное исключение ( UnhandledException ).

Существует специальный вид фонового агента, который работает иначе, чем периодические агенты: аудиоагенты, которые используются в приложениях, связанных со звуком, для продолжения воспроизведения звука, когда приложение закрыто. Цель состоит в том, чтобы предложить аналогичный опыт для родного Music + Videos Hub; даже когда приложение не на переднем плане, пользователи могут продолжать слушать свою музыкальную библиотеку.

Фоновый аудиоплеер

Опять же, фоновый агент определен в другом проекте, чем приложение переднего плана. Тем не мение:

  • Агент не нужно инициализировать в приложении переднего плана с использованием класса ScheduledActionService как мы это делали для периодических агентов.
  • Нет ограничений по времени. Агент запускается каждый раз, когда пользователи взаимодействуют с элементами управления музыкой, и он никогда не истекает. Единственным ограничением является то, что запущенная операция должна завершиться в течение 30 секунд.
  • Есть ограничение памяти, но ограничение выше: 20 МБ (имейте в виду, что ограничение памяти не активируется, когда подключен отладчик Visual Studio).
  • В этом сценарии фоновый агент является не просто компаньоном, а ядром приложения; он управляет всеми взаимодействиями с воспроизведением музыки, независимо от того, происходят ли они в приложении переднего плана или встроенном проигрывателе.

Базовый класс для воспроизведения фонового звука называется BackgroundAudioPlayer , который определяет встроенный аудиопроигрыватель Windows Phone. В системе есть только один экземпляр проигрывателя, и им нельзя поделиться. Если пользователи запускают другое приложение, использующее фоновый аудиоагент (включая собственный Music + Videos Hub), оно берет на себя управление воспроизведением звука. Как мы скоро увидим, класс BackgroundAudioPlayer используется как в приложении переднего плана, так и в фоновом агенте для взаимодействия с воспроизведением музыки.

Аудиодорожки, воспроизводимые фоновым аудиоагентом, представлены классом AudioTrack . Каждый трек, кроме ресурса для воспроизведения, содержит все метаданные, такие как название, исполнитель и название альбома.

Путь дорожки задается в Sourceсвойстве, которое может быть удаленным файлом или файлом, хранящимся в локальном хранилище. Тем не менее, большинство свойств могут быть установлены непосредственно при создании AudioTrackобъекта, как в следующем примере:

1
AudioTrack track = new AudioTrack(new Uri("http://www.windowsphonelounge.net/issues/wpl_issue_01.mp3", UriKind.Absolute), "Episode 1", "Igor & Matteo", "Windows Phone Lounge", null);

В предыдущем коде, помимо настройки исходного файла, мы также сразу установили такую ​​информацию, как название, исполнитель и альбом. Вызывается полезное доступное свойство PlayerControls, с помощью которого можно установить, какие элементы управления (воспроизведение, пауза, перемотка вперед и т. Д.) Доступны для дорожки. Таким образом, если вы разрабатываете приложение, например, подключенное к онлайн-радио, вы можете автоматически блокировать опции, которые не поддерживаются (например, кнопка пропуска трека).

Предложения Visual Studio два шаблона для создания фоновых аудио агентов: Windows Phone Audio Playback Agent и Windows Phone Audio Streaming агента. У них одна цель; их отличие заключается в том, что агент потоковой передачи звука Windows Phone необходим для работы с кодеками потоковой передачи мультимедиа, которые изначально не поддерживаются платформой.

Проект фонового аудиоагента уже поставляется с классом под названием AudioAgent, который наследуется от AudioPlayerAgentкласса. Как мы видели в периодических агентах, класс автоматически реализует некоторые методы, которые используются для взаимодействия с агентом. Самые важные из них OnUserAction()и OnPlayStateChanged().

OnUserAction() запускается каждый раз, когда пользователи вручную взаимодействуют с воспроизведением музыки, например, приостанавливают воспроизведение дорожки или нажимают кнопку пропуска дорожки в приложении переднего плана или проигрывателе фона.

Метод возвращает некоторые параметры, которые можно использовать для понимания контекста и выполнения соответствующих операций:

  • BackgroundAudioPlayerобъект, который является ссылкой на заднем плане аудиоплеера
  • AudioTrackобъект, который является ссылкой на трек воспроизводится в данный момент
  • UserActionобъект, который является действие инициируется пользователем

В следующем примере показана типичная реализация OnUserAction()метода:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
protected override void OnUserAction(BackgroundAudioPlayer player, AudioTrack track, UserAction action, object param)
{
    switch (action)
    {
        case UserAction.Pause:
        {
            player.Pause();
            break;
        }
        case UserAction.Play:
            {
                player.Play();
                break;
            }
        case UserAction.SkipNext:
            {
                //Play next track.
                break;
            }
        case UserAction.SkipPrevious:
            {
                //Play previous track.
                break;
            }
    }
    NotifyComplete();
}

Обычно с помощью switchоператора вы будете отслеживать каждое поддерживаемое взаимодействие с пользователем, которое хранится в UserActionобъекте. Затем вы отвечаете, используя методы, предоставляемые BackgroundAudioPlayerклассом. Playи Pauseявляются простейшими государствами для управления; SkipNextи SkipPreviousобычно требуется больше логики, так как вы должны получить предыдущий или следующий трек для воспроизведения в списке из вашей библиотеки.

Обратите внимание, что фоновые аудиоагенты также требуют выполнения NotifyComplete()метода, как только мы закончили управлять операцией; он должен быть вызван в течение 30 секунд, чтобы избежать завершения.

OnPlayStateChanged()Метод вызывается автоматически каждый раз , когда состояние воспроизведения музыки изменяется, но не как прямое следствие ручного действия. Например, когда текущая дорожка заканчивается, агент должен автоматически начать воспроизведение следующей дорожки в списке.

Структура метода очень похожа на OnUserAction()метод. В дополнение к ссылке на фоновый плеер и текущую дорожку в этом случае вы получите PlayStateобъект, который уведомляет вас о том, что происходит.

В следующем примере показана типичная реализация метода:

1
2
3
4
5
6
7
protected override void OnPlayStateChanged(BackgroundAudioPlayer player, AudioTrack track, PlayState playState)
{
    if (playState == PlayState.TrackEnded)
        //Play next track.
 
    NotifyComplete();
}

Совет: Фоновые аудиоагенты не сохраняются в памяти все время, а запускаются только при изменении состояния воспроизведения музыки. Если вам нужно сохранить некоторые данные в разных исполнениях, вам нужно полагаться на локальное хранилище.

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

Чтобы понять состояние воспроизведения (и правильно обновить пользовательский интерфейс), нам нужно снова использовать BackgroundAudioPlayerкласс, который мы видели. Разница в том, что в приложении переднего плана нам нужно использовать Instanceсинглтон, чтобы получить к нему доступ.

Методы, предоставляемые классом, одинаковы, поэтому мы можем использовать его для воспроизведения, приостановки или изменения состояния воспроизведения музыки (например, если мы хотим соединить эти операции с элементами управления вводом, такими как кнопки).

BackgroundAudioPlayerВыставляет важное событие под названием PlayStateChanged, которая запускается каждый раз при изменении состояния воспроизведения. Мы можем использовать его для обновления визуального интерфейса (например, если мы хотим отобразить трек, который воспроизводится в данный момент).

В следующем примере показано, как PlayStateChangedсобытие используется для изменения поведения кнопки воспроизведения / паузы и отображения некоторых метаданных о текущей воспроизводимой дорожке:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public partial class MainPage : PhoneApplicationPage
{
    private BackgroundAudioPlayer player;
 
    // Constructor
    public MainPage()
    {
        InitializeComponent();
        player = BackgroundAudioPlayer.Instance;
        player.PlayStateChanged += new EventHandler(player_PlayStateChanged);
    }
 
    private void OnPlayClicked(object sender, RoutedEventArgs e)
    {
        player.Play();
    }
 
    void player_PlayStateChanged(object sender, EventArgs e)
    {
        if (player.PlayerState == PlayState.Playing)
            Dispatcher.BeginInvoke(() => btnPlay.Content = "Pause");
        else
            Dispatcher.BeginInvoke(() => btnPlay.Content = "Play");
 
        if (player.PlayerState == PlayState.Playing)
        {
 
            Dispatcher.BeginInvoke(() =>
            {
                txtTitle.Text = player.Track.Title;
                txtArtist.Text = player.Track.Artist;
                txtAlbum.Text = player.Track.Album;
            });
        }
    }
}

Предыдущий код должен быть знакомым; у вас есть доступ ко всем свойствам, которые мы видели в фоновом агенте, например, PlayerStateдля определения текущего состояния воспроизведения или Trackдля идентификации текущей воспроизводимой дорожки. Trackне только свойство только для чтения. Если мы хотим установить новую дорожку для воспроизведения в приложении, мы можем просто назначить новый AudioTrackобъект Trackсвойству BackgroundAudioPlayerэкземпляра.

Тревоги и напоминания — это простые способы показывать напоминания пользователям в определенную дату и время, как это делают нативные приложения «Тревога» и «Календарь».

Они работают одинаково. API-интерфейсы принадлежат Microsoft.Phone.Schedulerпространству имен и наследуются от базового ScheduledNotificationкласса. Существует несколько общих свойств двух API:

  • Content : Описание напоминания.
  • BeginTime : Дата и время отображения напоминания.
  • RecurrenceType : Устанавливает, будет ли это повторное или одноразовое напоминание
  • ExpirationTime : Дата и время истечения периодического напоминания.

Каждое напоминание идентифицируется именем, которое должно быть уникальным для всех сигналов тревоги и напоминаний, созданных приложением. Они работают как фоновые агенты; их жизненный цикл контролируется ScheduledActionServiceклассом, который заботится о добавлении, обновлении и удалении их.

Аварийные сигналы идентифицируются Alarmклассом и используются для отображения напоминания, которое не имеет определенного контекста. Пользователи смогут отложить или отклонить его. Особенность сигналов тревоги заключается в том, что они могут воспроизводить собственный звук, который устанавливается в Soundсвойстве.

В следующем примере показано, как создать и запланировать тревогу:

01
02
03
04
05
06
07
08
09
10
11
12
private void OnSetAlarmClicked(object sender, RoutedEventArgs e)
{
    Alarm alarm = new Alarm("Alarm")
    {
        BeginTime = DateTime.Now.AddSeconds(15),
        Content = "It's time!",
        RecurrenceType = RecurrenceInterval.None,
        Sound = new Uri("/Assets/CustomSound.mp3", UriKind.Relative)
    };
 
    ScheduledActionService.Add(alarm);
}

Пример создает сигнал тревоги, который запланирован через 15 секунд после текущей даты и времени, и использует собственный звук, который представляет собой файл MP3 в проекте Visual Studio.

Напротив, напоминания идентифицируются Reminderклассом и используются, когда уведомление связано с определенным контекстом, аналогично тому, как напоминания календаря связаны с назначением.

Контекст управляется с помощью NavigationUriсвойства, которое поддерживает глубокую ссылку. Это страница (с необязательными параметрами строки запроса), которая открывается, когда пользователи нажимают на заголовок напоминания.

01
02
03
04
05
06
07
08
09
10
11
12
13
private void OnSetReminderClicked(object sender, RoutedEventArgs e)
{
    Reminder reminder = new Reminder("Reminder")
    {
        BeginTime = DateTime.Now.AddSeconds(15),
        Title = "Reminder",
        Content = "Meeting",
        RecurrenceType = RecurrenceInterval.None,
        NavigationUri = new Uri("/DetailPage.xaml?id=1", UriKind.Relative)
    };
 
    ScheduledActionService.Add(reminder);
}

В предыдущем коде запланировано напоминание, которое открывает страницу с именем DetailPage.xaml. Используя события навигации, описанные ранее в этой серии, вы сможете получить параметры строки запроса и загрузить запрошенные данные. Также обратите внимание, что Reminderкласс предлагает Titleсвойство, которое не поддерживается сигналами тревоги.

Live Tiles, без сомнения, самая уникальная функция Windows Phone, которую вы не найдете ни на одной другой платформе. Их называют Live Tiles, потому что они не просто ярлыки для открытия приложений; они могут быть обновлены с помощью локальных или удаленных уведомлений для отображения информации, не заставляя пользователей открывать приложение. Многие виды приложений используют эту функцию, например, приложения погоды, которые отображают прогноз, приложения новостей, которые отображают последние заголовки, и приложения фильмов, которые показывают предстоящие названия фильмов.

В Windows Phone 8 появилось много новых функций, касающихся плиток, таких как новые шаблоны и новые размеры.

Приложение может использовать три разных размера для плиток: маленький, средний и широкий. Как разработчики, мы сможем настроить содержимое тайла в соответствии с его размером, чтобы, например, широкая плитка могла отображать больше информации, чем маленькая.

Различные размеры плитки

Windows Phone 8 также представила три различных шаблона для настройки тайла: переворачивание, цикл и пиктограмма. Важно отметить, что вы можете выбрать только один шаблон для вашего приложения; он должен быть объявлен в файле манифеста в разделе пользовательского интерфейса приложения . После того, как вы установите его, вы не сможете изменить его во время выполнения, и все плитки, которые вы собираетесь создать или обновить, должны будут использовать этот шаблон. Кроме того, вы можете выбрать функции (плитки, изображения и т. Д.), Которые будут использоваться для основной плитки в разделе пользовательского интерфейса приложения ; эта информация будет использоваться до тех пор, пока уведомление не обновит ее.

В следующих разделах мы рассмотрим каждый доступный шаблон подробно. Для каждого из них мы обсудим архитектуру и код, необходимый для его обновления с уведомлением. Для удаленных уведомлений мы увидим необходимый XML. Для локальных уведомлений мы рассмотрим API для использования в приложении или в фоновом агенте.

В обоих случаях все поля, которые определяют плитку, являются необязательными. Если вы не установите некоторые из них, эти свойства будут просто игнорироваться. С другой стороны, если ранее заданное поле не было обновлено с уведомлением, старое значение будет сохранено.

Flip — это стандартный шаблон Windows Phone и единственный, который уже был доступен в Windows Phone 7. С помощью этого шаблона вы можете отображать текст, счетчики и изображения на лицевой стороне плитки. Периодически плитка будет поворачиваться или «переворачиваться», чтобы показать противоположную сторону, которая может отображать различный текст или изображения.

Анатомия плитки с использованием флип-шаблона

Как видно из предыдущего рисунка, вы можете настроить переднюю и заднюю стороны плитки. Если вы хотите включить изображение, вы должны использовать один из следующих размеров:

  • Малый: 159 × 159
  • Средний: 336 × 336
  • Широкий: 691 × 336

Флип-шаблон Tile определяется FlipTileDataклассом. В следующем примере показано, как использовать его для определения плитки, которой можно управлять с помощью кода.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
private void OnCreateFlipTileClicked(object sender, RoutedEventArgs e)
{
    FlipTileData data = new FlipTileData
    {
        SmallBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative),
        BackgroundImage = new Uri("Assets/Tiles/FlipCycleTileMedium.png", UriKind.Relative),
        WideBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileLarge.png", UriKind.Relative),
        Title = "Flip tile",
        BackTitle = "Back flip tile",
        BackContent = "This is a flip tile",
        WideBackContent = "This is a flip tile with wide content",
        Count = 5
    };
}

В следующем коде показано, как представлен тот же Tile с использованием определения XML, необходимого для удаленных уведомлений:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<?xml version=»1.0″ encoding=»utf-8″?>
<wp:Notification xmlns:wp="WPNotification" Version="2.0">
  <wp:Tile Id="[Tile ID]" Template="FlipTile">
    <wp:SmallBackgroundImage Action="Clear">[small Tile size URI]</wp:SmallBackgroundImage>
    <wp:WideBackgroundImage Action="Clear">[front of wide Tile size URI]</wp:WideBackgroundImage>
    <wp:WideBackBackgroundImage Action="Clear">[back of wide Tile size URI]</wp:WideBackBackgroundImage>
    <wp:WideBackContent Action="Clear">[back of wide Tile size content]</wp:WideBackContent>
    <wp:BackgroundImage Action="Clear">[front of medium Tile size URI]</wp:BackgroundImage>
    <wp:Count Action="Clear">[count]</wp:Count>
    <wp:Title Action="Clear">[title]</wp:Title>
    <wp:BackBackgroundImage Action="Clear">[back of medium Tile size URI]</wp:BackBackgroundImage>
    <wp:BackTitle Action="Clear">[back of Tile title]</wp:BackTitle>
    <wp:BackContent Action="Clear">[back of medium Tile size content]</wp:BackContent>
  </wp:Tile>
</wp:Notification>

Обратите внимание на Actionатрибут, который установлен для многих узлов. Если вы установите его, не назначая значение узлу, он просто сотрет предыдущее значение, чтобы вернуться к значению по умолчанию.

Шаблон цикла можно использовать для создания визуального опыта, аналогичного тому, который предлагает Центр фотографий. На лицевой стороне плитки может отображаться до девяти изображений.

Анатомия плитки с использованием шаблона цикла

Шаблон цикла предлагает меньше способов настроить плитку, чем два других шаблона, поскольку его фокус — изображения. Размеры изображения такие же, как те, что используются для флип-шаблона:

  • Малый: 159 × 159
  • Средний: 336 × 336
  • Широкий: 691 × 336

Шаблон цикла определяется CycleTileDataклассом, как показано в следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
private void OnCreateCycleTileClicked(object sender, RoutedEventArgs e)
{
    CycleTileData data = new CycleTileData()
    {
        Count = 5,
        SmallBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative),
        Title = "Cycle tile",
        CycleImages = new List<Uri>
        {
            new Uri("Assets/Tiles/Tile1.png", UriKind.Relative),
            new Uri("Assets/Tiles/Tile2.png", UriKind.Relative),
            new Uri("Assets/Tiles/Tile3.png", UriKind.Relative)
        }
    };
}

Следующий XML может использоваться для отправки удаленных уведомлений для обновления тайлов на основе шаблона цикла:

01
02
03
04
05
06
07
08
09
10
11
<?xml version=»1.0″ encoding=»utf-8″?>
<wp:Notification xmlns:wp="WPNotification" Version="2.0">
  <wp:Tile Id="[Tile ID]" Template="CycleTile">
    <wp:SmallBackgroundImage Action="Clear">[small Tile size URI]</wp:SmallBackgroundImage>
    <wp:CycleImage1 Action="Clear">[photo 1 URI]</wp:CycleImage1>
    <wp:CycleImage2 Action="Clear">[photo 2 URI]</wp:CycleImage2>
    <wp:CycleImage3 Action="Clear">[photo 3 URI]</wp:CycleImage3>
    <wp:Count Action="Clear">[count]</wp:Count>
    <wp:Title Action="Clear">[title]</wp:Title>
  </wp:Tile>
</wp:Notification>

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

Анатомия плитки с использованием шаблона цикла

Культовый шаблон имеет два основных отличия от шаблонов переворачивания и цикла. Во-первых, полноразмерные изображения не поддерживаются; вместо этого вы можете указать значок изображения, который отображается рядом со счетчиком. Требуются только два размера изображений:

  • Маленькие и широкие плитки: 110 × 110
  • Средняя плитка: 202 × 202

Другое отличие состоит в том, что можно настроить цвет фона (единственный способ сделать это с другими шаблонами — это использовать изображение с тем цветом фона, который вы предпочитаете). Если вы не установите цвет фона, шаблон автоматически будет использовать тему телефона.

Шаблонная плитка представлена IconicTileDataшаблоном, как показано в следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
13
private void OnCreateIconicTileClicked(object sender, RoutedEventArgs e)
{
    IconicTileData data = new IconicTileData()
    {
        SmallIconImage = new Uri("/Assets/Tiles/IconicTileSmall.png", UriKind.Relative),
        IconImage = new Uri("/Assets/Tiles/IconicTileMediumLarge.png", UriKind.Relative),
        Title = "My App",
        Count = 5,
        WideContent1 = "First line",
        WideContent2 = "Second line",
        WideContent3 = "Third line"
    };
}

В следующем примере представлено представление XML для удаленных push-уведомлений в плитке, в которой используется иконический шаблон:

01
02
03
04
05
06
07
08
09
10
11
12
13
<?xml version=»1.0″ encoding=»utf-8″?>
<wp:Notification xmlns:wp="WPNotification" Version="2.0">
  <wp:Tile Id="[Tile ID]" Template="IconicTile">
    <wp:SmallIconImage Action="Clear">[small Tile size URI]</wp:SmallIconImage>
    <wp:IconImage Action="Clear">[medium/wide Tile size URI]</wp:IconImage>
    <wp:WideContent1 Action="Clear">[1st row of content]</wp:WideContent1>
    <wp:WideContent2 Action="Clear">[2nd row of content]</wp:WideContent2>
    <wp:WideContent3 Action="Clear">[3rd row of content]</wp:WideContent3>
    <wp:Count Action="Clear">[count]</wp:Count>
    <wp:Title Action="Clear">[title]</wp:Title>
    <wp:BackgroundColor Action="Clear">[hex ARGB format color]</wp:BackgroundColor>
  </wp:Tile>
</wp:Notification>

Предыдущий код, в дополнение к тому, что он поддерживается в приложении или в фоновом агенте для обновления основной плитки, также может использоваться для создания нескольких плиток — функция, представленная в Windows Phone 7.5. Вторичные плитки ведут себя как основные: их можно обновлять с помощью уведомлений и перемещать или удалять с экрана «Пуск».

Разница в том, что вторичные тайлы имеют уникальный идентификатор, который является глубокой связью тайла. Основная плитка всегда открывает главную страницу приложения, в то время как дополнительные плитки могут открывать другую страницу приложения и включать один или несколько параметров строки запроса для идентификации контекста. Например, приложение погоды может создавать плитки для любимых городов пользователя, и каждая плитка будет перенаправлять пользователя на страницу прогноза для выбранного города.

Вызывается базовый класс для взаимодействия с Tiles ShellTile, который принадлежит Microsoft.Phone.Shellпространству имен.

Создать вторичный тайл очень просто: вы вызываете Create()метод, передавая глубокую ссылку на тайл и сам тайл, используя один из классов, которые мы видели ранее. В следующем примере показано, как создать дополнительную плитку, используя шаблон сальто:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private void OnCreateFlipTileClicked(object sender, RoutedEventArgs e)
{
    FlipTileData data = new FlipTileData
    {
        SmallBackgroundImage = new Uri("Assets/Tiles/FlipCycleTileSmall.png", UriKind.Relative),
        BackgroundImage = new Uri(“Assets/Tiles/FlipCycleTileMedium.png”, UriKind.Relative),
        WideBackgroundImage = new Uri(“Assets/Tiles/FlipCycleTileLarge.png”, UriKind.Relative),
        Title = “Flip tile”,
        BackTitle = “Back flip tile”,
        BackContent = “This is a flip tile”,
        WideBackContent = “This is a flip tile with wide content”,
        Count = 5
    };
 
    ShellTile.Create(new Uri(“/MainPage.xaml?id=1”, UriKind.Relative), data, true);
}

Когда приложение откроется с помощью этого тайла, вы сможете понять контекст и отобразить правильную информацию, используя OnNavigatedToметод и NavigationContextкласс, который мы использовали ранее в этой серии.

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

Удаление вторичной плитки требует ShellTileповторной работы с классом. Он предоставляет коллекцию под названием ActiveTiles, которая содержит все плитки, принадлежащие приложению, включая основную. Достаточно получить ссылку на плитку, которую мы хотим удалить (используя глубокую ссылку в качестве идентификатора) и вызвать Delete()метод для нее.

1
2
3
4
5
6
7
8
9
private void OnDeleteTileClicked(object sender, RoutedEventArgs e)
{
    Uri deepLink = new Uri(“/MainPage.xaml?id=1”, UriKind.Relative);
    ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri == deepLink);
    if (tile != null)
    {
        tile.Delete();
    }
}

В предыдущем примере удаляется плитка, указанная по глубокой ссылке /MainPage.xaml?id=1. В отличие от создания плитки, приложение не будет закрыто.

Подсказка: не забудьте всегда проверять, есть ли плитка перед его удалением. Как и любой другой Tile, пользователи также могут удалить его на главной странице, нажав и удерживая Tile, а затем нажав значок «Открепить».

Плитка также может быть обновлена. Обновления могут выполняться не только основным приложением, но и в фоновом режиме фоновым агентом.

Подход похож на тот, который мы видели для операции удаления. Сначала нам нужно получить ссылку на плитку, которую мы хотим обновить, а затем мы вызываем Update()метод, передавая объект плитки в качестве параметра. В следующем примере показано, как обновить плитку, использующую шаблон переворота:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private void OnUpdateMainTileClicked(object sender, RoutedEventArgs e)
{
    FlipTileData data = new FlipTileData
    {
        Title = “Updated Flip tile”,
        BackTitle = “Updated Back flip tile”,
        BackContent = “This is an updated flip tile”,
        WideBackContent = “This is an updated flip tile with wide content”,
        Count = 5
    };
    Uri deepLink = new Uri(“/MainPage.xaml?id=1”, UriKind.Relative);
    ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri == deepLink);
    if (tile != null)
    {
        tile.Update(data);
    }
}

Этот Update()метод также можно использовать для обновления основной плитки приложения. Он всегда хранится как первый элемент ActiveTilesколлекции, поэтому достаточно вызвать для него Update()метод, как в следующем примере:

01
02
03
04
05
06
07
08
09
10
11
12
private void OnUpdateMainTileClicked(object sender, RoutedEventArgs e)
{
    FlipTileData data = new FlipTileData
    {
        Title = “Updated Flip tile”,
        BackTitle = “Updated Back flip tile”,
        BackContent = “This is an updated flip tile”,
        WideBackContent = “This is an updated flip tile with wide content”,
        Count = 5
    };
    ShellTile.ActiveTiles.FirstOrDefault().Update(data);
}

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

Подсказка: вы можете пригласить пользователей прикрепить основной лист на начальном экране, но вы не можете форсировать его с помощью кода.

В Windows Phone 8 появился новый способ взаимодействия приложений с пользователями благодаря поддержке экрана блокировки. Есть два способа взаимодействия с ним:

  • Отображать уведомления так же, как приложения «Сообщения» и «Почта» отображают количество непрочитанных сообщений.
  • Изменить изображение экрана блокировки; в частности, приложение может стать поставщиком экрана блокировки и периодически обновлять изображение с помощью фонового агента.

Давайте посмотрим подробно, как поддерживать оба сценария.

На странице настроек пользователи могут выбрать до пяти приложений, которые могут отображать уведомления счетчика, и только одно приложение, которое может отображать текстовые уведомления.

Для поддержки обоих сценариев в нашем приложении нам нужно вручную добавить новое объявление в файл манифеста (не забудьте использовать опцию « Просмотр кода» в контекстном меню, поскольку она не поддерживается визуальным редактором):

1
2
3
4
<Extensions>
  <Extension ExtensionName=“LockScreen_Notification_IconCount” ConsumerID=“{111DFF24-AA15-4A96-8006-2BFF8122084F}” TaskID=“_default” />
  <Extension ExtensionName=“LockScreen_Notification_TextField” ConsumerID=“{111DFF24-AA15-4A96-8006-2BFF8122084F}” TaskID=“_default” />
</Extensions>

Первое расширение используется для поддержки встречных уведомлений, а второе — для текстовых уведомлений. Вы можете объявить только один из них или оба, в соответствии с вашими требованиями.

Если вы хотите поддерживать встречные уведомления, есть другая модификация, применяемая к файлу манифеста: внутри раздела Tokens вы найдете теги, которые определяют основные свойства основной плитки. Один из них называется DeviceLockImageURI, который необходимо указать с путем изображения, которое будет использоваться в качестве значка для уведомлений.

1
<DeviceLockImageURI IsRelative=“true” IsResource=“false”>Assets/WinLogo.png</DeviceLockImageURI>

Изображение должно иметь следующие свойства:

  • Поддерживаемое разрешение составляет 38 × 38.
  • Это должно быть в формате PNG.
  • Он может содержать только прозрачные или белые пиксели. Другие цвета не поддерживаются.

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

  • Если ваше приложение поддерживает встречные уведомления, вам необходимо отправить Tile-уведомление с номером, указанным в Countсвойстве.
  • Если ваше приложение поддерживает текстовые уведомления, вам нужно отправить уведомление Tile с текстом, хранящимся в WideBackContentсвойстве для флип-шаблона или в WideContent1свойстве для иконического шаблона. Шаблон цикла не поддерживает текстовые уведомления.
Уведомление о блокировке экрана со счетчиком и текстом

Отправной точкой для поддержки изображений экрана блокировки, опять же, является файл манифеста. Следующий пример — это объявление, которое следует добавить в раздел « Расширения »:

1
2
3
<Extensions>
  <Extension ExtensionName=“LockScreen_Background” ConsumerID=“{111DFF24-AA15-4A96-8006-2BFF8122084F}” TaskID=“_default” />
</Extensions>

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

API позволяют вам проверить, было ли приложение уже установлено в качестве поставщика, или вы можете спросить пользователя. Если приложение установлено в качестве провайдера, вы сможете эффективно менять обои; в противном случае вы получите исключение.

Два класса являются частью Windows.Phone.System.UserProfileпространства имен: LockScreenManagerмогут использоваться для определения текущего статуса провайдера и LockScreenмогут эффективно выполнять операции на экране блокировки.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
private async void OnSetWallpaperClicked(object sender, RoutedEventArgs e)
{
    Uri wallpaper = new Uri(“ms-appx:///Assets/Wallpapers/Wallpaper1.jpg”, UriKind.RelativeOrAbsolute);
 
    bool isProvider = LockScreenManager.IsProvidedByCurrentApplication;
    if (isProvider)
    {
        LockScreen.SetImageUri(wallpaper);
    }
    else
    {
        LockScreenRequestResult lockScreenRequestResult = await LockScreenManager.RequestAccessAsync();
        if (lockScreenRequestResult == LockScreenRequestResult.Granted)
        {
            LockScreen.SetImageUri(wallpaper);
        }
    }
}

Первый шаг — проверить, установлено ли текущее приложение в качестве поставщика, используя IsProvidedByCurrentApplicationсвойство LockScreenManagerкласса. В противном случае мы запрашиваем разрешение пользователя, вызывая RequestAccessAsync()метод. В ответ мы получаем выбор пользователя, который может быть положительным ( LockScreenRequestResult.Granted) или отрицательным ( LockScreenRequestResult.Denied).

В обоих случаях, только если приложение было установлено в качестве поставщика, мы можем эффективно изменить изображение экрана блокировки, используя SetImageUri()метод, который требует пути изображения в качестве параметра. Изображение может быть частью проекта (как в предыдущем примере, где мы используем ms-appx:///префикс) или храниться в локальном хранилище (в этом случае мы должны использовать ms-appdata:///Local/префикс). Удаленные изображения не поддерживаются напрямую; они должны быть загружены перед использованием их в качестве фона экрана блокировки.

Предыдущий код также можно использовать для фонового агента. Разница в том, что вы сможете проверить, установлено ли приложение в качестве поставщика и, в конечном итоге, изменить изображение. Вы не сможете запросить у пользователя разрешение использовать ваше приложение в качестве поставщика, поскольку фоновые агенты не могут взаимодействовать с пользовательским интерфейсом.

В этом учебном пособии мы увидели, что живые приложения являются одной из основных концепций в разработке Windows Phone, и для правильного создания качественного опыта участвуют многие факторы, такие как уведомления, агенты и плитки.

Следующий список подробно описывает то, что мы узнали:

  • Push-уведомления — лучший способ уведомить пользователей о чем-либо, даже если приложение находится в фоновом режиме. Приложение может отправлять тост-уведомления или обновлять плитки. Мы увидели, как создать требуемую архитектуру для доступа как на стороне клиента, так и на стороне сервера.
  • Push-уведомления предлагают лучший подход для оптимизации времени автономной работы и производительности, но они поддерживают ограниченные сценарии и требуют серверного приложения для их отправки. По этой причине в Windows Phone появились фоновые агенты, которые мы можем периодически запускать для отправки уведомлений или выполнения операций общего назначения, даже если приложение не находится на переднем плане.
  • Windows Phone предлагает специальный тип фонового агента, называемый аудио фоновым агентом, который используется в сценариях воспроизведения аудио. Приложения могут воспроизводить аудио, даже если они не запущены, как например, Music + Videos Hub.
  • Тревоги и напоминания — это простой способ показать напоминания пользователям, когда приложение не запущено.
  • Живые плитки являются одной из отличительных особенностей платформы. Мы научились настраивать их, выбирая между различными шаблонами и размерами.
  • Мы увидели еще одну новую функцию, представленную в Windows Phone 8, поддержку экрана блокировки: приложения теперь могут взаимодействовать с экраном блокировки, отображая уведомления и меняя обои.

Это руководство представляет собой главу из Windows Phone 8 Succinctly , бесплатной электронной книги от команды Syncfusion.