非阻塞下载

本文关键字:下载 | 更新日期: 2023-09-27 18:13:36

我是Windows Phone 7开发的新手,如果你愿意的话,我在寻找如何在"后台"下载一些数据方面遇到了一点麻烦。我知道这是可能的,因为像ESPN等应用程序在下载数据时显示"正在加载... ...",并且UI仍然完全响应。我想做的是下载一些推特数据。

这是我现在拥有的,但是它阻塞了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
                               };
}

EDIT:尝试多线程:

// 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
                               };
}

EDIT 2:使用HttpWebRequest,正如Rico Suter所建议的,在这篇博客文章的帮助下,我想我已经完成了:

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

非阻塞下载

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

参见c# async方法仍然挂起UI

我个人认为这是。net API的一个bug(或者更糟:被设计破坏了)

作为一种解决方法,您可以在单独的线程中启动下载。我建议使用tasks API。
Task.Factory.StartNew(
  ()=>
  {
      twitter.DownloadStringAsync(new Uri("http://api.twitter.com/1/statuses/user_timeline.xml?screen_name=badreligion"));
  }
);

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


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

您有两个选项: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