非阻塞下载

发布于 2024-12-14 00:23:18 字数 4064 浏览 0 评论 0原文

我是 Windows Phone 7 开发新手,如果您愿意的话,在查找如何在“后台”下载某些数据时遇到了一些麻烦。我知道这是可能的,因为 ESPN 等应用程序会显示“正在加载......”。下载数据时,UI 仍然完全响应。我想做的是下载一些 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,如<建议的a href="https://stackoverflow.com/questions/8029642/non-blocking-download/8044391#8044391">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
                                     };
      });
    }
}

I'm new to Windows Phone 7 development, and am having a bit of trouble finding out how to download some data in the 'background', if you will. I know it is possible, because apps like ESPN, etc, display a "Loading ... .. ." while downloading their data, and the UI is still completely responsive. What I'm trying to do is download some Twitter data.

Here is what I have now, but it is blocking 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: Attempt at multithreading:

// 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: Using HttpWebRequest, as suggested by Rico Suter, and with the help of this blog post, I think I've done it:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

牵你手 2024-12-21 00:23:18

我认为 WebClient 方法部分阻塞。第一部分(包括 DNS 查找)会被阻止,但下载本身不会被阻止。

请参阅 C# 异步方法仍然挂起 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 将事件发布到主线程。

I think the WebClient methods are partially blocking. The first part including DNS lookup is blocking, but the download itself is not.

See C# async methods still hang UI

Personally I'd call this a bug in the .net API (or even worse: broken by design)

As a workaround you can start the download in a separate thread. I recommend using the tasks API for that.

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

Not optimal, since it occupies a thread while performing the DNS lookup, but should be acceptable in practice.


I think another problem with your code is that the callback will not happen on the main thread, but on a threadpool thread. You need to use a SynchronizationContext that posts the event to the main thread.

挽袖吟 2024-12-21 00:23:18

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

You have two options: HttpWebRequest and WebClient. Both classes are downloading in the background. Only difference: With WebClient the method twitter_DownloadStringCompleted will be called in UI thread so the parsing of the data will block the UI.

If you use HttpWebRequest the method will be called in another thread but to set the data for data binding or directly to a control you have to use something like this:

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

The code in (2) will be called in UI thread. Set your progressbar to visible in StartTwitterUpdate and set your progress bar to invisible in (2).

Check out this classes to simplify http calls (POST, FILES, GZIP, etc):

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文