13

First of all, I'm a total Android noob. Have looked for solutions for some time now, but can't find any helpful hints in the right direction so far. This might be generally caused by the nature of the issue itself, being quite niche.

The working code below is based on a code lab at https://codelabs.developers.google.com/codelabs/realtime-asset-tracking.

I was wondering though, since it was mentioned within several resources as the preferred way now, how you'd do something like that based on Android's Work Manager instead of a Service with an ongoing notification?

public class TrackerService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        requestLocationUpdates();
    }

    private void requestLocationUpdates() {
        LocationRequest request = new LocationRequest();
        request.setInterval(5 * 60 * 1000);
        request.setFastestInterval(30 * 1000);
        request.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        FusedLocationProviderClient client = LocationServices.getFusedLocationProviderClient(this);
        int permission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
        if (permission == PackageManager.PERMISSION_GRANTED) {
            client.requestLocationUpdates(request, new LocationCallback() {
                @Override
                public void onLocationResult(LocationResult locationResult) {
                    Location location = locationResult.getLastLocation();
                    if (location != null) {
                        Log.d(TAG, "location update " + location);
                    }
                }
            }, null);
        }
    }
}

With my experience from web projects the above code establishes a "listener" based on the FusedLocationProviderClient. As soon as there's a new location update, it would call onLocationResult with the respective location result.

What I found out about the Work Manager so far is that you can set up a Worker with Work Manager to doWork() once or periodically. Effectively like a cron job...

What I don't understand, if there's no running service in the background where would the Worker and the request for location updates be initiated? And how would they work together?

individual8
  • 429
  • 1
  • 5
  • 13
  • Let's make clear, you will initiated your request for location updates in your class that extends from Worker class, and you will run your own doWork function. Look at this example - https://codelabs.developers.google.com/codelabs/android-workmanager/#3 – Vadim Eksler Dec 02 '18 at 06:37
  • Thx @VadimEksler - That code would go in my default activity? If I'd run a worker periodically, wouldn't it then register multiple callbacks for location updates? – individual8 Dec 02 '18 at 10:08
  • 1) Separate class MyLocationWorker that extends Worker. And you start it like `PeriodicWorkRequest myWorkRequest = new PeriodicWorkRequest.Builder(MyLocationWorker.class, 30, TimeUnit.MINUTES) .build();` 2) You need to remove callback when you will get your location result. – Vadim Eksler Dec 02 '18 at 10:37

1 Answers1

21

Here I have create demo : LocationTracker-WorkManager

MyWorker.java

public class MyWorker extends Worker {

    private static final String TAG = "MyWorker";

    /**
     * The desired interval for location updates. Inexact. Updates may be more or less frequent.
     */
    private static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;

    /**
     * The fastest rate for active location updates. Updates will never be more frequent
     * than this value.
     */
    private static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    /**
     * The current location.
     */
    private Location mLocation;

    /**
     * Provides access to the Fused Location Provider API.
     */
    private FusedLocationProviderClient mFusedLocationClient;

    private Context mContext;
    /**
     * Callback for changes in location.
     */
    private LocationCallback mLocationCallback;

    public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
        super(context, workerParams);
        mContext = context;
    }

    @NonNull
    @Override
    public Result doWork() {
        Log.d(TAG, "doWork: Done");

                mFusedLocationClient = LocationServices.getFusedLocationProviderClient(mContext);
                mLocationCallback = new LocationCallback() {
                    @Override
                    public void onLocationResult(LocationResult locationResult) {
                        super.onLocationResult(locationResult);
                    }
                };

                LocationRequest mLocationRequest = new LocationRequest();
                mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
                mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
                mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

                try {
                    mFusedLocationClient
                            .getLastLocation()
                            .addOnCompleteListener(new OnCompleteListener<Location>() {
                                @Override
                                public void onComplete(@NonNull Task<Location> task) {
                                    if (task.isSuccessful() && task.getResult() != null) {
                                        mLocation = task.getResult();
                                        Log.d(TAG, "Location : " + mLocation);
                                        mFusedLocationClient.removeLocationUpdates(mLocationCallback);
                                    } else {
                                        Log.w(TAG, "Failed to get location.");
                                    }
                                }
                            });
                } catch (SecurityException unlikely) {
                    Log.e(TAG, "Lost location permission." + unlikely);
                }

                try {
                    mFusedLocationClient.requestLocationUpdates(mLocationRequest, null);
                } catch (SecurityException unlikely) {
                    //Utils.setRequestingLocationUpdates(this, false);
                    Log.e(TAG, "Lost location permission. Could not request updates. " + unlikely);
                }

        } catch (ParseException ignored) {

        }

        return Result.success();
    }
}

How to start worker:

PeriodicWorkRequest periodicWork = new PeriodicWorkRequest.Builder(MyWorker.class, 15, TimeUnit.MINUTES)
                            .addTag(TAG)
                            .build();
WorkManager.getInstance().enqueueUniquePeriodicWork("Location", ExistingPeriodicWorkPolicy.REPLACE, periodicWork);

Hope it will be helpful. Do let me know if you get any problem. Thanks.

Pratik Butani
  • 60,504
  • 58
  • 273
  • 437
  • 1
    Very interesting. I see it even works after restarting. So that seems the way to go. Will have to play further with the idea I had. Thx for the provided solution. – individual8 Jun 14 '19 at 12:30
  • 1
    That answered my question. Thank you. – individual8 Jun 19 '19 at 09:56
  • 3
    This answer needs more upvotes, it solves a lot of peoples' specific problems, including mine. – barnacle.m Sep 09 '19 at 10:54
  • @PratikButaniAndroidDev currently working on a way to do this but for prompting a fresh location update, using ListenableWorker with `concurrent-futures` from workmanager – barnacle.m Sep 09 '19 at 11:36
  • Doesn't it returns success immediately and the callback just won't fire up? I recommend reading this section https://developer.android.com/topic/libraries/architecture/workmanager/advanced/threading – Bartek Pacia Sep 20 '19 at 23:14
  • @Baftek yes, we can use [listenableworker](https://developer.android.com/topic/libraries/architecture/workmanager/advanced/listenableworker.html) – Pratik Butani Jan 09 '20 at 07:17
  • Hi Pratik, what about getting location in background when app is removed. I checked your project but it doesn't include background – Tahir Hussain Mir Sep 04 '20 at 03:19
  • 1
    Have you checked by printing some logs in `doWork()`? It will be called in the background. The minimum interval is 15 minutes. – Pratik Butani Sep 04 '20 at 04:40
  • it is working only first time after that not getting latest location – Ramesh Bhati Jul 20 '23 at 10:39