Sliverlight for Windows Phone 异步的一种解决方案
在 Windows Phone 中异步操作比较常见,我的方法很简单,两句话。这是第二句,它看上去是这样的:
MethodCall.Invoke( () => // 这是一个Func<Object>,返回一个结果 { // 这是一个耗时1秒以上的操作,为了获得一个值,亦或是一个结果集,我写这个类的目的是我把http请求改写成了同步,所以这样会很方便,同样这个类不仅限于http请求。所有耗时、延时操作都可用。 string result = BigFunction(); // List<string> results = BigFunction();
return result; }, (obj) => // 这是一个Action<Object>,处理返回结果 { // 获得了返回结果,这里是线程安全的,内部使用 Dispatcher.BeginInvoke 来调用 MessageBox.Show(obj.ToString()); // List<string> results = (List<string>)obj; });
另附上 MethodCall.cs
using System; using System.Threading; public static class MethodCall { static MethodCall() { OnComplate += new ComplateCallBack((obj) => { System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() => { _callBack.Invoke(obj); }); }); } private static event ComplateCallBack OnComplate; private delegate void ComplateCallBack(object result); private static Action<object> _callBack; public static void Invoke(Func<object> action, Action<object> callback) { _callBack = callback; ThreadStart t = new ThreadStart(() => { OnComplate(action.Invoke()); }); new Thread(t).Start(); } }
用例说明(2011-11-15 11:50 新增):
因为wp7中只有异步请求,如果想封装一个框架实现某些功能,而框架内需要用到http请求,我只能把它写成同步的,使用线程等待的机制实现了一个扩展方法:
public static class WebRequestExt
{
const int DefaultTimeout = 2 * 60 * 1000; // 2 minutes timeout
public static Stream GetRequestStream(this WebRequest request)
{
using (AutoResetEvent done = new AutoResetEvent(false))
{
RequestState state = new RequestState();
state.request = (HttpWebRequest)request;
request.BeginGetRequestStream((ar) =>
{
RequestState rstate = (RequestState)ar.AsyncState;
rstate.streamResponse = rstate.request.EndGetRequestStream(ar);
done.Set();
}, state);
done.WaitOne(DefaultTimeout);
return state.streamResponse;
}
}
public static HttpWebResponse GetResponse(this WebRequest request, ref string errText)
{
using (AutoResetEvent done = new AutoResetEvent(false))
{
RequestState state = new RequestState();
state.request = (HttpWebRequest)request;
request.BeginGetResponse((ar) =>
{
RequestState rstate = (RequestState)ar.AsyncState;
HttpWebRequest rrequest = state.request;
try
{
rstate.response = (HttpWebResponse)request.EndGetResponse(ar);
Stream responseStream = state.response.GetResponseStream();
rstate.streamResponse = responseStream;
}
catch (Exception e)
{
rstate.exception = e;
}
done.Set();
}, state);
done.WaitOne(DefaultTimeout);
if (state.exception != null)
{
errText = state.exception.Message;
}
return state.response;
}
}
}
internal class RequestState
{
// This class stores the State of the request.
public HttpWebRequest request;
public HttpWebResponse response;
public Stream streamResponse;
public Exception exception;
public RequestState()
{
request = null;
streamResponse = null;
}
}
我在使用的时候就可以像这样
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.baidu.com/");
string errText = null;
// 这里直接获得百度首页的html
HttpWebResponse response = request.GetResponse(ref errText);
// 这样的同步方式,而不是使用begin,end模式,再到callback方法中去处理
// IAsyncResult result = request.BeginGetResponse(RequestCallBack, state);
基于这个方法,可以写出同步的http请求,那么我在用的时候每次都去new线程不是很方便,所以封装了这个方法,其实就是省去了一些代码而已,用的时候可以这样:
MethodCall.Invoke(
() =>
{
/*
* 实际上这段代码是在新线程里执行的,比如在Click事件里直接这样写,也不会阻塞线程
*/
HttpWebRequest request = (HttpWebRequest)WebRequest.Create("http://www.baidu.com/");
string errText = null;
// 同步调用GetResponse方法
HttpWebResponse response = request.GetResponse(ref errText);
using (StreamReader sr = new StreamReader(response.GetResponseStream()))
{
// 返回http请求的结果
return sr.ReadToEnd();
}
},
(obj) =>
{
/*
* 这里通过MethodCall在内部执行完上面的方法后调用的,obj参数为上面方法的返回值
*/
// 那么这里就可以获得百度首页的html了
// 同样如果上面返回的是一个List<string>或者其他的对象
// 也可以(List<string>)obj或者(xxx)obj来转换,返回值是你可以决定的
// 当然这也可以改写为一个泛型版,类型转换也不用了
string html = obj.ToString();
MessageBox.Show(html);
});