利用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套代码了,好了,就说到这里,欢迎大家留言讨论。

 

 

posted on 2013-04-03 10:54  豆浆咖啡  阅读(1238)  评论(3编辑  收藏  举报