TorbenJ
TorbenJ

Reputation: 4582

DependencyService.Get<T> doesn't return correct instance

I'm new to Xamarin and tried some beginner tutorials so far.
Now I wanted to build some custom stuff and need to request the current location.
For this I installed Xamarin.GooglePlayServices.Location in order to have access to the FusedLocationProviderClient class.

In MainActivity I retrieve the FusedLocationProviderClient:

public class MainActivity : FormsAppCompatActivity, ILocationProvider
{
    private FusedLocationProviderClient FusedLocationClient { get; set; }

    protected override void OnCreate(Bundle savedInstanceState)
    {
        // snip

        FusedLocationClient = LocationServices.GetFusedLocationProviderClient(this);
    }
}

The ILocationProvider needs the method TaskCompletionSource<Model.Location> GetPosition() to be implemented:

public TaskCompletionSource<Model.Location> GetPosition()
{
    // snip

    if (CheckSelfPermission(Manifest.Permission_group.Location) == Permission.Granted)
    {
        FusedLocationClient.LastLocation.AddOnCompleteListener(new OnLocationRequestCompleteListener(this, tcs));
    }

    // snip
}

The UI has a button to request the current location.
Whenever the user clicks the button I get the ILocationProvider via DependencyService and execute the GetPosition method:

private void GetPositionClicked(object sender, EventArgs e)
{
    var provider = DependencyService.Get<ILocationProvider>();

    if(provider != null)
    {
        var tcs = provider.GetPosition();

        // snip
    }
}

The problem now is that the application crashes as soon as I try to execute the CheckSelfPermission method in GetPosition().
I set a breakpoint in GetPosition() and noticed that FusedLocationClient is null although it clearly wasn't null after OnCreate was called.

I then inspected this in OnCreate and GetPosition and noticed that they weren't the same instances which leads me to the conclusion that something is clearly wrong here.

To solve this for now I did the following:

public class MainActivity : FormsAppCompatActivity, ILocationProvider
{
    internal static MainActivity Instance { get; private set; }

    private FusedLocationProviderClient FusedLocationClient { get; set; }

    protected override void OnCreate(Bundle savedInstanceState)
    {
        // snip
        Instance = this
        // snip
    }

    // snip
    public TaskCompletionSource<Model.Location> GetPosition()
    {
        if (this != Instance)
        {
            return Instance.GetPosition();
        }

        // snip
    }
}

At least this works for now but I can't imagine that this is the way to go.
What I learned so far is that DependencyService seems to not get already created instances but create one instead (at least once).
What would be the correct way to call methods in MainActivity from the shared .NET Standard library?

Upvotes: 1

Views: 178

Answers (1)

SushiHangover
SushiHangover

Reputation: 74144

Move those methods out of your Activity class into a standalone class (within your Xamarin.Android application (or a Xamarin.Android library project).

Assuming an interface like:

public  interface ILocationProvider
{
    Task<Tuple<double, double>> GetPosition();
}

Android Implementation:

public class Location_Android: Java.Lang.Object, ILocationProvider
{
    private FusedLocationProviderClient FusedLocationClient { get; set; }

    public Location_Android()
    {
        FusedLocationClient = new FusedLocationProviderClient(Application.Context);
    }

    public async Task<Tuple<double, double>> GetPosition()
    {
        var loc =  await FusedLocationClient?.GetLastLocationAsync();
        return new Tuple<double, double>(loc.Latitude, loc.Longitude); 
    }
}

Upvotes: 1

Related Questions