Я внедряю аутентификацию Android на Facebook в Xamarin Forms и борюсь с SDK Facebook, ломая цепочку ожидания / асинхронности.SDK требует передачи реализаций IFacebookCallback
и GraphRequest.IGraphJSONObjectCallback
, которые определяют обратные вызовы для обработки результатов.Я хотел бы сделать следующее в коде, который запрашивает логин:
public async Task LogInFacebookAsync()
{
var loginResult = await _facebookManager.Login();
// do stuff with result
}
Однако вот что я сейчас делаю.
Код, который запрашивает логин:
public void LogInFacebookAsync()
{
_facebookManager.Login(onFacebookLoginComplete);
}
private async Task<string> onFacebookLoginComplete(FacebookUser fbUser, string errorMessage)
{
// do stuff with result
}
Вот мой класс FacebookManager, который реализует необходимые интерфейсы.Обратите внимание, что результаты могут быть получены из нескольких методов обратного вызова: OnCancel()
, OnError()
, OnCompleted()
, каждый из которых вызывает _onLoginComplete, как передано в коде выше.
public class FacebookManager : Java.Lang.Object, IFacebookManager, IFacebookCallback, GraphRequest.IGraphJSONObjectCallback
{
public Func<FacebookUser, string, Task> _onLoginComplete;
public ICallbackManager _callbackManager;
private Activity _context;
public FacebookManager()
{
this._context = CrossCurrentActivity.Current.Activity;
_callbackManager = CallbackManagerFactory.Create();
LoginManager.Instance.RegisterCallback(_callbackManager, this);
}
public async Task Login(Func<FacebookUser, string, Task> onLoginComplete)
{
_onLoginComplete = onLoginComplete;
LoginManager.Instance.SetLoginBehavior(LoginBehavior.NativeWithFallback);
LoginManager.Instance.LogInWithReadPermissions(_context, new List<string> { "public_profile", "email" });
await Task.CompletedTask;
}
#region IFacebookCallback
public void OnCancel()
{
_onLoginComplete?.Invoke(null, "Canceled!");
}
public void OnError(FacebookException error)
{
_onLoginComplete?.Invoke(null, error.Message);
}
public void OnSuccess(Java.Lang.Object result)
{
var n = result as LoginResult;
if (n != null)
{
var request = GraphRequest.NewMeRequest(n.AccessToken, this);
var bundle = new Android.OS.Bundle();
bundle.PutString("fields", "id, first_name, email, last_name, picture.width(500).height(500)");
request.Parameters = bundle;
request.ExecuteAsync();
}
}
#endregion
#region GraphRequest.IGraphJSONObjectCallback
public void OnCompleted(JSONObject p0, GraphResponse p1)
{
// extract user
_onLoginComplete?.Invoke(fbUser, string.Empty);
}
#endregion
}
Можно ли как-нибудь обернуть эти операции, чтобы я был "полностью асинхронным"?
Обновление:
Спасибо, Пауло, за отличный ответ .Я уже думал в том же духе, а вы действительно выкристаллизовали решение.Мое единственное беспокойство с ответом было о том, что это потокобезопасныйЧто, если дважды щелкнуть кнопку или сделать два вызова API одновременно, которые требуют входа в Facebook?Далее я попытаюсь сделать метод LoginAsync()
более безопасным.Однако я не специалист по безопасности потоков и приветствую любую критику.
private TaskCompletionSource<FacebookUser> _loginTcs;
public async Task<FacebookUser> LoginAsync()
{
Task<FacebookUser> loginTask = _loginTcs?.Task;
// Don't want to create a new login request to the Facebook API if one is already being made.
if (loginTask != null && (loginTask.Status == TaskStatus.Running || loginTask.Status == TaskStatus.WaitingForActivation
|| loginTask.Status == TaskStatus.WaitingToRun || loginTask.Status == TaskStatus.WaitingForChildrenToComplete))
{
return await loginTask.ConfigureAwait(false);
}
this._loginTcs = new TaskCompletionSource<FacebookUser>();
LoginManager.Instance.SetLoginBehavior(LoginBehavior.NativeWithFallback);
LoginManager.Instance.LogInWithReadPermissions(_context, new List<string> { "public_profile", "email" });
var user = await this._loginTcs.Task.ConfigureAwait(false);
return user;
}