Microsoft — мировой лидер в области ПО и ИТ-услуг
381,25
рейтинг
15 декабря 2011 в 15:20

Разное → Обновления Live Tiles в фоновых агентах


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

Одна из ключевых концепций – Live Tiles или «живые тайлы» — «иконки приложений на стероидах». Правильное использование Live Tiles привязывает пользователя к приложению и даёт совсем другие ощущения при использовании. Например, когда я выбирал приложение для прогноза погоды, ключевым критерием для меня являлось то, чтобы приложение имело Live Tile и оперативно обновляло прогноз. В результате, после настройки приложения и закрепления Live Tile на стартовом экране, я практически больше это приложение не запускал, но при этом, сразу же после снятия лок-скрина вижу оперативную информацию о погоде.

Хороший пример, подумаете вы, но как нам сделать так?


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

Давайте на примере разберёмся, как реализовать обновление Live Tile из фонового агента.

Сначала я подготовим 2 фоновые картинки для тайлов. Я сделал следующие:

и

Это PNG файлы, размера 173х173 на прозрачном фоне, чтобы через него была видна подложка цвета темы, установленной сейчас на телефоне.

Потом создадим простой проект на основе Windows Phone Application на C#. Добавим подготовленные картинки в него. Я их добавил под простыми названиями 1.png и 2.png. В настройках нужно установить для них тип Content и условия копирования Copy if newer.

Перейдём непосредственно к разработке приложения.

У нас будет обновляться основной тайл приложения и один вторичный тайл.

Замечание: У приложения всегда есть тайл приложения, даже если он не закреплён на стартовом экране. Вторичных тайлов может быть несколько и их необходимо создавать из приложения.

Добавим вторую страницу в приложение и назовём её LandingPage.xaml — она будет открываться, когда будет нажиматься вторичный тайл нашего приложения.

Перейдём к стартовой странице MainPage.xaml приложения и добавим на неё кнопку:
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
   <TextBlock x:Name="ApplicationTitle" Text="LIVE TILES UPDATE DEMO" Style="{StaticResource PhoneTextNormalStyle}"/>
   <TextBlock x:Name="PageTitle" Text="настройки" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>

<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   <StackPanel>
      <Button Height="100" Content="Добавить ещё тайл" Name="TileAdder" Click="TileAdder_Click"></Button>
    </StackPanel>            
</Grid>


В код добавим обработчик нажатия кнопки, в котором будем добавлять вторичный тайл с некой «демо-логикой»:
private void TileAdder_Click(object sender, RoutedEventArgs e)
{
    IsolatedStorageFile CounterStorage = IsolatedStorageFile.GetUserStoreForApplication();
    IsolatedStorageFileStream CounterStream = CounterStorage.OpenFile("counter", System.IO.FileMode.OpenOrCreate);
    int count = CounterStream.ReadByte();
    if (count < 0) count = 1;
            
            
    StandardTileData appTileData = new StandardTileData();

    if ((count % 2) == 0)
    {
        appTileData.Title = "Агент 002";
        appTileData.BackgroundImage = new Uri("/1.png", UriKind.RelativeOrAbsolute);
    }
    else
        if ((count % 7) == 0)
        {
            appTileData.Title = "Агент 007";
            appTileData.BackgroundImage = new Uri("", UriKind.RelativeOrAbsolute);
        }
        else
        {
            appTileData.Title = "Агент 000";
            appTileData.BackgroundImage = new Uri("/2.png", UriKind.RelativeOrAbsolute);
        }

    appTileData.Count = count;
            
    CounterStream.Seek(0, SeekOrigin.Begin);
    count = count + 5;
    if (count > 99) count = 99;
    CounterStream.WriteByte((byte)count);
    CounterStream.Close();

    ShellTile.Create(new Uri("/LandingPage.xaml", UriKind.RelativeOrAbsolute), appTileData);
}


Чтобы создать вторичный тайл, сначала мы создаём объект, содержащий данные тайла, затем вызываем метод, которому передаём URI страницы, которая будет вызываться и объект с данными — всё предельно просто.

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

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

Поскольку, если свойство Count больше, чем 99, отображаться будет всё равно 99, мы считаем только до 99 и спокойно можем конвертировать из int в byte.

Теперь нам нужно создать фоновый агент, который бы обновлял данные на наших тайлах. Доавим в решение проект типа Windows Phone Scheduler Task Agent, после чего добавим в основной проект ссылку на него и добавим его namespace в блок using главной страницы основного приложения.

Также в блок using главной страницы осинового приложения надо не забыть добавить namespace для работы с Isolated Storage и агентами. Я назвал проект с агентом UpdateTileAgent и в моём случае этот блок выглядит следующим образом:
using UpdateTileAgent;

using Microsoft.Phone.Scheduler;

using System.IO.IsolatedStorage;
using System.IO;


Итак, нам нужно создать и зарегистрировать фоновый агент. Я буду это делать в обработчике события Loaded основной страницы. Для этого я добавлю этот обработчик, а в обработчик добавлю код создания агента:
const string UpdateTileAgentName = "Agent-Tile";
        
// Constructor
public MainPage()
{
    InitializeComponent();

    this.Loaded += new RoutedEventHandler(MainPage_Loaded);
}

void MainPage_Loaded(object sender, RoutedEventArgs e)
{
    PeriodicTask myPeriodicTask = ScheduledActionService.Find(UpdateTileAgentName) as PeriodicTask;

    if (myPeriodicTask != null)
    {
        try
        {
            ScheduledActionService.Remove(UpdateTileAgentName);
        }
        catch (Exception ex)
        {
            MessageBox.Show("Невозможно удалить ранее созданный сервис:" + ex.Message);
        }
    }

    myPeriodicTask = new PeriodicTask(UpdateTileAgentName);
    myPeriodicTask.Description = "Agent-Tile";

    try
    {
        ScheduledActionService.Add(myPeriodicTask);

#if DEBUG
        ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
#endif
    }
    catch (Exception ex)
    {
        MessageBox.Show("Невозможно создать сервис:" + ex.Message);
    }

    if (ShellTile.ActiveTiles.Count() > 1) TileAdder.IsEnabled = false;

}


В целом, код достаточно простой. Хочу обратить внимание на несколько особенностей.

Периодические агенты запускаются раз в полчаса. Для отладки это не очень удобно, поэтому я использую условную директиву компиляции, в которой в отладочной сборке прошу запустить агента через 10 секунд.
#if DEBUG
        ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
#endif


Я не хочу, чтобы можно было создать несколько вторичных тайлов со ссылкой на одну и ту же страницу. Поэтому, я проверяю количество тайлов и в зависимости от этого включаю/выключаю кнопку добавления тайлов.
if (ShellTile.ActiveTiles.Count() > 1) TileAdder.IsEnabled = false;

Поле Description агента является обязательным и без него он не добавится в планировщик.

Всё, с самим приложением мы закончили. Теперь можно приступать к агенту.

Основная работа в агенте ведётся в методе OnInvoke:
 protected override void OnInvoke(ScheduledTask task)


Для того, чтобы удобно работать с тайлами, не забудьте добавить необходимые директивы в using блок кода агента. У меня он выглядит следующим образом:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Microsoft.Phone.Scheduler;

using Microsoft.Phone.Shell;
using System.IO.IsolatedStorage;
using System.IO;


Добавим в код агента обновление тайла исходя из «внешних» данных в виде счётчика и добавим немного разнообразия, используя логику, аналогичную логике создания вторичного тайла.

Я выделил код обновления тайла в отдельную функцию.
void TileUpdater(ShellTile tile, int count)
{
    StandardTileData appTileData = new StandardTileData();
            
    if ((count % 2) == 0)
    {
        appTileData.Title = "Агент 002";
        appTileData.BackgroundImage = new Uri("/1.png", UriKind.RelativeOrAbsolute);
    }
    else
        if ((count % 7) == 0)
        {
            appTileData.Title = "Агент 007";
            appTileData.BackgroundImage = new Uri("", UriKind.RelativeOrAbsolute);
        }
        else
        {
            appTileData.Title = "Агент 000";
            appTileData.BackgroundImage = new Uri("/2.png", UriKind.RelativeOrAbsolute);
        }

    appTileData.Count = count+1;
            

    tile.Update(appTileData);
        
}


Сам же метод OnInvoke выглядит так:
protected override void OnInvoke(ScheduledTask task)
{
    ShellTile AppTile = ShellTile.ActiveTiles.FirstOrDefault();

    IsolatedStorageFile CounterStorage = IsolatedStorageFile.GetUserStoreForApplication();
    IsolatedStorageFileStream CounterStream = CounterStorage.OpenFile("counter", System.IO.FileMode.OpenOrCreate);
    int count = CounterStream.ReadByte();
    if (count < 0) count = 1;

    //специальным образом обновляем тайл приложения
    TileUpdater(AppTile, count+1);

    //у нас есть тайлы кроме тайла приложения
    if (ShellTile.ActiveTiles.Count() > 1)
    {
        //первый тайл - этой тайл приложения
        IEnumerable<ShellTile> tiles = ShellTile.ActiveTiles.Skip(1);
                
        //обовляем все доступные Secondary Tiles
        foreach (ShellTile st in tiles)
        {
            TileUpdater(st, count);
        }
    }

    CounterStream.Seek(0, SeekOrigin.Begin);
    count++;
    if (count > 99) count = 99;
    CounterStream.WriteByte((byte)count);
    CounterStream.Close();


#if DEBUG
    ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
#endif
    NotifyComplete();
}


Что мы здесь видим? Уже знакомую нам просьбу запустить агент через 10 секунд и разделённую логику обновления основного тайла приложения и дополнительных. Хочу напомнить, что это можно реализовать, потому что, тайл приложения первый в списке и есть всегда, даже если приложение не закреплено на стартовом экране.

Также вы здесь можете обновить в агенте и другие свойства тайла. Подробнее о тайлах и свойствах можно прочитать здесь: http://msdn.microsoft.com/ru-ru/windowsphone/hh505822.

Теперь же можно собрать и запустить приложение в эмуляторе. Закрепите само приложение на стартовом экране, не забудьте создать второй тайл. Посмотрите, как меняются тайлы со временем.

Теперь, вооружившись этими знаниями, я думаю, вы без проблем добавите продвинутую функциональность Live Tiles в свои Windows Phone приложения.

UPD: Спасибо max_sokolov за напоминание. Периодически запускающиеся фоновые агенты имеют следующие свойства:
  • запускаются приблизительно раз в 30 минут
  • время их работы ограничено 25 секунадами (некоторые источники говорят о 15 секундах)
  • потребление ими мощностей процессора ограничено
  • потребление ими памяти ограничено (зависит от платформы, мин. значение — 6Мб)
  • система может решить не запускать агента, если ресурсов недостаточно (например, телефон разряжен)
  • если агент 3 раза завершится с ошибкой, система перестанет его запускать
  • агент добавляется в расписание запуска на 14 дней, потом можно/нужно обновить

Полезные ссылки:
Центр разработки Windows Phone на MSDN
Windows Phone SDK 7.1
Форумы по разработке под Windows Phone на русском языке
Автор: @stasus
Microsoft
рейтинг 381,25
Microsoft — мировой лидер в области ПО и ИТ-услуг

Комментарии (24)

  • 0
    Стоить еще отметить, что помимо того, что периодические агенты запускаются раз в 30 минут, они еще работают не больше 25 секунд и могут потреблять не больше 6 мб памяти. Пруф.
    • 0
      Насчёт потребления памяти — зависит от платформы — это минимально возможное.

      За напоминание — спасибо — сейчас в текст добавлю.
    • 0
      на эмуляторе для тестов может раз в 10 минут запускаться
      • 0
        На эмуляторе под отладкой можно запускать с любоай разумной переодичностью:

        ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
    • +1
      У фоновых агентов есть еще одно ограничение — они работают максимум 2 недели с момента запуска. Как минимум раз в две недели агент необходимо перезапускать. А сделать это можно только из приложения, так как агент сам себя перезапустить не может.

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

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

      P.S.
      А погодные приложения, про которые сказано в начале статьи, скорее всего обновляются, используя ShellTileSchedule. Это механизм, позволяющий периодически грузить с удаленного сервера картинку для тайла. И, если мы генерируем контент для тайла локально, использовать его не получиться.

      Если я где-то не прав, поправьте меня.
      • 0
        Да, 2 недели максимальное время — я дописал в UPD.

        Погодное приложение имеет BackGroud Agent при отключении которого, как мне показалось, погода на тайле перестала обновляется. Хотя с такой погодой которая сейчас стоит в Москве, я мог и ошибиться.

        Но сегодня, когда на вечерней школе мне задали тот же вопрос — про ренью агента в агенте, вспомнив про приложение начал сомневаться, что оно использует агента именно для обновления погодного тайла.
      • 0
        Думаю, рассчитывают на сцерании, что хотя бы раз в 2 недели пользователь запустит «полезное» приложение. У меня где-то так и выходит — уточнить раскладку температуры вперёд на неделю, например.
        • 0
          Ну это понятно. Просто я пытался написать приложение, где вся функциональность — периодический вывод информации, генерируемой локально, на тайл. В таком случае рассчитывать на периодический запуск приложения не приходиться.
      • 0
        Что же касается ShellTileSchedule который может работать «бесконечно» — да это решение, но у него тоже есть ограничения — он обновляет только background image, причём берёт URI только на удалённый. Это означает, что нужен север и сервис генерации кратинок.
        • 0
          Тут еще проблема. Это ограниченный выбор частоты обновления. Если нужны свежие данные и часто, то уж лучше BA использовать.
    • 0
      Еще одно замечание, возможно такое наблюдается только у меня, но у меня не работает этот код
      #if DEBUG
      ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
      #endif

      помогло это
      #if DEBUG_AGENT
      ScheduledActionService.LaunchForTest(UpdateTileAgentName, TimeSpan.FromSeconds(10));
      #endif

      может кто-то сэкономит время.
      • 0
        Зависит от того, какие символы прописаны в компиляции в свойствах проекта.
  • 0
    Эм, а линк на выбранное приложение погоды?)
  • 0
    Мне кажется, что фоновые агенты «слегка» параноидально урезаны в функциональности
    • 0
      Отнюдь не параноидально. Телефон должен кушать мало энергии.
      • –3
        К сожелению, вп7 не демонстрирует выдающихся над андроидом результатов по времени автономной работы.
        • +1
          Сложно сказать. Мой сутки держит при достаточно активной работе. Андроидов не было.
          • +1
            Такая же ситуация. Постоянно включен WiFi, частенько радио, плюс игры по пути на/с работы. Заряжать приходится реже, чем раз в сутки. Андроида не была, сравнивать не могу.
            • 0
              Мой HD7 выдерживает день только если его использовать как простой телефон (и то — редко). Это позорище. Вы оба пишите, что андроидов не было, так что вам получается, действительно не с чем сравнивать.
              • 0
                HTC Mozart. Как телефон 3-5 дней, в зависимости от местонахождения; с постоянно включенными 3g, WiFi 1-2 дня, в зависимости от того, где нахожусь: g-e-3g-h оч. энергозатратные переключения
  • 0
    У меня почему-то тайлы обновляются только когда телефон на зарядке или когда edge включен. Т.е. Тогда, когда есть постоянный доступ в сеть. При этом при обычном использовании к вайфаю коннектится только при разблокировке. У всех так? И если да то как у вас тайлы так хорошо обновляются?
    • +1
      На WP7 Wi-Fi работает только при разблокированном экране. Как только блокируете экран, почти сразу соединение по Wi-Fi пропадает. Исключение — телефон на зарядке. В это время Wi-Fi работает постоянно. И, вроде бы, если скачиваете приложение из Marketplace через Wi-Fi, то оно докачается даже после блокировки экрана. Так что для постоянного обновления «живых плиток» нужно подключение через «передачу данных».
      • 0
        Это о конкретной модели речь? У меня Samsung Omnia W. Только что проверил — при обновлении игры на 50% нажал power, через минуту включил, увидел, что все благополучно загружено.

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

Самое читаемое Разное