非阻塞下载
本文关键字:下载 | 更新日期: 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
,将事件发布到主线程。
您有两个选项:HttpWebRequest
和WebClient
。两个类都在后台下载。唯一的区别是:对于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