繁体   English   中英

如何在不阻塞主UI线程的情况下等待事件处理程序返回值

[英]How to wait for a return value from an event handler without blocking main UI thread

所以我有这个方法,它使用一个库来生成一个外部网页,允许用户输入他们的Facebook凭据进行验证。 输入后,我们将根据该用户的帐户创建配置文件并将其保存到数据库中。 一旦我们将它保存到数据库,我们期望一个布尔值来表示该帐户是否是唯一的。 我们需要一些方法来等待数据库在继续执行之前完成所有工作。

我尝试了几种不同的方法,包括在新线程上完成所有工作(由于所有UI都需要在主线程上,因此无法工作)以及使用AutoResetEvent等待auth.Completed事件触发。 但是当使用它时,它通常会阻塞线程,甚至不会为用户显示我们的外部网页。

让这更复杂的是试图处理所有不同的事件而不是超越自己。 所以首先我们需要等待auth.Completed触发,然后从该事件内部我们需要等待异步request.GetResponseAsync().ContinueWith<Task<bool>>(async t => ... event to finish,and终于等待数据库完成其任务,并以某种方式将返回值一直推回到调用过程。

截至目前,此代码将执行所有所需的功能,但将返回false,因为我们不等待任何事件触发。

public bool LoginFacebook(bool isLinkedAccount)
    {
#if __IOS__

        UIKit.UIWindow window = UIKit.UIApplication.SharedApplication.KeyWindow;
        UIKit.UIViewController viewController = window.RootViewController;
#endif
        var auth = new OAuth2Authenticator(
                              clientId: "***********",
                              scope: "email",
                              authorizeUrl: new Uri("https://m.facebook.com/dialog/oauth/"),
                              redirectUrl: new Uri("http://www.facebook.com/connect/login_success.html")
                              );

#if __ANDROID__
        Forms.Context.StartActivity(auth.GetUI(Android.App.Application.Context));
#elif __IOS__
        if (viewController != null)
        {
            while (viewController.PresentedViewController != null)
                viewController = viewController.PresentedViewController;
            viewController.PresentViewController(auth.GetUI(), true, null);
        }
#endif
        // If authorization succeeds or is canceled, .Completed will be fired.
        auth.AllowCancel = true;
        auth.Completed += (sender, eventArgs) =>
        {
#if __IOS__
            viewController.DismissViewController(true, null);
#endif
            if (eventArgs.IsAuthenticated)
            {
                var request = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me"), null, eventArgs.Account);
                request.GetResponseAsync().ContinueWith<Task<bool>>(async t =>
                {
                    if (t.IsFaulted)
                        Console.WriteLine("Error: " + t.Exception.InnerException.Message);
                    else
                    {
                        try
                        {
                            string content = t.Result.GetResponseText();
                            Console.WriteLine(content);
                            JObject user = JsonConvert.DeserializeObject<JObject>(content);
                            Facebook profile = new Facebook();
                            profile.uuid = user["id"].ToString();
                            profile.firstname = user["first_name"].ToString();
                            profile.lastname = user["last_name"].ToString();
                            profile.email = user["email"].ToString();
                            profile.url = user["link"].ToString();

                            //Get picture
                            profile.photo = "https://graph.facebook.com/" + profile.uuid + "/picture?type=large";

                            if (!isLinkedAccount)
                            {
                                //App.AnimateLoading(LoadingImage, OverlayImage, LoadingMessage);
                                return await DatabaseInstance.InsertSocialProfile(SocialNetworks.Facebook, profile, true);
                            }
                            else
                            {
                                await DatabaseInstance.LinkSocialAccountToProfile(App.UserProfile, profile, SocialNetworks.Facebook);
                                return true;
                            }
                        }
                        catch (Exception e)
                        {
                            Console.WriteLine(e.ToString());
                        }
                    }
                    return false;
                });
            }
        };
        return false;
    }

您可以使用TaskCompletionSource来表示一个等待的任务,在事件处理程序和其他async方法之间进行协商。

为了简洁和清晰,我从你的例子中删除了所有#ifdef代码,只关注核心。 考虑到这一点,这是什么样子:

public async bool LoginFacebook(bool isLinkedAccount)
{
    var auth = new OAuth2Authenticator(
                          clientId: "***********",
                          scope: "email",
                          authorizeUrl: new Uri("https://m.facebook.com/dialog/oauth/"),
                          redirectUrl: new Uri("http://www.facebook.com/connect/login_success.html")
                          );

    // If authorization succeeds or is canceled, .Completed will be fired.
    auth.AllowCancel = true;

    TaskCompletionSource<bool> completionSource = new TaskCompletionSource<bool>();

    auth.Completed += (sender, eventArgs) =>
    {
        completionSource.SetResult(eventArgs.IsAuthenticated);
    };

    if (!(await completionSource.Task))
    {
        return false;
    }

    var request = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me"), null, eventArgs.Account);
    var t = await request.GetResponseAsync();

    if (t.IsFaulted)
    {
        Console.WriteLine("Error: " + t.Exception.InnerException.Message);
        return false;
    }

    try
    {
        string content = t.Result.GetResponseText();
        Console.WriteLine(content);
        JObject user = JsonConvert.DeserializeObject<JObject>(content);
        Facebook profile = new Facebook();
        profile.uuid = user["id"].ToString();
        profile.firstname = user["first_name"].ToString();
        profile.lastname = user["last_name"].ToString();
        profile.email = user["email"].ToString();
        profile.url = user["link"].ToString();

        //Get picture
        profile.photo = "https://graph.facebook.com/" + profile.uuid + "/picture?type=large";

        if (!isLinkedAccount)
        {
            return await DatabaseInstance.InsertSocialProfile(SocialNetworks.Facebook, profile, true);
        }
        else
        {
            await DatabaseInstance.LinkSocialAccountToProfile(App.UserProfile, profile, SocialNetworks.Facebook);
            return true;
        }
    }
    catch (Exception e)
    {
        Console.WriteLine(e.ToString());
        return false;
    }
}

当然,通过将此方法更改为async方法,调用方现在可以在结果上异步等待,从而避免阻塞主线程。

当然,缺少一个好的, 最小的完整的代码示例 ,上面只是浏览器编辑的代码。 我没有编译它,没关系测试它。 但以上是一般的想法。 我假设您可以将该概念合并到实际代码中。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM