Статьи

«Ноль строк кода» для проверки обновлений в Windows Phone в приложении

«Хорошие художники копируют; великие художники воруют »- Стив Джобс

Каждый   разработчик Windows Phone , имеющий друзей, родственников или жену, не имеющих технических знаний, с Windows Phone, знает проблему: многие пользователи не проверяют свой Магазин регулярно и просто не обновляют свои приложения — или, по крайней мере, недостаточно часто. Высокоинтеллектуальный  Педро Ламас  — старший инженер Nokia, автор  инструментария Cimbalino для Windows Phone и мой личный спаситель, когда я столкнулся с некоторыми очень экзотическими проблемами с Nokia Music — недавно описал  способ автоматической проверки наличия обновлений  внутри приложения .С его разрешения я решил упаковать основную логику в поведение, очень похожее на то, что я делал с логикой оценок в моем предыдущем посте — так что вы можете перетащить это на свою главную страницу приложения и покончить с этим. Педро использовал что-то вроде мой SafeBehavior  в Cimbalino, и теперь я использую часть его кода в  библиотеке wp7nl на codeplex . Вот как работает сообщество. В этом свете цитата в верхней части этой статьи выбрана неудачно ?

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

образ

  • Заголовок  — это текст, который будет отображаться в окне сообщения при обнаружении новой версией поведения. Значением по умолчанию является «Новая версия!»
  • Сообщение — это текст, который появится внутри окна сообщения, когда поведение обнаружит новую версию. Значение по умолчанию: «Доступна новая версия этого приложения, вы хотите обновить?»

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

Само поведение является довольно простым SafeBehavior и выглядит так:

using System;
using System.Globalization;
using System.Net;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Xml;
using Microsoft.Phone.Tasks;
using Wp7nl.Utilities;

namespace Wp7nl.Behaviors
{
  /// <summary>
  /// A behavior checking from in the app if there's an update available. 
  /// </summary>
  public class UpgradeCheckBehavior : SafeBehavior<Page>
  {
    private ManifestAppInfo appInfo;

    protected override void OnSetup()
    {
      appInfo = new ManifestAppInfo();
      CheckUpgrade();
    }

    private void CheckUpgrade()
    {
#if !DEBUG
      GetLatestVersion().ContinueWith(ProcessResult);
#endif
    }
  }
}

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

Используя мой   вспомогательный класс ManifestAppInfo, я извлекаю все метаданные приложения, которые используются GetLatestVersion — по сути, слегка измененную версию работы Педро:

/// <summary>
/// This method is almost 100% stolen from 
/// http://www.pedrolamas.com/2013/07/24/checking-for-updates-from-inside-a-windows-phone-app/
/// </summary>
private Task<Version> GetLatestVersion()
{
  var cultureInfoName = CultureInfo.CurrentUICulture.Name;
  var url = string.Format(
      "http://marketplaceedgeservice.windowsphone.com/v8/catalog/apps/{0}?os={1}&cc={2}&oc=&lang={3}​",
      appInfo.ProductId,
      Environment.OSVersion.Version,
      cultureInfoName.Substring(cultureInfoName.Length - 2).ToUpperInvariant(),
      cultureInfoName);

  var request = WebRequest.Create(url);

  return Task.Factory.FromAsync(request.BeginGetResponse, result =>
  {
    try
    {
      var response = (HttpWebResponse)request.EndGetResponse(result);
      if (response.StatusCode != HttpStatusCode.OK)
      {
        throw new WebException("Http Error: " + response.StatusCode);
      }

      using (var outputStream = response.GetResponseStream())
      {
        using (var reader = XmlReader.Create(outputStream))
        {
          reader.MoveToContent();
          var aNamespace = reader.LookupNamespace("a");
          reader.ReadToFollowing("entry", aNamespace);
          reader.ReadToDescendant("version");
          return new Version(reader.ReadElementContentAsString());
        }
      }
    }
    catch (Exception)
    {
      return null;
    }
  },
  null);
}

Я думаю, что мое единственное дополнение к этому — это попытка поймать метод, поскольку у меня, как правило, возникали некоторые странные ошибки при запуске его в Visual Studio. Этот метод ловко загружает метаданные приложения из магазина и извлекает из него версию.

И тогда, конечно, единственное, что осталось, — это сравнить полученную версию с текущей версией, а если текущая версия больше, отобразить окно с просьбой обновить пользователя:

private void ProcessResult(Task<Version> t)
{
  if(t.IsCompleted && t.Result != null )
  {
    var currentVersion = new Version(appInfo.Version);
    if (currentVersion < t.Result )
    {
      DoShowUpgrade();
    }
  }
}

private void DoShowUpgrade()
{
  Deployment.Current.Dispatcher.BeginInvoke(() =>
  {
    var result = MessageBox.Show(Message, Caption, MessageBoxButton.OKCancel);
    if (result == MessageBoxResult.OK)
    {
      var marketplaceReviewTask = new MarketplaceDetailTask();
      try
      {
        marketplaceReviewTask.Show();
      }
      catch (InvalidOperationException ex)
      {
      }
    }
  });
}

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

Как это возможно? Приложение, созданное Visual Studio, запускает временный идентификатор, которого даже не должно быть в Магазине! Это верно, если вы не  сделаете  это с реальным идентификатором. Поэтому я открыл файл WPAppManifest.xml, который находится в папке «Свойства», и немного испортил настройки:образ

Я изменил идентификатор приложения на идентификатор моей последней игры  2 Phone Pong  в магазине и изменил номер версии на 0.9.0.0 (в то время как номер версии в Магазине, конечно, по крайней мере, 1.0.0.0). Теперь приложение думает, что спросить в магазине номер версии 2 Phone Pong, возвращает (на момент написания этой статьи) 1.0.0.0, выясняет, что она ниже текущей версии, и выдает сообщение. Если вы нажмете кнопку «ОК», это приведет вас к 2 Phone Pong в Магазине. Это доказывает, что код Педро на самом деле работает, и теперь у вас есть решение с нулевым кодом, позволяющее вашим пользователям использовать самую лучшую версию вашего приложения, и больше нет оправданий не делать этого ?

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

Скоро я включу это в следующую версию моей  библиотеки wp7nl на codeplex