Android periodic location updates using workmanager
Asked Answered
A

2

7

I am using a WorkManager as follows -

class LocationWorker(
    ctx: Context, params: WorkerParameters
) : CoroutineWorker(ctx, params), KoinComponent {

    private val locationDataRepository: LocationDataRepository by inject()

    override suspend fun doWork(): Result {
        return try {
            locationDataRepository.triggerLocationUpdates()
            Result.success()
        } catch (e: Exception) {
            Result.failure()
        }
    }
}

I trigger the Worker as -

val myWorker =
            PeriodicWorkRequestBuilder<LocationWorker>(
                15,
                TimeUnit.MINUTES
            ).addTag(
                "location"
            ).build()
        WorkManager.getInstance(context).enqueueUniquePeriodicWork(
            "location",
            ExistingPeriodicWorkPolicy.KEEP,
            myWorker
        )

As you see, the WorkManager minimum period is 15 minutes. I want to track the location for very short intervals say every few seconds and also I want the location to be tracked even when the phone screen is off. Is WorkManager the right choice for my requirements or would you suggest me some other API?

Ailing answered 20/9, 2019 at 18:2 Comment(8)
I think Yes , Kindly read this blog Post of medium you will understand it batter. Happy Codding :) medium.com/google-developer-experts/…Airline
@umerfarooq thank you :) But work manager provides least period interval of 15 minutes..What if i want to do every 1 min?Ailing
WorkManager is not designed to run tasks every second, as it has two options to build work request that is and PeriodicWorkRequest - runs repeated task every 15 mins, even if we change the time interval to anyhwere < 15 mins it will by default run for 15 mins only. OneTimeWorkRequest - runs onceAirline
You can implement job schedulerbut it will drain the battery.Airline
@umerfarooq So are there any other alternatives?Ailing
I think firebase job scheduler. But go with your search and other people opnioin as well.:)Airline
@Maria I use a foreground service to get location updates every 30 seconds and save it in database so as to bundle it to server every 15 minutes using alarm managerAntakiya
@Coderbox yes even I use foreground service to get location updates. However the disadvantage is the foreground service cannot run when app is killedAiling
A
2

It might help you it works even when the app is killed..I'm just a tad concerned when device enters doze mode as gps cannot be accessed when device is stationary

foreground service main purpose is to run with a persistent notification when the app is killed

LocationService.class

public class LocationService extends Service implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
    public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 1000 * 30; //30 secs
    public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
            UPDATE_INTERVAL_IN_MILLISECONDS / 2;
    protected static final String TAG = "LocationUpdateService";
    public static final int HORIZONTAL_ACCURACY_IN_METERS = 100;

    /**
     * The identifier for the notification displayed for the foreground service.
     */
    private static final int NOTIFICATION_ID = 12345678;

    public static boolean mRequestingLocationUpdates = false;
    public boolean isEnded = false;
    protected GoogleApiClient mGoogleApiClient;
    protected LocationRequest mLocationRequest;



    private SimpleDateFormat sdf = new SimpleDateFormat("MM/dd/yyyy HH:mm:ss");
    private double latitude = 0;
    private double longitude = 0;
    private float[] results1 = new float[1];
    private float distanceInMeters = 0;
    private Handler mHandler;


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


        String CHANNEL_ID = "FOREGROUND_CHANNEL";
        if (Build.VERSION.SDK_INT >= 26) {

            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "location_notification",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        }

        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setOngoing(true)
                .setContentTitle("G Star")

                .setSmallIcon(R.drawable.gm_noti_logo)
                .setContentText("Running").build();

        startForeground(NOTIFICATION_ID, notification);
        Utility.getInstance().makeDescriptiveLogs("ON CREATE WAS HIT");

        sendLocationDataToServerPeriodically();


    }

    private void sendLocationDataToServerPeriodically() {


//getting the alarm manager
        AlarmManager am = (AlarmManager) getSystemService(Context.ALARM_SERVICE);

        //creating a new intent specifying the broadcast receiver
        Intent intentLR = new Intent(this, PostLocationReceiver.class);

        PendingIntent pi = PendingIntent.getBroadcast(this, 0, intentLR,
                PendingIntent.FLAG_UPDATE_CURRENT);


        if (android.os.Build.VERSION.SDK_INT >= 23) {
            assert am != null;
            am.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                    AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
        } else if (Build.VERSION.SDK_INT >= 19) {
            if (am != null) {
                am.setInexactRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        } else {
            if (am != null) {
                am.setRepeating(AlarmManager.RTC_WAKEUP,
                        System.currentTimeMillis(), AlarmManager.INTERVAL_FIFTEEN_MINUTES, pi);
            }
        }


    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        isEnded = false;

        Utility.getInstance().makeDescriptiveLogs("ONSTART COMMAND WAS HIT");
        buildGoogleApiClient();
        if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) {
            startLocationUpdates();
        }
        mHandler = new Handler();
        return START_STICKY;
    }

    @Override
    public void onConnected(Bundle bundle) {
        startLocationUpdates();
    }

    @Override
    public void onConnectionSuspended(int i) {
        // The connection to Google Play services was lost for some reason. We call connect() to
        // attempt to re-establish the connection.

        mGoogleApiClient.connect();
    }

    @Override
    public void onLocationChanged(Location location) {

        if (location.getAccuracy() < HORIZONTAL_ACCURACY_IN_METERS)
            updateUI(location);
    }

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
        // onConnectionFailed.

    }

    protected synchronized void buildGoogleApiClient() {

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();

        createLocationRequest();
    }

    /**
     * Updates the latitude, the longitude, and the last location time in the UI.
     */
    private void updateUI(Location mCurrentLocation) {



        mHandler.post(() -> {
            /*GET DEVICE CURRENT BATTERY LEVEL*/
            int batteryPercent = Utility.getInstance().getBatteryPercentage(LocationService.this);


            /*  CALCULATE DISTANCE BETWEEN LAT LONG INTERVALS*/
            if (latitude != 0 && longitude != 0) {
                Location.distanceBetween(latitude, longitude, mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), results1);
                distanceInMeters = results1[0];
            }


            latitude = mCurrentLocation.getLatitude();
            longitude = mCurrentLocation.getLongitude();

            /*CHECK IF DEVICE HAS ACTIVE INTERNET CONNECTION*/
            String networkStatus = Utility.getInstance().checkConnection(LocationService.this) ? "1" : "0";


            /*CHECK NETWORK SIGNAL STRENGTH*/
            String signalStrength = Utility.getInstance().getSignalStrength(LocationService.this);

            SQLiteDBHandler db = SQLiteDBHandler.getInstance(LocationService.this);
            db.insertDeviceLocation(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(), Build.VERSION.SDK_INT >= Build.VERSION_CODES.O ? mCurrentLocation.getSpeedAccuracyMetersPerSecond() : mCurrentLocation.getSpeed(), sdf.format(Calendar.getInstance().getTime()), distanceInMeters, batteryPercent, networkStatus, signalStrength);


        });

    }

    protected void createLocationRequest() {
        mGoogleApiClient.connect();
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setSmallestDisplacement(5);
        mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    }

    /**
     * Requests location updates from the FusedLocationApi.
     */
    public void startLocationUpdates() {
        if (!mRequestingLocationUpdates) {
            mRequestingLocationUpdates = true;

            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }


            LocationServices.FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);

            isEnded = true;
        }
    }

    /**
     * Removes location updates from the FusedLocationApi.
     */
    public void stopLocationUpdates() {
        if (mRequestingLocationUpdates) {
            mRequestingLocationUpdates = false;



            LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

        }
    }

    @Override
    public void onDestroy() {

        mHandler.removeCallbacksAndMessages(null);

        stopLocationUpdates();

        super.onDestroy();
    }


}
Antakiya answered 27/11, 2019 at 9:56 Comment(5)
So are you saying we don't need WorkManager?Zillion
@Zillion No,I shared what works for me so it could be helpful for someone.it would be a better implementation with WorkManager instead of AlarmManager..some more juice for the deviceAntakiya
@Antakiya How that will affect the battery optimization?Rancidity
now a days service do not work in android 12 and above so think we need to user work manager insteadSwig
Does it work when the app is fully closed and phone in doze mode?Spill
A
0

I think that this codelab is the best solution. Without the WorkManager, but for me it works in AccessibilityService ( I realized it like an alternative to ForegroundService) to escape the permanent notification https://codelabs.developers.google.com/codelabs/background-location-updates-android-o/#0

Apo answered 10/12, 2019 at 0:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.