利用Async CTP改造Live SDK for WP7
最近在开发Windows Phone8应用中用到了Live SDK for WP8,但是在将该应用的WP8版本移植到WP7的时候却发生了许多问题,无法直接将项目进行移植。具体原因是因为Live SDK for WP8利用了Async特性,但是WP7默认是不支持Async特性的,所以微软在Live SDK for WP7中还是采用回调函数的方式,这就给代码的移植造成了很大的困难,当时我在想是否要重写一遍后台代码,虽然这个方案可行,但是以后2套代码进行维护的时候,又极度麻烦,我最后还是决定扩展Live SDK for WP7来实现Async特性。
首先必须下载Async CTP可以参考Windows Phone开发经验谈(16)-使用Async CTP简化异步编程,准备工作做完了,我们就可以开工了
建立类EAPCommon和AsyncExtensions用于对Live SDK原始方法的扩展
using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ExpressWP.Extensions { internal static class EAPCommon { internal static void HandleProgress<T, E>(TaskCompletionSource<T> tcs, ProgressChangedEventArgs eventArgs, Func<E> getProgress, IProgress<E> callback) { if (eventArgs.UserState == tcs) { callback.Report(getProgress.Invoke()); } } internal static void HandleCompletion<T>(TaskCompletionSource<T> tcs, bool requireMatch, AsyncCompletedEventArgs e, Func<T> getResult, Action unregisterHandler) { if (requireMatch) { if (e.UserState != tcs) { return; } } try { unregisterHandler.Invoke(); } finally { if (e.Cancelled) { tcs.TrySetCanceled(); } else { if (e.Error != null) { tcs.TrySetException(e.Error); } else { tcs.TrySetResult(getResult.Invoke()); } } } } } }
using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Text; using System.Threading.Tasks; using Microsoft.Live; using ExpressWP.Util; namespace ExpressWP.Extensions { public static class AsyncExtensions { private static Uri GetUri(this WebClient webClient, string path) { string baseAddress = webClient.BaseAddress; Uri address; if (!string.IsNullOrEmpty(baseAddress)) { if (!Uri.TryCreate(new Uri(baseAddress), path, out address)) { return webClient.GetUri(Path.GetFullPath(path)); } } else { if (!Uri.TryCreate(path, UriKind.Absolute, out address)) { return webClient.GetUri(Path.GetFullPath(path)); } } return webClient.GetUri(address); } private static Uri GetUri(this WebClient webClient, Uri address) { if (address == null) { throw new ArgumentNullException("address"); } Uri result = address; if (!address.IsAbsoluteUri && !string.IsNullOrEmpty(webClient.BaseAddress) && !Uri.TryCreate(new Uri(webClient.BaseAddress), address, out result)) { return address; } return result; } public static Task<Stream> OpenReadTaskAsync(this WebClient webClient, string address) { return webClient.OpenReadTaskAsync(webClient.GetUri(address)); } public static Task<Stream> OpenReadTaskAsync(this WebClient webClient, Uri address) { TaskCompletionSource<Stream> tcs = new TaskCompletionSource<Stream>(address); OpenReadCompletedEventHandler handler = null; handler = delegate(object sender, OpenReadCompletedEventArgs e) { EAPCommon.HandleCompletion<Stream>(tcs, true, e, () => e.Result, delegate { webClient.OpenReadCompleted -= (handler); }); }; webClient.OpenReadCompleted += (handler); try { webClient.OpenReadAsync(address, tcs); } catch { webClient.OpenReadCompleted -= (handler); throw; } return tcs.Task; } public static Task<LoginCompletedEventArgs> LoginTaskAsync(this LiveAuthClient liveAuthClient, IEnumerable<string> scopes) { TaskCompletionSource<LoginCompletedEventArgs> tcs = new TaskCompletionSource<LoginCompletedEventArgs>(scopes); EventHandler<LoginCompletedEventArgs> handler = null; handler = delegate(object sender, LoginCompletedEventArgs e) { EAPCommon.HandleCompletion<LoginCompletedEventArgs>(tcs, true, e, () => e, delegate { liveAuthClient.LoginCompleted -= (handler); }); }; liveAuthClient.LoginCompleted += (handler); try { liveAuthClient.LoginAsync(scopes, tcs); } catch { liveAuthClient.LoginCompleted -= (handler); throw; } return tcs.Task; } public static Task<LiveOperationCompletedEventArgs> GetTaskAsync(this LiveConnectClient liveConnectClient, string path) { TaskCompletionSource<LiveOperationCompletedEventArgs> tcs = new TaskCompletionSource<LiveOperationCompletedEventArgs>(path); EventHandler<LiveOperationCompletedEventArgs> handler = null; handler = delegate(object sender, LiveOperationCompletedEventArgs e) { EAPCommon.HandleCompletion<LiveOperationCompletedEventArgs>(tcs, true, e, () => e, delegate { liveConnectClient.GetCompleted -= (handler); }); }; liveConnectClient.GetCompleted += (handler); try { liveConnectClient.GetAsync(path, tcs); } catch { liveConnectClient.GetCompleted -= (handler); throw; } return tcs.Task; } public static Task<LiveOperationCompletedEventArgs> PostTaskAsync(this LiveConnectClient liveConnectClient, string path, IDictionary<string, object> body) { TaskCompletionSource<LiveOperationCompletedEventArgs> tcs = new TaskCompletionSource<LiveOperationCompletedEventArgs>(path); EventHandler<LiveOperationCompletedEventArgs> handler = null; handler = delegate(object sender, LiveOperationCompletedEventArgs e) { EAPCommon.HandleCompletion<LiveOperationCompletedEventArgs>(tcs, true, e, () => e, delegate { liveConnectClient.PostCompleted -= (handler); }); }; liveConnectClient.PostCompleted += (handler); try { liveConnectClient.PostAsync(path, body, tcs); } catch { liveConnectClient.PostCompleted -= (handler); throw; } return tcs.Task; } public static Task<LiveOperationCompletedEventArgs> UploadTaskAsync(this LiveConnectClient liveConnectClient, string path, Uri uri, OverwriteOption over) { TaskCompletionSource<LiveOperationCompletedEventArgs> tcs = new TaskCompletionSource<LiveOperationCompletedEventArgs>(path); EventHandler<LiveOperationCompletedEventArgs> handler = null; handler = delegate(object sender, LiveOperationCompletedEventArgs e) { EAPCommon.HandleCompletion<LiveOperationCompletedEventArgs>(tcs, true, e, () => e, delegate { liveConnectClient.UploadCompleted -= (handler); }); }; liveConnectClient.UploadCompleted += (handler); try { var stream = FileOperation.GetStreamFromISF(uri); liveConnectClient.UploadAsync(path, "packages.xml", stream, over, tcs); } catch(Exception ex) { liveConnectClient.UploadCompleted -= (handler); throw; } return tcs.Task; } public static Task<LiveDownloadCompletedEventArgs> DownloadTaskAsync(this LiveConnectClient liveConnectClient, string path) { TaskCompletionSource<LiveDownloadCompletedEventArgs> tcs = new TaskCompletionSource<LiveDownloadCompletedEventArgs>(path); EventHandler<LiveDownloadCompletedEventArgs> handler = null; handler = delegate(object sender, LiveDownloadCompletedEventArgs e) { EAPCommon.HandleCompletion<LiveDownloadCompletedEventArgs>(tcs, true, e, () => e, delegate { liveConnectClient.DownloadCompleted -= (handler); }); }; liveConnectClient.DownloadCompleted += (handler); try { liveConnectClient.DownloadAsync(path, tcs); } catch { liveConnectClient.DownloadCompleted -= (handler); throw; } return tcs.Task; } } }
其实代码并不复杂,我主要为Live SDK扩展了下面4个方法,让它们实现了Async的特性
DownloadTaskAsync
UploadTaskAsync
GetTaskAsync
LoginTaskAsync
最后给大家看看实现后的效果。
这样就不需要嵌套多个回调函数,改善了编程体验,也不需要再维护2套代码了,好了,就说到这里,欢迎大家留言讨论。