【问题标题】:Non Blocking Download非阻塞下载
【发布时间】:2011-11-08 09:28:29
【问题描述】:

我是 Windows Phone 7 开发的新手,如果你愿意的话,我在寻找如何在“后台”下载一些数据时遇到了一些麻烦。我知道这是可能的,因为 ESPN 等应用程序会显示“正在加载……”。在下载他们的数据时,用户界面仍然是完全响应的。我正在尝试下载一些 Twitter 数据。

这是我现在拥有的,但它阻止了 atm:

// Constructor:

// load the twitter data
WebClient twitter = new WebClient();

twitter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(twitter_DownloadStringCompleted);
twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));

// Callback function:

void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
  if (e.Error != null)
  {
    return;
  }

  XElement xmlTweets = XElement.Parse(e.Result);

  TwitterListBox.ItemsSource = from tweet in xmlTweets.Descendants("status")
                               select new TwitterItem
                               {
                                 ImageSource = tweet.Element("user").Element("profile_image_url").Value,
                                 Message = tweet.Element("text").Value,
                                 UserName = tweet.Element("user").Element("screen_name").Value
                               };

}

编辑:尝试多线程:

// in constructor
Dispatcher.BeginInvoke(new ThreadStart(StartTwitterUpdate));

// other functions
private void StartTwitterUpdate()
{
  // load the twitter data
  WebClient twitter = new WebClient();

  twitter.DownloadStringCompleted += new DownloadStringCompletedEventHandler(twitter_DownloadStringCompleted);
  twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));
}

void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
  if (e.Error != null)
  {
    return;
  }

  XElement xmlTweets = XElement.Parse(e.Result);

  TwitterListBox.ItemsSource = from tweet in xmlTweets.Descendants("status")
                               select new TwitterItem
                               {
                                 ImageSource = tweet.Element("user").Element("profile_image_url").Value,
                                 Message = tweet.Element("text").Value,
                                 UserName = tweet.Element("user").Element("screen_name").Value
                               };

}

编辑 2: 使用 HttpWebRequest,按照 Rico Suter 的建议,并在 this 博客文章的帮助下,我想我已经做到了:

// constructor
StartTwitterUpdate();


private void StartTwitterUpdate()
{
  HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));

  request.BeginGetResponse(new AsyncCallback(twitter_DownloadStringCompleted), request);
}

void twitter_DownloadStringCompleted(IAsyncResult asynchronousResult)
{
  HttpWebRequest request = (HttpWebRequest)asynchronousResult.AsyncState;

  HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(asynchronousResult);

  using (StreamReader streamReader1 = 
    new StreamReader(response.GetResponseStream()))
    {
      string resultString = streamReader1.ReadToEnd();

      XElement xmlTweets = XElement.Parse(resultString);

      Deployment.Current.Dispatcher.BeginInvoke(() =>
      {
        TwitterListBox.ItemsSource = from tweet in xmlTweets.Descendants("status")
                                     select new TwitterItem
                                     {
                                       ImageSource = tweet.Element("user").Element("profile_image_url").Value,
                                       Message = tweet.Element("text").Value,
                                       UserName = "@" + tweet.Element("user").Element("screen_name").Value
                                     };
      });
    }
}

【问题讨论】:

  • 您的代码没有被阻塞。 (这就是Async 的意思)
  • @SLaks:我知道这就是异步的意思,我知道它/不应该/阻塞,但确实如此。我的应用程序的其他部分是否可能被阻止?也许列表的实际绘图?它被设置为(我认为)绑定?我总共只做了大约 4 个小时的 MS 编程,所以我希望我不是在问一些非常明显的问题。
  • 它不应该阻塞。你的回调函数不是需要很多时间吗?如果您有大量数据要汇集。

标签: c# windows-phone-7 webclient


【解决方案1】:

我认为WebClient 方法是部分阻塞的。包括 DNS 查找的第一部分被阻止,但下载本身没有。

C# async methods still hang UI

我个人认为这是 .net API 中的错误(或者更糟糕的是:被设计破坏)

作为一种解决方法,您可以在单独的线程中开始下载。我建议为此使用任务 API。

Task.Factory.StartNew(
  ()=>
  {
      twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));
  }
);

不是最优的,因为它在执行 DNS 查找时占用了一个线程,但在实践中应该是可以接受的。


我认为您的代码的另一个问题是回调不会发生在主线程上,而是发生在线程池线程上。您需要使用 SynchronizationContext 将事件发布到主线程。

【讨论】:

  • 这听起来确实像我正在处理的问题,但我不确定如何根据该线程中给出的响应来解决它。
  • 我已经更新了我的问题,但它似乎不起作用。我可能误解了如何开始一个线程。
  • 值得指出的是,the Silverlight/WP7 version of the BeginGetResponse docs 不包含您在其他帖子中提到的警告。
  • @CodeInChaos 我似乎无法让我的项目找到Task/Tasks。它可能不包含在 WP7 库中吗?在using System.Threading. 之后,自动完成不会在 VS2010 中完成,就像它不知道这些类一样。
  • 如果有人尝试使用 WebClient.DownloadStringTaskAsync 而不是使用 .NET 4.5 的原始方法,那就不用麻烦了,它会阻止同样的(WebClient.DownloadFileTaskAsync,它也是小弟) .
【解决方案2】:

您有两个选择:HttpWebRequestWebClient。两个类都在后台下载。唯一的区别:WebClient 方法 twitter_DownloadStringCompleted 将在 UI 线程中调用,因此数据解析将阻塞 UI。

如果您使用HttpWebRequest,该方法将在另一个线程中调用,但要设置数据以进行数据绑定或直接设置为控件,您必须使用以下内容:

void twitter_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
{
    // do your parsing with e.Result...
    Deployment.Current.Dispatcher.BeginInvoke(() => {
        // set your data (2) to UI
    });
}

(2) 中的代码将在 UI 线程中调用。在StartTwitterUpdate 中将您的进度条设置为可见,并在 (2) 中将您的进度条设置为不可见。

查看这些类以简化 http 调用(POST、FILES、GZIP 等):

http://mytoolkit.codeplex.com/wikipage?title=Http

【讨论】:

    猜你喜欢
    • 2016-07-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多