非阻塞下载
我是 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为
WebClient
方法部分阻塞。第一部分(包括 DNS 查找)会被阻止,但下载本身不会被阻止。请参阅 C# 异步方法仍然挂起 UI
我个人将其称为.net API 中的错误(或者更糟糕:设计损坏)
作为解决方法,您可以在单独的线程中开始下载。我建议为此使用任务 API。
这不是最优的,因为它在执行 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.
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.您有两个选择:
HttpWebRequest
和WebClient
。两个类都在后台下载。唯一的区别:使用WebClient
时,方法twitter_DownloadStringCompleted
将在 UI 线程中调用,因此数据解析将阻塞 UI。如果您使用 HttpWebRequest 该方法将在另一个线程中调用,但要设置数据以进行数据绑定或直接设置到控件,您必须使用类似以下内容:
(2) 中的代码将在 UI 线程中调用。在
StartTwitterUpdate
中将进度条设置为可见,并在 (2) 中将进度条设置为不可见。查看此类以简化 http 调用(POST、FILES、GZIP 等):
http://mytoolkit .codeplex.com/wikipage?title=Http
You have two options:
HttpWebRequest
andWebClient
. Both classes are downloading in the background. Only difference: WithWebClient
the methodtwitter_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:
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