简体   繁体   中英

WP8 Background Agent - Code executes fine outside of agent, fails inside?

I have a background agent I created to update my live tile. The agent schedules and executes fine, but the code that the agent executes has became the problem - it simply fails to execute fully and provides no error. From what I can tell, there are no restricted APIs that I am using, unless the Cimbalino toolkit is somehow providing a problem, even though I am using the background-agent-specific version from NuGet.

Code appears to stop executing when RenderText() and RenderTextWide() fail to run. No error is provided. The same code works perfectly fine when run in the foreground app.

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();
            }
        }      
    }
}

I just ran the code that you shared in a background agent with debugger attached, and the problem was quite obvious - it throws an exception! An invalid cross-thread access exception to be precise. All UI-related work must happen int the UI thread and instantiating controls and WriteableBitmaps is such work.

Solution

You need to use Deployment.Current.Dispatcher.BeginInvoke to invoke RenderText and RenderTextWide in the appropriate thread. You can also just invoke everything in OnInvoke in the UI thread.

Note: Deployment.Current.Dispatcher.BeginInvoke invokes the given action asynchronously, so if you don't run everything on the UI thread, you may need to do something to sync the different threads.

NotifyComplete

You must ALWAYS call NotifyComplete AFTER you've done all the work. Basically what you need to do is try-catch(all) and then call NotifyComplete . Also, you may want to put it in ScheduledAgent_UnhandledException just in case.

NotifyComplete shouldn't be in an if as in your code, although that condition is always true in Windows Phone. ShellTile.ActiveTiles always has at least one tile - the primary tile - even if it's not pinned.

Debugging background agents

Yes, you can do that. If the agent is not working, the best way to see what's the problem is to launch the app with the debugger attached, force the background agent's invocation and put a breakpoint in the code that you want to check. Use ScheduledActionService.LaunchForTest to start the background agent sooner than normal.

Offtopic/Questions

May I ask what are you trying to achieve with that private static volatile bool _classInitialized field? And why is it volatile ?

I also have seen this problem with my application after upgrading from WP8.0 to WP8.1 Silverlight. My scheduled agent "crashes" absolutely silently at arbitrary points in my code (generating live tiles). The crashes or hangs occur frequently and no exceptions are thrown. I've been working on this on and off for over a month now with no success. I've been very side-tracked thinking I was causing the problem myself somehow. I did find a reference last night that may be the solution. I haven't fully tested it yet in my application.

In the WP8.1 Silverlight API change notes there is a very cryptic paragraph that says:

For a managed Windows Phone 8 ScheduledTaskAgent to be able to access Silverlight 8.1 features, it runs on the modern execution stack, which is converged with Windows. This uses a different CPU quota mechanism than Windows Phone 8. In some cases, a Silverlight 8.1 background agent might find that it gets less CPU time than it did previously. If that happens, the app may choose to call RequestAccessAsync(). This will increase the CPU quota given to the agent.

I've found some posts elsewhere indicating that the following code fixes the problem for some when placed immediately before adding a background agent task:

await Windows.ApplicationModel.Background.BackgroundExecutionManager.RequestAccessAsync();

I would love to hear if others are seeing these types of problems and if this helps them.

SOLVED!.

I have the same problem and I solved with the following code:

Deployment.Current.Dispatcher.BeginInvoke(delegate() { RenderText("Test"); }); Deployment.Current.Dispatcher.BeginInvoke(delegate() { RenderTextWide("Test"); });

The file 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();
        }
    }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM