11

Edit: Sample project that demonstrates crash can be found here: https://github.com/rringham/brokenazurexamforms - you need to set your own Azure App Service URL in:

  • src/BrokenAzureForms/Droid/Services/User/DroidUserService.cs
  • src/BrokenAzureForms/iOS/Services/User/IosUserService.cs

I'm seeing Xamarin Forms's Navigation.PushAsync() crash on Android when I attempt to use it after authenticating with Azure MobileServiceClient. This crash is isolated to Android - it does not happen on iOS.

Here's the setup - I've got a basic NavigationPage as my main app page:

MainPage = new NavigationPage(new LoginPage());

On my LoginPage, I authenticate using a DependencyService-injected class that performs authentication in my Android project:

private async void OnMicrosoftAccountTapped(object sender, EventArgs args)
{
    IUserService userService = DependencyService.Get<IUserService>();
    bool authenticated = await userService.LoginWithAzureAD();
    if (authenticated)
    {
        await Navigation.PushAsync(new HomePage(), false);
    }
 }

In my Android implementation of IUserService, I do this (pretty much exactly what the Azure / Xamarin Forms tutorials show):

public async Task<bool> LoginWithAzureAD()
{
    try
    {
        _user = await _client.LoginAsync(Xamarin.Forms.Forms.Context, MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory);
    }
    catch (Exception)
    {
        return false;
    }

    return true;
}

Here's where things fall apart. When LoginWithAzureAD() is done, control resumes in OnMicrosoftAccountTapped(); we then go to call Navigation.PushAsync(), and boom - the app crashes, with very little detail to go on:

MobileServiceClient / Navigation crash

All I can think is that Azure MobileServiceClient is doing something pretty funky with Xamarin.Forms.Forms.Context internally, because if I remove the call to await userService.LoginWithAzureAD(), the call to Navigation.PushAsync() works with no issues. Something in MobileServiceClient is either broken, or is breaking something in Xamarin Forms.

Anyone see anything like this?

Rob
  • 25,984
  • 32
  • 109
  • 155
  • Hi @Rob Ringham, did you manage to get this sorted in the end? This is exactly the problem I am facing at the moment and had nailed it down to the same call as you had. Thanks. – Brett Rigby Jun 01 '17 at 21:49

2 Answers2

4

When I do this, I use the following:

In MainActivity.cs:

// Initialize for Azure Mobile Apps
Microsoft.WindowsAzure.MobileServices.CurrentPlatform.Init();

// Initialize for Xamarin Forms
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);

// Initialize for Login Provider
var lp = (DroidLoginProvider)DependencyService.Get<ILoginProvider>();
lp.Initialize(this);

Then, in my DroidLoginProvider class, I do the following:

Context _context;

public void Initialize(Context context)
{
    this._context = context;
}

public async Task LoginAsync(MobileServiceClient client)
{
    await client.LoginAsync(this._context, MobileServiceAuthenticationProvider.whatever);
}

I call the LoginAsync from a Singleton wrapper in my shared project. It's important that it is a Singleton because there should only be one MobileServiceClient in a project - the authentication is stored in the MobileServiceClient.CurrentUser property and is only set on the current client.

You can see a working project with this logic here: https://github.com/adrianhall/30-days-of-zumo-v2/tree/master/file-upload

Adrian Hall
  • 7,990
  • 1
  • 18
  • 26
  • Thanks Adrian - I've given this a try, specifically adding the initialization call for Azure Mobile Apps in MainActivity, as well as caching the Android Context on my login provider from MainActivity; I've updated the code in my Git repo so that it is effectively functionality equivalent to what you're suggesting, but unfortunately I still see the crash (also I do follow the Singleton approach RE: MobileServiceClient). I'll give your sample project a whirl and see if that works! – Rob Jul 20 '16 at 04:03
  • Final comment on this - I'm using Xamarin Forms v2.3.x in my projects. There is a later version, but I have issues with getting the right combination to get a valid compile on Android. – Adrian Hall Jul 21 '16 at 16:00
  • Just as an FYI, I've just been reading-up about Singletons, and found this on MSDN: "The main disadvantage of [the Singleton] implementation, however, is that it is not safe for multithreaded environments. If separate threads of execution enter the Instance property method at the same time, more that one instance of the Singleton object may be created." (Taken from https://msdn.microsoft.com/en-us/library/ff650316.aspx) – Brett Rigby Jun 03 '17 at 07:46
  • You generally should ensure that the getInstance() method is synchronized - i.e. surrounded by a lock() statement. See http://csharpindepth.com/Articles/General/Singleton.aspx for a brilliant in-depth article on singletons in C#. – Adrian Hall Jun 10 '17 at 01:24
4

I think the login issue is orthogonal, and you're calling PushAsync from a background thread. You should just await the call to your dependency service method from your main thread, and then do PushAsync there.

Here's a sample:

lindydonna
  • 3,874
  • 17
  • 26
  • Hmm, I'm definitely not calling `PushAsync` from a background thread - my LoginWithAzureAD method is called on the UI thread in a button Clicked event handler, which in turn (and on the same thread) calls `MobileServiceClient`'s `LoginAsync` method. – Rob Jul 19 '16 at 22:49
  • @RobRingham Yeah it sounds like it would still be the UI thread. Just to isolate the issue, try moving the PushAsync code to the UI and await the login result, and see if the exception repros. – lindydonna Jul 20 '16 at 02:31
  • I'm not sure I follow - the `PushAsync` call is definitely being called on the UI thread after I've awaited the login result (which returns successfully). I've even tried putting the call to `PushAsync` further down the UI run loop via `Device.BeginInvokeOnMainThread()`, to no avail. Something in the way `MobileServiceClient` works appears to be incompatible with the way Xamarin Forms works on Android; I suspect it may be a bug within Xamarin Forms itself. – Rob Jul 20 '16 at 04:29