WP8 Фоновый агент - Код выполняется нормально вне агента, происходит сбой внутри?
У меня есть фоновый агент, который я создал, чтобы обновить мою живую плитку. Агент планирует и выполняет нормально, но код, который выполняет агент, стал проблемой - он просто не в состоянии выполнить полностью и не предоставляет ошибки. Из того, что я могу сказать, нет никаких ограниченных API, которые я использую, за исключением того, что инструментарий Cimbalino каким-то образом создает проблему, хотя я использую версию, специфичную для фонового агента, из NuGet.
Код перестает выполняться, когда RenderText() и RenderTextWide() не запускаются. Ошибка не предоставляется. Тот же код прекрасно работает при запуске в приложении переднего плана.
using System;
using System.Windows;
using Microsoft.Phone.Scheduler;
using Microsoft.Phone.Shell;
using Microsoft.Phone.Info;
using Cimbalino.Phone.Toolkit;
using System.Linq;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System.Windows.Controls;
using System.IO.IsolatedStorage;
using Cimbalino.Phone.Toolkit.Extensions;
namespace ScheduledTaskAgent1
{
public class ScheduledAgent : ScheduledTaskAgent
{
private static volatile bool _classInitialized;
public ScheduledAgent()
{
if (!_classInitialized)
{
_classInitialized = true;
// Subscribe to the managed exception handler
Deployment.Current.Dispatcher.BeginInvoke(delegate
{
Application.Current.UnhandledException += ScheduledAgent_UnhandledException;
});
}
}
/// Code to execute on Unhandled Exceptions
private void ScheduledAgent_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
if (System.Diagnostics.Debugger.IsAttached)
{
System.Diagnostics.Debugger.Break();
}
}
//Method to create normal tile image
private static void RenderText(string currproperty)
{
WriteableBitmap b = new WriteableBitmap(336, 336);
var canvas = new Grid();
canvas.Width = b.PixelWidth;
canvas.Height = b.PixelHeight;
var background = new Canvas();
background.Height = b.PixelHeight;
background.Width = b.PixelWidth;
SolidColorBrush backColor = new SolidColorBrush(Colors.Transparent);
background.Background = backColor;
TextBlock currtemp = new TextBlock();
currtemp.FontSize = 100;
currtemp.FontFamily = new FontFamily("Segoe UI Light");
currtemp.FontWeight = FontWeights.Bold;
currtemp.Foreground = new SolidColorBrush(Colors.White);
currtemp.Text = currproperty;
currtemp.Margin = new Thickness(20, 10, 0, 0);
currtemp.Width = b.PixelWidth - currtemp.Margin.Left * 2;
canvas.Children.Add(currtemp);
b.Render(background, null);
b.Render(canvas, null);
b.Invalidate();
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream imageStream = new IsolatedStorageFileStream("/Shared/ShellContent/BackBackgroundImage2.png", System.IO.FileMode.Create, isf))
{
b.SavePng(imageStream);
}
}
}
//Method to create wide tile image
private static void RenderTextWide(string currproperty)
{
WriteableBitmap b = new WriteableBitmap(691, 336);
var canvas = new Grid();
canvas.Width = b.PixelWidth;
canvas.Height = b.PixelHeight;
var background = new Canvas();
background.Height = b.PixelHeight;
background.Width = b.PixelWidth;
//Created background color as Accent color
SolidColorBrush backColor = new SolidColorBrush(Colors.Transparent);
background.Background = backColor;
TextBlock currtemp = new TextBlock();
currtemp.FontSize = 100;
currtemp.FontFamily = new FontFamily("Segoe UI Light");
currtemp.FontWeight = FontWeights.Bold;
currtemp.Foreground = new SolidColorBrush(Colors.White);
currtemp.Text = currproperty;
currtemp.Margin = new Thickness(20, 10, 0, 0);
currtemp.Width = b.PixelWidth - currtemp.Margin.Left * 2;
canvas.Children.Add(currtemp);
using (IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication())
{
using (IsolatedStorageFileStream imageStream = new IsolatedStorageFileStream("/Shared/ShellContent/WideBackBackgroundImage2.png", System.IO.FileMode.Create, isf))
{
b.SavePng(imageStream);
}
}
}
//When task invokes, run the tile update code.
protected override void OnInvoke(ScheduledTask task)
{
ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault();
if (tile != null)
{
FlipTileData flipTile = new FlipTileData();
flipTile.Title = "";
flipTile.BackTitle = "Atmosphere";
RenderText("Test");
RenderTextWide("Test");
flipTile.BackBackgroundImage = new Uri("/Assets/NewUI/1.PNG", UriKind.Relative); //Default image for Background Image Medium Tile 336x336 px
flipTile.BackgroundImage = new Uri(@"isostore:/Shared/ShellContent/BackBackgroundImage2.png", UriKind.Absolute); //Generated image for Back Background 336x336
flipTile.WideBackBackgroundImage = new Uri("/Assets/NewUI/2.PNG", UriKind.Relative); ////Default image for Background Image Wide Tile 691x336 px
flipTile.WideBackgroundImage = new Uri(@"isostore:/Shared/ShellContent/WideBackBackgroundImage2.png", UriKind.Absolute);
tile.Update(flipTile);
//Tile updated, tell agent operation complete
NotifyComplete();
}
}
}
}
3 ответа
Я просто запустил код, которым вы поделились, в фоновом агенте с присоединенным отладчиком, и проблема была совершенно очевидной - это исключение! Точнее, недопустимое исключение межпоточного доступа. Вся работа, связанная с пользовательским интерфейсом, должна выполняться в потоке пользовательского интерфейса, и создание таких элементов управления и WriteableBitmaps - такая работа.
Решение
Вам нужно использовать Deployment.Current.Dispatcher.BeginInvoke
вызывать RenderText
а также RenderTextWide
в соответствующей теме. Вы также можете просто вызвать все в OnInvoke
в потоке пользовательского интерфейса.
Замечания: Deployment.Current.Dispatcher.BeginInvoke
вызывает данное действие асинхронно, поэтому, если вы не запускаете все в потоке пользовательского интерфейса, вам может потребоваться сделать что-то для синхронизации разных потоков.
NotifyComplete
Вы должны ВСЕГДА звонить NotifyComplete
После того, как вы сделали всю работу. По сути, вам нужно попробовать-поймать (все) и затем вызвать NotifyComplete
, Кроме того, вы можете положить его в ScheduledAgent_UnhandledException
так, на всякий случай.
NotifyComplete
не должно быть в if
как в вашем коде, хотя это условие всегда выполняется в Windows Phone. ShellTile.ActiveTiles
всегда есть хотя бы одна плитка - основная плитка - даже если она не закреплена.
Отладка фоновых агентов
Да, вы можете сделать это. Если агент не работает, лучший способ выяснить, в чем проблема, - запустить приложение с подключенным отладчиком, форсировать вызов фонового агента и поместить точку останова в код, который вы хотите проверить. использование ScheduledActionService.LaunchForTest
запустить фоновый агент раньше, чем обычно.
Оффтопик / Вопросы
Могу ли я спросить, что вы пытаетесь достичь с этим private static volatile bool _classInitialized
поле? И почему это volatile
?
Я также видел эту проблему с моим приложением после обновления с WP8.0 до WP8.1 Silverlight. Мой запланированный агент "зависает" абсолютно бесшумно в произвольных точках моего кода (генерируя живые плитки). Сбои или зависания происходят часто, и никаких исключений не выдается. Я работал над этим больше и больше месяца безуспешно. Я был очень отстранен, думая, что я сам как-то вызываю проблему. Я нашел ссылку прошлой ночью, которая может быть решением. Я еще не полностью проверил это в моем приложении.
В заметках об изменениях в WP8.1 Silverlight API есть очень загадочный абзац, который гласит:
Чтобы управляемый агент Windows Phone 8 ScheduledTaskAgent имел доступ к функциям Silverlight 8.1, он работает в современном стеке выполнения, который сходен с Windows. При этом используется другой механизм квот ЦП, чем в Windows Phone 8. В некоторых случаях фоновый агент Silverlight 8.1 может обнаружить, что он получает меньше процессорного времени, чем раньше. Если это произойдет, приложение может выбрать для вызова RequestAccessAsync(). Это увеличит квоту ЦП, данную агенту.
Я нашел некоторые посты в других местах, указывающие, что следующий код исправляет проблему для некоторых, когда помещается непосредственно перед добавлением задачи фонового агента:
await Windows.ApplicationModel.Background.BackgroundExecutionManager.RequestAccessAsync();
Я хотел бы услышать, видят ли другие проблемы такого типа и помогает ли это им.
РЕШИТЬ!.
У меня та же проблема, и я решил с помощью следующего кода:
Deployment.Current.Dispatcher.BeginInvoke (делегат () { RenderText("Тест"); }); Deployment.Current.Dispatcher.BeginInvoke(Delegate() { RenderTextWide("Test"); });
Файл ScheduledAgent.cs:
protected override void OnInvoke(ScheduledTask task)
{
ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault();
if (tile != null)
{
FlipTileData flipTile = new FlipTileData();
flipTile.Title = "";
flipTile.BackTitle = "Atmosphere";
Deployment.Current.Dispatcher.BeginInvoke(delegate() { RenderText("Test"); });
Deployment.Current.Dispatcher.BeginInvoke(delegate() { RenderTextWide("Test"); });
flipTile.BackBackgroundImage = new Uri("/Assets/NewUI/1.PNG", UriKind.Relative); //Default image for Background Image Medium Tile 336x336 px
flipTile.BackgroundImage = new Uri(@"isostore:/Shared/ShellContent/BackBackgroundImage2.png", UriKind.Absolute); //Generated image for Back Background 336x336
flipTile.WideBackBackgroundImage = new Uri("/Assets/NewUI/2.PNG", UriKind.Relative); ////Default image for Background Image Wide Tile 691x336 px
flipTile.WideBackgroundImage = new Uri(@"isostore:/Shared/ShellContent/WideBackBackgroundImage2.png", UriKind.Absolute);
tile.Update(flipTile);
//Tile updated, tell agent operation complete
NotifyComplete();
}
}