• 在ScheduledTaskAgent中使用HttpWebRequest


    ScheduledTaskAgent是WP7的后台代理,可以利用它在后台执行某些操作,比如更新Live Tile。可以用推送通知来更新Live Tile,但某些实时性要求不高的任务可以用后台代理来做。

    但后台代理有诸多限制,比如某些API就不能使用。http://msdn.microsoft.com/zh-cn/library/hh202962(v=vs.92).aspx 这里有后台代理不支持的API列表,比如摄像头这些设备就无法在后台代理中使用。需要注意的API:


    GeoCoordinateWatcher

    此 API 用于获取设备的地理坐标,支持在后台代理中使用,但它使用缓存的位置值而不是实时数据。设备每 15 分钟更新缓存的位置值。

    Mutex 类

    应该使用 Mutex 类同步对在前台应用程序和后台代理之间共享的资源(如独立存储中的文件)的访问。

    ShellToast 类

    该类可以用于从正在运行的后台代理弹出 Toast 通知。

    ShellTile 类的 Update(ShellTileData) 方法

    ShellTile 类的 Delete()()()() 方法

    ShellTile 类的 ActiveTiles 属性。

    这些方法可以用于修改正在运行的后台代理中的 shell 磁贴。请注意,不能在后台代理中创建 shell 磁贴。

    HttpWebRequest 类

    该类允许您从正在运行的后台代理进行 Web 请求。

     
    有HttpWebRequest类就可以实现目的了,从网络请求数据,用ShellTile的Update()方法更新Live Tile即可。在使用中也发现了一些问题,记录下来。
     
    首先要实现Live Tile。http://msdn.microsoft.com/zh-cn/library/hh202979(v=vs.92).aspx 此页面演示了如何创建、修改Live Tile。我的目的是创建显示货币汇率的Live Tile,代码如下:
     1 /// <summary>
    2 /// Handles the Click event of the menuPinToStart control.固定到开始屏幕
    3 /// </summary>
    4 /// <param name="sender">The source of the event.</param>
    5 /// <param name="e">The <see cref="System.Windows.RoutedEventArgs"/> instance containing the event data.</param>
    6 private void menuPinToStart_Click(object sender, RoutedEventArgs e)
    7 {
    8 MenuItem menuItem = sender as MenuItem;
    9 CurrEntity currEntity = menuItem.DataContext as CurrEntity;
    10
    11 if (currEntity.CurrBase.Code != currEntity.CurrTarget.Code)
    12 {
    13 StringBuilder sb = new StringBuilder();
    14 sb.AppendFormat(CultureInfo.InvariantCulture, paratemplate, currEntity.CurrBase.Code, currEntity.CurrTarget.Code);
    15
    16 // Look to see whether the Tile already exists and if so, don't try to create it again.
    17 ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("MainPage.xaml?" + sb.ToString()));
    18
    19 // Create the Tile if we didn't find that it already exists.
    20 if (TileToFind == null)
    21 {
    22 // Create the Tile object and set some initial properties for the Tile.
    23 // The Count value of 12 shows the number 12 on the front of the Tile. Valid values are 1-99.
    24 // A Count value of 0 indicates that the Count should not be displayed.
    25 StandardTileData NewTileData = new StandardTileData
    26 {
    27 BackgroundImage = new Uri("TileBackground.png", UriKind.Relative),
    28 Title = "1" + currEntity.CurrBase.Code + "=" + currEntity.Rate.ToString() + currEntity.CurrTarget.Code,
    29 Count = 0,
    30 BackTitle = currEntity.TradeDate.ToString(),
    31 BackContent = "1" + currEntity.CurrBase.Code + " = " + currEntity.Rate.ToString() + currEntity.CurrTarget.Code,
    32 //BackBackgroundImage = new Uri("Blue.jpg", UriKind.Relative)
    33 };
    34 // Create the Tile and pin it to Start. This will cause a navigation to Start and a deactivation of our application.
    35 ShellTile.Create(new Uri("/MainPage.xaml?" + sb.ToString(), UriKind.Relative), NewTileData);
    36 }
    37 }
    38 }
    为了区别每种货币,将货币代码附在ShellTile的NavigationUri属性里,这样可以在后台代理中取出相应的货币。
     
    然后创建后台代理。新建一个后台代理项目。http://msdn.microsoft.com/zh-cn/library/hh202941(v=vs.92).aspx 这个页面演示了如何使用后台代理弹出Toast通知,为了更新Live Tile,需要改动一下。关键代码如下:
     1 protected override void OnInvoke(ScheduledTask task)
    2 {
    3 //TODO: Add code to perform your task in background
    4 string toastMessage = "";
    5
    6 // If your application uses both PeriodicTask and ResourceIntensiveTask
    7 // you can branch your application code here. Otherwise, you don't need to.
    8 if (task is PeriodicTask)
    9 {
    10 // Execute periodic task actions here.
    11 toastMessage = "Periodic task running.";
    12 }
    13 else
    14 {
    15 // Execute resource-intensive task actions here.
    16 toastMessage = "Resource-intensive task running.";
    17 }
    18
    19 // Launch a toast to show that the agent is running.
    20 // The toast will not be shown if the foreground application is running.
    21 ShellToast toast = new ShellToast();
    22 toast.Title = "Background Agent Sample";
    23 toast.Content = toastMessage;
    24 toast.Show();
    25
    26 // If debugging is enabled, launch the agent again in one minute.
    27 #if DEBUG_AGENT
    28 ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
    29 #endif
    30
    31 // Call NotifyComplete to let the system know the agent is done working.
    32 NotifyComplete();
    33 }

    把其中的代码换成更新Live Tile的代码即可。首先要异步请求数据,然后更新Live Tile。异步请求的代码如下:

      1 /// <summary>
    2 /// State information for our BeginGetResponse async call
    3 /// </summary>
    4 public class RequestState
    5 {
    6 public HttpWebRequest AsyncRequest { get; set; }
    7 public HttpWebResponse AsyncResponse { get; set; }
    8 }
    9
    10
    11 private void ConvertCurrencyByHttpWebRequest()
    12 {
    13 //先判断网络是否可用
    14 bool networkIsAvailable = Microsoft.Phone.Net.NetworkInformation.NetworkInterface.GetIsNetworkAvailable();//当前网络是否可用
    15 if (networkIsAvailable)
    16 {
    17 //把当前要转换的货币转换成uri
    18 StringBuilder sb = new StringBuilder();
    19
    20 foreach (CurrEntity data in this.currEntityList)
    21 {
    22 try
    23 {
    24 //do something
    37 }
    38 catch
    39 {
    40 }
    41 }
    42
    43 string url = String.Format(CultureInfo.InvariantCulture, currUrl, sb.ToString());
    44 Uri uri = new Uri(url);
    45
    46 //开始异步调用
    47 HttpWebRequest myRequest = (HttpWebRequest)WebRequest.Create(uri);
    48 //设置异步请求状态
    49 RequestState requestState = new RequestState();
    50 requestState.AsyncRequest = myRequest;
    51 //开始对 Internet 资源的异步请求。
    52 //IAsyncResult result = (IAsyncResult)req.BeginGetResponse(new AsyncCallback(ResponseCallback), req);//返回异步操作的状态
    53 myRequest.BeginGetResponse(new AsyncCallback(ResponseCallback), requestState);
    54
    55
    56 }
    57 }
    58
    59
    60
    61 private void ResponseCallback(IAsyncResult asyncResult)
    62 {
    63 RequestState requestState = (RequestState)asyncResult.AsyncState;
    64 //获取异步操作返回的的信息
    65 HttpWebRequest myRequest = (HttpWebRequest)requestState.AsyncRequest;
    66 //结束对 Internet 资源的异步请求
    67 requestState.AsyncResponse = (HttpWebResponse)myRequest.EndGetResponse(asyncResult);
    68 //WebResponse response = request.EndGetResponse(result);
    69
    70 using (Stream stream = requestState.AsyncResponse.GetResponseStream())
    71 using (StreamReader respReader = new StreamReader(stream))
    72 {
    73 try
    74 {
    75 //do something
    128 //更新Live Tile
    129 UpdateTile();
    130 }
    131 finally
    132 {
    133 respReader.Close();
    134 }
    135
    136 }
    137
    138 }

    更新Live Tile:

     1 /// <summary>
    2 /// Updates the tile.更新LiveTile
    3 /// </summary>
    4 private void UpdateTile()
    5 {
    6 //throw new NotImplementedException();
    7 foreach (CurrEntity currEntity in currEntityList)
    8 {
    9 StringBuilder sb = new StringBuilder();
    10 sb.AppendFormat(CultureInfo.InvariantCulture, paratemplate, currEntity.CurrBase.Code, currEntity.CurrTarget.Code);
    11 //找出对应的Tile
    12 ShellTile TileToFind = ShellTile.ActiveTiles.FirstOrDefault(x => x.NavigationUri.ToString().Contains("MainPage.xaml?" + sb.ToString()));
    13 if (TileToFind != null)
    14 {
    15 StandardTileData NewTileData = new StandardTileData
    16 {
    17 BackgroundImage = new Uri("TileBackground.png", UriKind.Relative),
    18 Title = "1" + currEntity.CurrBase.Code + "=" + currEntity.Rate.ToString() + currEntity.CurrTarget.Code,
    19 Count = 0,
    20 BackTitle = currEntity.TradeDate.ToString(),
    21 BackContent = "1" + currEntity.CurrBase.Code + " = " + currEntity.Rate.ToString() + currEntity.CurrTarget.Code,
    22 //BackBackgroundImage = new Uri("Blue.jpg", UriKind.Relative)
    23 };
    24 TileToFind.Update(NewTileData);
    25
    26 }
    27 }
    28 //ShellToast toast = new ShellToast();
    29 //toast.Title = "test";
    30 //toast.Content = "update success!";
    31 //toast.Show();
    32 }

    将后台代理的代码改为:

     1 protected override void OnInvoke(ScheduledTask task)
    2 {
    3 //TODO: Add code to perform your task in background
    4 //string toastTitle = "Periodic";
    5 this.AdjustToLocalTime = true;
    6 this.currEntityList = new List<CurrEntity>();
    7 // If your application uses both PeriodicTask and ResourceIntensiveTask
    8 // you can branch your application code here. Otherwise, you don't need to.
    9 //if (task is PeriodicTask)
    10 //{
    11 //// Execute periodic task actions here.
    12 // toastTitle = "Periodic ";
    13 //}
    14 //else
    15 //{
    16 //// Execute resource-intensive task actions here.
    17 // toastTitle = "Resource-intensive ";
    18 //}
    19
    20
    21 var query = from x in ShellTile.ActiveTiles where x.NavigationUri.ToString().Contains("?s=") select x;
    22 List<ShellTile> shellTileList = query.ToList();
    23 if (shellTileList.Count > 0)
    24 {
    25 //foreach (ShellTile tile in query.ToList())
    26 for (int i = 0; i < shellTileList.Count; i++)
    27 {
    28 ShellTile tile = shellTileList[i] as ShellTile;
    29 string strCode = tile.NavigationUri.ToString().Substring(tile.NavigationUri.ToString().IndexOf("=") + 1, 6);
    30 CurrEntity currEntity = new CurrEntity();
    31 currEntity.CurrBase = new CurrDescEntity(strCode.Substring(0, 3), "");
    32 currEntity.CurrTarget = new CurrDescEntity(strCode.Substring(3, 3), "");
    33 currEntity.CurrencyAmount = 0;
    34 currEntity.IsInversion = false;
    35 currEntity.IsStandard = false;
    36 currEntity.Rate = 0;
    37 currEntity.TradeDate = System.DateTime.Now;
    38 currEntityList.Add(currEntity);
    39 }
    40 //调用HttpWebRequest更新数据
    41 this.ConvertCurrencyByHttpWebRequest();
    42 }
    43
    44
    45
    46 // If debugging is enabled, launch the agent again in one minute.
    47 #if DEBUG_AGENT
    48 ScheduledActionService.LaunchForTest(task.Name, TimeSpan.FromSeconds(60));
    49 #endif
    50
    51 NotifyComplete();
    52 }

    从Live Tile的NavigationUri中读取参数,并调用HttpWebRequest进行异步请求。

    然后运行一下,奇怪的是,总是无法更新。断点调试,发现异步请求 myRequest.BeginGetResponse(new AsyncCallback(ResponseCallback), requestState); 执行了,但总是无法获取返回数据。好像ResponseCallback根本就没有执行。

    查看后台代理的OnInvoke方法,发现在调用异步请求后,执行了一句 NotifyComplete();

    这个方法意思是:通知操作系统,代理已完成其针对当前代理调用的预定任务。

    也就是说,发起异步请求后,还没有等到异步返回结果,就通知系统代理已经完成任务了,导致ResponseCallback没有执行。

    因此把NotifyComplete()转移到异步返回数据之后即可。

    2012.1.20补充:http://forums.create.msdn.com/forums/p/90294/587823.aspx#587823

    可以写成下面这样:

    Move NotifyComplete() into your anonymous delegate like this:
    1 protected override void OnInvoke(ScheduledTask task)
    2 {
    3 // tried also Create instead of CreateHttp
    4 var request = (HttpWebRequest)HttpWebRequest.CreateHttp(new Uri("http://www.1112.net/lastpage.html"));
    5
    6
    7 // breakpoint here #1
    8 request.BeginGetResponse((ar) =>
    9 {
    10 // breakpoint here #2
    11 try
    12 {
    13 using (StreamReader reader = new StreamReader(request.EndGetRequestStream(ar)))
    14 {
    15 // breakpoint here #3
    16 string result = reader.ReadToEnd();
    17 }
    18 }
    19 catch (Exception)
    20 {
    21 // breakpoint here #4
    22 }
    23 // call it here.
    24 NotifyComplete();
    25 }, null);
    26
    27 // DON'T call this here.
    28 // NotifyComple



  • 相关阅读:
    将指定目录的所有文件及文件夹copy到指定目录下,只copy 7天内创建的或是7天内修改过的
    修复置疑态状态的数据库 重建数据库日志
    修改MYSQL最大连接数的3种方法
    [转载]RRDTool 中文手册简易入门(二)
    [转载]RRDTool 快速学习思维导图
    sql server 2008 新功能创建压缩表和索引
    分析src=http://s.see9.us/s.js>亦或3b3.org注入攻击及解决方案探讨....
    创建login帐号及为其添加安全权限
    计数排序 Counting Sort
    基数排序 Radix sort
  • 原文地址:https://www.cnblogs.com/yanxiaodi/p/2325320.html
Copyright © 2020-2023  润新知