如何在不阻塞主 UI 线程的情况下等待来自事件处理程序的 return 值
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 => ...
事件完成,最后等待数据库完成其任务并以某种方式推送 return 返回调用过程的值。
截至目前,此代码将执行所有需要的功能,但会 return 错误,因为我们没有等待任何事件触发。
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
方法,调用者现在可以异步等待结果,避免阻塞主线程。
当然,缺少 a good, minimal, complete code example 开始,以上只是浏览器编辑的代码。我没有编译它,不要介意测试它。但以上是总体思路。我假设您可以将这个概念合并到您的实际代码中。
所以我有了这个方法,它使用一个库来生成一个外部网页,以允许用户输入他们的 Facebook 凭据进行验证。输入它后,我们将根据该用户的帐户创建一个配置文件并将其保存到数据库中。一旦我们将它保存到数据库中,我们就期望一个布尔值来表示该帐户是否是唯一的。在继续执行之前,我们需要一些方法来等待数据库完成所有工作。
我尝试了几种不同的方法,包括在新线程上完成所有工作(这不会工作,因为所有 UI 都需要在主线程上)以及使用 AutoResetEvent
等待 auth.Completed
事件触发。但是在使用这个的时候,它通常会阻塞线程,甚至不会为用户呈现我们的外部网页。
让事情变得更复杂的是尝试处理所有不同的事件,而不是超越自己。所以首先我们需要等待 auth.Completed
触发,然后我们需要从该事件内部等待异步 request.GetResponseAsync().ContinueWith<Task<bool>>(async t => ...
事件完成,最后等待数据库完成其任务并以某种方式推送 return 返回调用过程的值。
截至目前,此代码将执行所有需要的功能,但会 return 错误,因为我们没有等待任何事件触发。
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
方法,调用者现在可以异步等待结果,避免阻塞主线程。
当然,缺少 a good, minimal, complete code example 开始,以上只是浏览器编辑的代码。我没有编译它,不要介意测试它。但以上是总体思路。我假设您可以将这个概念合并到您的实际代码中。