简体   繁体   中英

GPS location not getting updates when app is killed

I have a requirement of sending a user's location to a web server after a certain interval, currently sending after every two minutes. It is working fine unless the app is running. When the app is in background location stops getting updated, sending the same location again and again. I am little confused. it would be great if anyone suggests an alternative to my approach. I start location sending on click of a button and does not stop unless and until the user clicks on the button to stop. Below is my code. I want to send current location of the user to a web server whether the app is running or not. Any help is appreciated. problem is not in sending location after a certain interval , problem is location stops getting updated if I kill the app coordinates remain the same. If app is running in foreground then even a slight tilt in device makes the coordinates change. I earlier used firebaseJobDispatcher to call this service. Problem is not in calling the service. Problem is location stops getting updated and remains same every time I call the service if I have killed the app.

I am using alarm manager to call this service every 2 minutes.

public class GPSTracker_DUP extends Service implements LocationListener {

    private  Context mContext=null;
    RetrofitAPI retrofitAPI;

    // flag for GPS status
    boolean isGPSEnabled = false;

    // flag for network status
    boolean isNetworkEnabled = false;

    // flag for GPS status
    boolean canGetLocation = false;

    SaveData objSaveData;
    Location location; // location
    double latitude; // latitude
    double longitude; // longitude
    private String provider;
    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

    // Declaring a Location Manager
    protected LocationManager locationManager;

    public GPSTracker_DUP(Context context) {
        this.mContext = context;
        //getLocation();
    }

    public GPSTracker_DUP(){}



    public Location getLocation() {
        try {
            locationManager = (LocationManager) mContext
                    .getSystemService(LOCATION_SERVICE);


            Criteria criteria = new Criteria();
            provider = locationManager.getBestProvider(criteria, false);

            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return null;
            }

            // getting GPS status
            isGPSEnabled = locationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);
            if(!isGPSEnabled)
            {
                showSettingsAlert();

            }
            else
            {

                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,1000*60*2,0,this);
                location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);

                if (location != null) {
                    Log.e("Provider ",  provider + " has been selected."+location.getLatitude()+"==="+location.getLongitude());

                    saveLocation(location.getLatitude(),location.getLongitude());

                    //onLocationChanged(location);
                }
            }


            // getting network status
//            isNetworkEnabled = locationManager
//                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);


        } catch (Exception e) {
            e.printStackTrace();
        }

        return location;
    }


    public static boolean isConnected(Context context){
        NetworkInfo info = getNetworkInfo(context);
        return (info != null && info.isConnected());
    }

    public static NetworkInfo getNetworkInfo(Context context){
        ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        return cm.getActiveNetworkInfo();
    }
    /**
     * Stop using GPS listener
     * Calling this function will stop using GPS in your app
     * */
    public void stopUsingGPS(){
        if(locationManager != null){
            locationManager.removeUpdates(GPSTracker_DUP.this);
        }
    }

    /**
     * Function to get latitude
     * */
    public double getLatitude(){
        if(location != null){
            latitude = location.getLatitude();
        }

        // return latitude
        return latitude;
    }

    /**
     * Function to get longitude
     * */
    public double getLongitude(){
        if(location != null){
            longitude = location.getLongitude();
        }

        // return longitude
        return longitude;
    }

    /**
     * Function to check GPS/wifi enabled
     * @return boolean
     * */
    public boolean canGetLocation() {
        return this.canGetLocation;
    }


    /**
     * Function to show settings alert dialog
     * On pressing Settings button will lauch Settings Options
     * */
    public void showSettingsAlert(){
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

        // Setting Dialog Title
        alertDialog.setTitle("GPS is settings");

        // Setting Dialog Message
        alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");

        // On pressing Settings button
        alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog,int which) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                mContext.startActivity(intent);
            }
        });

        // on pressing cancel button
        alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });

        // Showing Alert Message
        alertDialog.show();
    }


    @Override
    public void onLocationChanged(Location location) {
        latitude = location.getLatitude();
        longitude = location.getLongitude();
        Log.e("onlocation","changed");


    }
    public void saveLocation(Double latitude,Double longitude){
        objSaveData = new SaveData(mContext);

            Log.e("Saving Coordinates", latitude + " " + longitude);
            AudioDbHelper audioDbHelper= new AudioDbHelper(mContext);
            UserCoordinates userCoordinates = new UserCoordinates();
            userCoordinates.setLatitude(String.valueOf(latitude));
            userCoordinates.setLongitude(String.valueOf(longitude));
            userCoordinates.setUploaded("no");
            SaveData objSaveData = new SaveData(mContext);
            userCoordinates.setUserEmail(objSaveData.getString("LoginId"));
            String time = new SimpleDateFormat("hh:mm: aa").format(Calendar.getInstance().getTime());
            userCoordinates.setLocationTime(time);
            audioDbHelper.addCoordinates(userCoordinates);

    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopUsingGPS();
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

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

Since You need to send location after some interval, It's better to use Job Scheduler which starts the service after an interval, fetches the location and makes the API call.

The best is Job Scheduler and is what is recommended by Google, but Android versions limit it's use, Its better to use Evernote Android Job . Depending on the Android version either the JobScheduler, GcmNetworkManager or AlarmManager is used.

You don't have to worry about the service getting killed as now it's the OS's responsibility to start the service

Also about fetching the location, Use Google PLay Location, to fetch location. What it does is it fetches the location from Google Play service in your device which is updated from time to time.

Take a look at this Util class that I use in one of my projects to fetch location from a running service. It is in Kotlin and uses Dagger2, but you will get the idea. It has a callback interface which replies with the current location and address fetched via Google Play Location Service

ServiceLocationUtil.kt

You need to create a Start_Sticky service for this by default its START_STICKY_COMPATIBILITY . Override onStartCommand() .

 @Override
public int onStartCommand(Intent intent, int flags, int startId){
    // Do your Stuff
    return START_STICKY;
}

Whereas due to Background Limitations this will not work in latest versions of android. So you probably want to checkout Background Location Limits and Android 8.0 Behavior Changes .

I suggest you to use JobShedular if you need to send location after some interval. There are some available like Evernote android-job , Firebase JobDispatcher .

Also readIntelligent Job-Scheduling .

You should use WorkManager or FirebaseJobDispatcher for background processes. But FirebaseJobDispatcher is not supported from Android Q.

This is my solution using WorkManager for getting location in background

Define this in activity or fragment

   private fun startTaskWithWorkManager() {
    val constraints: Constraints = Constraints.Builder()
        .setRequiredNetworkType(NetworkType.CONNECTED)
        .build()
    val locationWorker =
        PeriodicWorkRequest.Builder(LocationWorker::class.java, MIN_PERIODIC_INTERVAL_MILLIS, TimeUnit.MILLISECONDS)
            .setConstraints(constraints)
            .addTag(LOCATION_WORKER_TAG)
            .setInputData(createInputData())
            .build()
    WorkManager.getInstance()
        .enqueueUniquePeriodicWork(LOCATION_WORKER_TAG, ExistingPeriodicWorkPolicy.KEEP, locationWorker)
}

After that you should create a class that will extends from ListenableWorker. In my case I should have to use ListenableWorker instead of Worker. The difference you can find here and here .

class LocationWorker(context: Context, private val workerParams: WorkerParameters) :
ListenableWorker(context, workerParams) {

lateinit var mFuture: SettableFuture<ListenableWorker.Result>
private var fusedLocationProviderClient = FusedLocationProviderClient(context)

@SuppressLint("RestrictedApi", "MissingPermission")
override fun startWork(): ListenableFuture<Result> {
    val uniqueId = workerParams.inputData.getString(UNIQUE_ID_KEY)
    mFuture = SettableFuture.create()
    Timber.d("mFutureStart")
    fusedLocationProviderClient.lastLocation.addOnSuccessListener { location ->
        Timber.d("location == $location")
        if (location != null) {
             mFuture.set(Result.success())
        } else mFuture.set(Result.failure())
      }
    return mFuture
  }
}

Thats it :) Work like a charm

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM