Search code examples
androidgpslocationlocation-client

requestLocationUpdates after a exact fixed interval


Objective: To save current locations in Service in database after exact 15 min with in service (using less battery).I use these location at various points in my app.

locationrequest = LocationRequest.create();
        locationrequest.setInterval(5*60000);
locationrequest
        .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        locationclient.requestLocationUpdates(locationrequest, mPendingIntent);

Problem: I'm using the above code does not request location according to set interval value.Although, I'm aware that This interval is inexact. You may not receive updates at all, or you may receive them slower than requested. You may also receive them faster than requested. Sometimes, the location is updated after 1 min , I don't want to waste processing and battery to get locations at small intervals.

public class LoginActivity extends Activity implements OnClickListener  
    , 

GooglePlayServicesClient.ConnectionCallbacks,GooglePlayServicesClient.OnConnectionFailedListener,LocationListener {

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.login_screen);
///my code 
mIntentService = new Intent(LoginActivity.this,LocationService.class);
        mIntentService.putExtra("time",String.valueOf(System.currentTimeMillis()) );
          mPendingIntent = PendingIntent.getService(LoginActivity.this, 1, mIntentService, 0);

          int resp =GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
          if(resp == ConnectionResult.SUCCESS){
           locationclient = new LocationClient(this,this,this);
           locationclient.connect();
          }
          else{
           Toast.makeText(this, "Google Play Service Error " + resp, Toast.LENGTH_LONG).show();
          }


    @Override
    public void onLocationChanged(Location location) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onConnectionFailed(ConnectionResult arg0) {
        // TODO Auto-generated method stub

    }

    @Override
    public void onConnected(Bundle arg0) {
        // TODO Auto-generated method stub
        Log.i("fused", " onConnected " );
//      mIntentService = new Intent(LoginActivity.this,LocationService.class);
//        mPendingIntent = PendingIntent.getService(LoginActivity.this, 1, mIntentService, 0);

        locationrequest = LocationRequest.create();
        locationrequest.setInterval(5*60000);
//      locationrequest
//      .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        locationclient.requestLocationUpdates(locationrequest, mPendingIntent);
//       locationrequest = LocationRequest.create();
//         locationrequest.setInterval(1000);//??
//         locationclient.requestLocationUpdates(locationrequest, this);

    }

    @Override
    public void onDisconnected() {
        // TODO Auto-generated method stub

    }

}

LocationService

public class LocationService extends IntentService {

    private String TAG = this.getClass().getSimpleName();
    public LocationService() {
        super("Fused Location");
    }

    public LocationService(String name) {
        super("Fused Location");
    }

    @Override
    protected void onHandleIntent(Intent intent) {
//      Log.i("fused", "onHandleIntent LocationService");

            Location location = intent.getParcelableExtra(LocationClient.KEY_LOCATION_CHANGED);
            if(location !=null){
                String time= intent.getStringExtra("time");
                Log.i("fused", "onHandleIntent LocationService " +time+"---"+ location.getLatitude() + "," + location.getLongitude());


                updateTransientLocation(getApplicationContext(), location);


            }

    }

Also, I need to save these locations periodically in database in background only and hence cannot use requestLocationUpdates without pending intent to service.

I have refered to this for the code

Thanks.

EDIT -SOLUTION This is how my problem was solved

Code in Activity

    Intent myIntent = new Intent(context,LocationReceiver.class);


        PendingIntent pi = PendingIntent.getBroadcast(context, 0, myIntent, 0);

        alarmMgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                SystemClock.elapsedRealtime(),
//              120000,pi);
  • I removed the location Service class and added location receiver

LocationReceiver

public class LocationReceiver extends BroadcastReceiver implements
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {
    SharedPreferences prefs = null;
    LocationClient locationclient = null;
    Context contxt;

    /** For location poller NO LONGER IN USE **/


    @Override
    public void onReceive(Context context, Intent intent) {
        contxt=context;
        //Log.i("locationreciever", "in location rec");
        Log.i("fused", "in location rec");


        int resp = GooglePlayServicesUtil
                .isGooglePlayServicesAvailable(context);

        if (resp == ConnectionResult.SUCCESS) {
            locationclient = new LocationClient(context, this, this);
            locationclient.connect();
        } else {
            Log.i("fused", "loc client Google Play Service Error");
        }
}

@Override
    public void onLocationChanged(Location location) {
        Log.i("fused", " onLocationChanged Location Request :" + location.getLatitude() + "," + location.getLongitude());
        updateTransientLocation(contxt, location);
        if (locationclient != null) {
            if (locationclient.isConnected()) {
                locationclient.removeLocationUpdates(this);
                locationclient.disconnect();
            }
        }
    }

    @Override
    public void onConnectionFailed(ConnectionResult arg0) {
        Log.i("fused", "loc client connection failed");
    }

    @Override
    public void onConnected(Bundle arg0) {
        Log.i("fused", "loc client onConnected");
        LocationRequest locationrequest = LocationRequest.create();
        locationrequest
    .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
        locationclient.requestLocationUpdates(locationrequest, this);
    }

    @Override
    public void onDisconnected() {
        Log.i("fused", "loc client disconnected");
    }
}

Solution

  • The best solution would be to use your current approach. You're tell the OS that you don't need locations more often, but something else might be requesting locations, in which case you might as well just accept it, now that the phone has already woken up to get a GPS fix and broadcast it to every process that's interested in a location. This way, your application may actually never have to turn on the GPS, because you're basically just using a location fix that was requested by another process more often that every 15 minutes. The keyword to search for here is the new fused location provider.

    If you insist on getting a location exactly every 15 minutes, you can, instead of scheduling a location request, use an AlarmManager to schedule a job to run every 15 minutes. In your alarm manager, you can then immediately request a new single location, and then completely stop requesting new locations until your job is scheduled to run again. If you go down this route, you'll likely run into problems with your service ending before you get a result, because of the asynchronous nature of the location service. Therefore, you want to poll for a location in your alarm manager. You can use a project like CWAC LocationPoller for that

    The documentation has examples of how to schedule recurring events: https://developer.android.com/training/scheduling/alarms.html

    Depending on your need, you should be think about the fact that a location may not be available every 15 minutes. Maybe the user is outside of GPS/wifi/phone range. So it may or may not be beneficial to start a task a bit early, or more often, to make sure you have a reasonable fix after your 15 minute window has elapsed.

    With all that said, here's the code snippet you're actually interested in to solve your specific problem (taken directly from the CWAC locationpoller site):

    1. Create a recurring alarm manager

    mgr=(AlarmManager)getSystemService(ALARM_SERVICE);
    
    Intent i=new Intent(this, LocationPoller.class);
    
    Bundle bundle = new Bundle();
    LocationPollerParameter parameter = new LocationPollerParameter(bundle);
    parameter.setIntentToBroadcastOnCompletion(new Intent(this, LocationReceiver.class));
    // try GPS and fall back to NETWORK_PROVIDER
    parameter.setProviders(new String[] {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER});
    parameter.setTimeout(60000);
    i.putExtras(bundle);
    
    
    pi=PendingIntent.getBroadcast(this, 0, i, 0);
    mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                                        SystemClock.elapsedRealtime(),
                                        PERIOD,
                                        pi);
    

    2. Create a BroadcastReceiver to receive your location data

    Bundle b=intent.getExtras();
    
    LocationPollerResult locationResult = new LocationPollerResult(b);
    
    Location loc=locationResult.getLocation();
    String msg;
    
    if (loc==null) {
      loc=locationResult.getLastKnownLocation();
    
      if (loc==null) {
        msg=locationResult.getError();
      }
      else {
        msg="TIMEOUT, lastKnown="+loc.toString();
      }
    }
    else {
      msg=loc.toString();
    }
    
    if (msg==null) {
      msg="Invalid broadcast received!";
    }