2

I'm about to loose it soon... I have two apps with working background tasks that are updating the live tile. The data for the live tile is downloaded, parsed and then an image is created dynamically and used as the background for the live tile.

Everything is working just fine for a day or two, but then the updating starts behaving very strange. The first one-two days both live tiles for my apps are updating every 28 minutes like clockwork. But then they start skipping updates. Often app A then updates when app B doesn't update the live tile so that they are not updating at the same time and only once an hour. To put it simple they are way off schedule.

This is really frustrating since I need to be able to rely on the tiles beeing updated every 30 minutes (if I have enough battery, good reception and so on).

I would really appreciate if someone could help me out and maybe take a look at my code to see if there might be something messing up the update interval (like not calling NotifyComplete correctly). I have removed some code and and tried to simplify it. Please ask if you need anything else to understand this.

I have been trying to fix this for the last two months, trying different phones and going throughmy code very carefully.

Your help is more appreciated than you can ever know. Thanks in advance.

My OnInvoke function:

        Timer t = null;

        ShellToast toast = new ShellToast();


        try
        {
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(new Uri(strUrlHBH));
            request.Accept = "*/*";
            request.AllowAutoRedirect = true;

            // disable caching.
            request.Headers["Cache-Control"] = "no-cache";
            request.Headers["Pragma"] = "no-cache";

            t = new Timer(
                state =>
                {
                    if (string.Compare(state.ToString(), id, StringComparison.InvariantCultureIgnoreCase) == 0)
                    {
                        //logger.Write("Timeout reached for connection [{0}], aborting download.", id);
                        runNotifyComplete = false;
                        NotifyComplete();
                        request.Abort();
                        t.Dispose();
                    }

                },
                id,
                timeout,
                0);

            request.BeginGetResponse(
                r =>
                {
                    try
                    {
                        if (t != null)
                        {
                            t.Dispose();
                        }

                        var httpRequest = (HttpWebRequest)r.AsyncState;
                        var httpResponse = (HttpWebResponse)httpRequest.EndGetResponse(r);
                        using (var reader = new StreamReader(httpResponse.GetResponseStream()))
                        {

                            var response = reader.ReadToEnd();

                            Deployment.Current.Dispatcher.BeginInvoke(new Action(() =>
                            {
                                try
                                {

                                    //Parse the result

                                    if (boolResult) //If we have a result
                                    {


                                        Grid grid = new Grid();



                                        StackPanel sp = new StackPanel();
                                        sp.Height = 173;
                                        sp.Width = 173;



                                        //StreamResourceInfo info = Application.GetResourceStream(new Uri("Pin-to-start2.png", UriKind.Relative));


                                        if ((bool)settings["LiveTileMetro"] == true)
                                        {

                                            // create source bitmap for Image control (image is assumed to be alread 173x173)
                                            /*WriteableBitmap wbmp2 = new WriteableBitmap(1, 1);
                                            wbmp2.SetSource(info.Stream);
                                            Image img = new Image();
                                            img.Source = wbmp2;
                                            // add Image to Grid
                                            grid.Children.Add(img);
                                            strBackBackground = "Pin-to-start2.png";
                                        }
                                        else
                                        {*/
                                            sp.Background = (SolidColorBrush)Application.Current.Resources["PhoneAccentBrush"];
                                            //sp.Background.Opacity = 0.0;
                                            strBackBackground = "";
                                        }

                                        StreamResourceInfo info;

                                        //GC.Collect();
                                        info = Application.GetResourceStream(new Uri("/MyApp;component/images/Icons/livetile/livetile.png", UriKind.Relative));


                                        WriteableBitmap wbmp3 = new WriteableBitmap(1, 1);
                                        try
                                        {
                                            wbmp3.SetSource(info.Stream);
                                        }
                                        catch
                                        {
                                        }
                                        Image img3 = new Image();
                                        img3.Source = wbmp3;
                                        // add Image to Grid
                                        img3.Width = 173;
                                        img3.Height = 173;
                                        img3.Margin = new Thickness { Left = 0, Bottom = 0, Right = 0, Top = 0 };


                                        TextBlock txtTemperature = new TextBlock();
                                        TextBlock txtTemperatureRing = new TextBlock();

                                        txtTemperature.Foreground = new SolidColorBrush(Colors.White);
                                        txtTemperature.Text = strTemp;
                                        txtTemperature.TextAlignment = TextAlignment.Right;
                                        txtTemperatureRing.Style = (Style)Application.Current.Resources["PhoneTextTitle3Style"];
                                        txtTemperatureRing.FontFamily = new FontFamily("Segoe WP Light");
                                        txtTemperatureRing.FontSize = 40;
                                        txtTemperatureRing.Foreground = new SolidColorBrush(Colors.White);
                                        txtTemperatureRing.Text = "°";
                                        txtTemperatureRing.TextAlignment = TextAlignment.Right;

                                            txtTemperature.FontFamily = new FontFamily("Segoe WP Light");
                                            txtTemperature.FontSize = 60;
                                            txtTemperature.Margin = new Thickness { Left = 0, Bottom = 0, Right = 0, Top = -75 };
                                            txtTemperature.Height = 80;
                                            txtTemperature.Width = 145;
                                            txtTemperatureRing.Margin = new Thickness { Left = 128, Bottom = 0, Right = 0, Top = -97 };
                                            txtTemperatureRing.Height = 50;
                                            txtTemperatureRing.Width = 39;

                                        sp.Children.Add(img3);
                                        sp.Children.Add(txtTemperature);
                                        sp.Children.Add(txtTemperatureRing);


                                        //call measure, arrange and updatelayout to prepare for rendering
                                        sp.Measure(new Size(173, 173));
                                        sp.Arrange(new Rect(0, 0, 173, 173));
                                        sp.UpdateLayout();
                                        grid.Children.Add(sp);


                                        WriteableBitmap wbmp = new WriteableBitmap(173, 173);
                                        wbmp.Render(grid, null);
                                        wbmp.Invalidate();


                                        //write image to isolated storage
                                        string sIsoStorePath = @"\Shared\ShellContent\tile.png";
                                        using (IsolatedStorageFile appStorage = IsolatedStorageFile.GetUserStoreForApplication())
                                        {
                                            //ensure directory exists
                                            String sDirectory = System.IO.Path.GetDirectoryName(sIsoStorePath);
                                            if (!appStorage.DirectoryExists(sDirectory))
                                            {
                                                appStorage.CreateDirectory(sDirectory);
                                            }

                                            using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(sIsoStorePath, System.IO.FileMode.Create, appStorage))
                                            {
                                                wbmp.SaveJpeg(stream, 173, 173, 0, 100);
                                            }
                                        }


                                        /// If application uses both PeriodicTask and ResourceIntensiveTask


                                            //ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("TileID=2"));
                                            ShellTile TileToFind = ShellTile.ActiveTiles.First();

                                            //test if Tile was created
                                            if (TileToFind != null)
                                            {
                                                StandardTileData NewTileData = new StandardTileData
                                                {
                                                    BackgroundImage = new Uri("isostore:Shared/ShellContent/tile.png", UriKind.Absolute),
                                                    Title = strTitle,
                                                    Count = null,
                                                    BackTitle = (string)settings["SelectedCityName"],
                                                    BackBackgroundImage = new Uri(strBackBackground, UriKind.Relative),
                                                    BackContent = strWind + Environment.NewLine + strPrecipitation
                                                };

                                                //ShellTile.Create(new Uri("/MainPage.xaml?TileID=2", UriKind.Relative), NewTileData);
                                                TileToFind.Update(NewTileData);

                                            }
                                        }



                                        if (runNotifyComplete == true)
                                        {
                                            runNotifyComplete = false;
                                            NotifyComplete();
                                        }
                                    }//If matches.count
                                }
                                catch
                                {
                                    if (runNotifyComplete == true)
                                    {
                                        runNotifyComplete = false;
                                        NotifyComplete();
                                    }
                                }
                                finally
                                {
                                    if (runNotifyComplete == true)
                                    {
                                        runNotifyComplete = false;
                                        NotifyComplete();
                                    }
                                }
                            }));
                        }



                        if (runNotifyComplete == true)
                        {
                            runNotifyComplete = false;
                            NotifyComplete();
                        }

                    }
                    catch
                    {


                        // error handling.
                        if (runNotifyComplete == true)
                        {

                            runNotifyComplete = false;
                            NotifyComplete();
                        }
                    }
                    finally
                    {
                    }
                },
                request);
        }
        catch
        {
            if (runNotifyComplete == true)
            {
                runNotifyComplete = false;
                NotifyComplete();
            }
        }
        finally
        {
        }

EDIT 1: Here is the code for initializing the background task MessageBox.Show(MyResources.LiveTileToggleMsgBoxText, "Live tile", MessageBoxButton.OK);

              //start background agent 
            PeriodicTask periodicTask = new PeriodicTask("AppPeriodicAgent0440");

            periodicTask.Description = "Periodic task for APP that updates the LiveTile .";
            periodicTask.ExpirationTime = System.DateTime.Now.AddDays(14);

            // If the agent is already registered with the system,
            if (ScheduledActionService.Find(periodicTask.Name) != null)
            {
                ScheduledActionService.Remove("AppPeriodicAgent0440");
            }

            try
            {
                //only can be called when application is running in foreground
                ScheduledActionService.Add(periodicTask);
            }
            catch
            {
            }

EDIT 2: This code is run everytime I exit the application in order to keep the background task alive. I put it in OnNavigatedFrom to avoid slowing down the start up of the app

//start background agent 
                        PeriodicTask periodicTask = new PeriodicTask("AppPeriodicAgent0440");

                        periodicTask.Description = "Periodic task for APP that updates the LiveTile.";
                        periodicTask.ExpirationTime = System.DateTime.Now.AddDays(14);

                        // If the agent is already registered with the system,
                        if (ScheduledActionService.Find(periodicTask.Name) != null)
                        {
                            ScheduledActionService.Remove("AppPeriodicAgent0440");
                        }

                        //only can be called when application is running in foreground
                        ScheduledActionService.Add(periodicTask);
John
  • 681
  • 1
  • 8
  • 20
  • The first step I would take is try to actually log any errors, rather than just blank catches (or you might have just taken it out for this example). Either way are they any exceptions raised or the timing just starts going off or skipping? – Adam Dec 17 '11 at 15:09
  • It's probably more useful to see how you're setting the parameters of your PeriodicTask – Peter Ritchie Dec 17 '11 at 15:10
  • Adam > So I should put a MessageBox printing out the exception instead of just ignoring it? – John Dec 17 '11 at 15:32
  • Peter > I added the code, I hope that the code I added is what you were asking for? – John Dec 17 '11 at 15:33
  • @John use the @ next to a person's username so they get notified. – William Melani Dec 19 '11 at 22:48

2 Answers2

1

If you read the Background Agents overview http://msdn.microsoft.com/en-us/library/hh202942(v=vs.92).aspx you'll see that it details several scenarios where you schedule won't be followed. For example:

Battery Saver mode is an option that the user can enable on the device to indicate that battery life should be prioritized. If this mode is enabled, periodic agents may not run, even if the interval has elapsed

Peter Ritchie
  • 35,463
  • 9
  • 80
  • 98
  • Okay, but that should only affect the interval and executing if the battery is running low, right? My problem occurs even if the battery is full. – John Dec 17 '11 at 15:25
  • You've had a full charge for more than two days? Try turning off battery saver mode and see if your task runs more periodically... – Peter Ritchie Dec 17 '11 at 15:38
  • No, my phone is not that good :) But I usually keep it connected to the power since I have it next to me at my desk. But I'll give it a try to see if this might be one problem. But I have also noted that if I have very poor reception (barely none) then it takes several hours before the tile starts updating after reaching an area with good reception. Sometimes it takes overnight before it's back on schedule. – John Dec 17 '11 at 15:41
  • Yes, I noticed all your code uses NotifyComplete, so in the event of poor reception the scheduler *shouldn't* be rescheduling your tasks... I have seem Windows Phones that take a *long* time to get back on the Internet--sometimes having to "reboot" them... – Peter Ritchie Dec 17 '11 at 15:57
  • Okay, but if I'm in a place with poor reception for a few hours and the http request times out I still want to call NotifyComplete. Otherwise the background agent will be disabled after two timeouts. – John Dec 17 '11 at 16:01
  • Rurned off battery save mode without any result. App A updated every 60 minutes and app B every 90 minutes. Restarted the phone and now the update interval is back to every 28 minutes. I have noticed this before and in a day or two the update interval will be out of sync. This is so frustrating. – John Dec 17 '11 at 21:38
  • Yep, my comment about NotifyComplete was about http timeouts not being your issue (implied :). I think you're just being struck by there being no guarantee you're tasks will be executed exactly at the time you're requesting. – Peter Ritchie Dec 19 '11 at 16:46
0

There is no guarantee that the scheduled task will run on the expected schedule. It even has a +/- 10 minute clause in the execution timeschedule.

From your question there is no information about how you're recording other events/tasks that may be happenening on the device at the time you're expecting a schedule to run.

In that you have no logging of errors, timeouts or checking of the LastExitReason of the task there could be all sorts of reasons for the behaviour you're seeing.

If I had to guess where the issue is, based on the information you have provided, I'd presume the problem is with the web request (either the network or the server) and an error in the request causes the UI to not be updated.

There is no way to guarantee that the tile will update on a predictable schedule. If you must absolutely have this updated for your circumstances/requirements then this platform isn't going to be able to provide that guarantee.

If you need multiple tiles to be kept in sync I'd approach this by having all the tiles updated by the same process.
This way, even if there is an inconsistency in the updating of the tiles from a time perspective they'll be in sync with each other.

Matt Lacey
  • 65,560
  • 11
  • 91
  • 143
  • I know that there is no guarantee but my problem is that it after 1-2 days never runs every 28 minute. I have put a toast notification in the timeout part of my httpwebrequest (not shown in my code above) but this one never fires when the tile doesn't update. So there must be something else that is failing there that is not a timeout. – John Dec 21 '11 at 08:34