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();
        }
    }
Другие вопросы по тегам