Amrita Stha
Amrita Stha

Reputation: 3347

Fused location provider with JobScheduler

I have a fused location provider code in MainActivity class providing latitude and longitude values, which is passed to JobService class using persistableBundle. It works fine when the user is using the app (ie. app is in foreground). As soon as the app is swiped out or destroyed, the last updated value from MainActivity is repeatedly uploaded through the job scheduler all the time (ie. the jobscheduler gets the same value all the time, the fused location provider don't work). What should I do to make it work even if the app is not in the foreground? (PS. it works when the app is minimized. ie. it can be seen in recent app list but the problem occurs as soon as it is swiped out from the list)

MainActivity.class

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // JobScheduler starts
        btnStartJob = (Button)findViewById(R.id.startjob);

        jobScheduler = (JobScheduler)getSystemService(JOB_SCHEDULER_SERVICE);
        btnStartJob.setOnClickListener(new View.OnClickListener(){

            @Override
            public void onClick(View v) {

                ComponentName jobService =
                        new ComponentName(getPackageName(), MyJobService.class.getName());
                PersistableBundle bundle = new PersistableBundle();
                bundle.putString("lat", latitude+"");
                bundle.putString("lon", longitude+"");

                JobInfo jobInfo =
                        new JobInfo.Builder(MYJOBID, jobService).setPeriodic(10000).
                        setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY).
                        setRequiresCharging(false).
                        setRequiresDeviceIdle(false).
                        setPersisted(true).
                        setExtras(bundle).
                        build();

                int jobId = jobScheduler.schedule(jobInfo);
                if(jobScheduler.schedule(jobInfo)>0){
                }else{
                }
            }
        });
    }

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
    }


    private boolean checkPlayServices() {
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.SUCCESS) {
            if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
                GooglePlayServicesUtil.getErrorDialog(resultCode, this,
                        PLAY_SERVICES_RESOLUTION_REQUEST).show();
            } else {
                Toast.makeText(getApplicationContext(),
                        "This device is not supported.", Toast.LENGTH_LONG)
                        .show();
                finish();
            }
            return false;
        }
        return true;
    }

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

    protected void createLocationRequest(Bundle bundle) {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(10000);
        mLocationRequest.setFastestInterval(5000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, new LocationCallback() {
            @Override
            public void onLocationResult(final LocationResult locationResult) {
                latitude = locationResult.getLastLocation().getLatitude() + "";
                longitude = locationResult.getLastLocation().getLongitude() + "";
                Log.e("onLocationResult lat", latitude);
                Log.e("onLocationResult Lon", longitude);
            }

            @Override
            public void onLocationAvailability(LocationAvailability locationAvailability) {
            }
        }, null);
    }

    @Override
    public void onConnectionSuspended(int i) {
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = "
                + result.getErrorCode());
    }

    @Override
    protected void onPause() {
        super.onPause();
    }

    @Override
    protected void onResume() {
        super.onResume();
        checkPlayServices();
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mGoogleApiClient != null) {
            mGoogleApiClient.connect();
            Log.i(TAG, "mGoogleApiClient.connect()");
        }
    }

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

MyJobService class

public class MyJobService extends JobService {

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        latitude = jobParameters.getExtras().getString("lat");
        longitude = jobParameters.getExtras().getString("lon");
        Log.e("service1",latitude + "");
        Log.e("service2",longitude + "");
        return true;
    }
}

Update 1:

tried to implement the fused location in Jobservice but doesnot work

public class MyJobService extends JobService implements GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener{

    String latitude = null;
    String longitude = null;


    public MyJobService() {
    }

    @Override
    public boolean onStartJob(JobParameters jobParameters) {
        Log.d("onStart", "onStartJob() :: ");
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters jobParameters) {
        Toast.makeText(this,
                "MyJobService.onStopJob()",
                Toast.LENGTH_SHORT).show();
        return false;
    }

    //fused location provider starts

    private GoogleApiClient mGoogleApiClient;
    private final static int PLAY_SERVICES_RESOLUTION_REQUEST = 1000;
    private LocationRequest mLocationRequest;
    private static final String TAG = "zzzz";

    protected synchronized void buildGoogleApiClient() {
        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }


    private boolean checkPlayServices() {
        int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (resultCode != ConnectionResult.SUCCESS) {
            if (GooglePlayServicesUtil.isUserRecoverableError(resultCode)) {
//                GooglePlayServicesUtil.getErrorDialog(resultCode, this,
//                        PLAY_SERVICES_RESOLUTION_REQUEST).show();
                Log.e("GooglePlayServices", resultCode + "");
            } else {
                Toast.makeText(getApplicationContext(),
                        "This device is not supported.", Toast.LENGTH_LONG)
                        .show();
                stopSelf();
            }
            return false;
        }
        return true;
    }

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

    protected void createLocationRequest(Bundle bundle) {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5000);
        mLocationRequest.setFastestInterval(5000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

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

            return;
        }

        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, new LocationCallback() {
            @Override
            public void onLocationResult(final LocationResult locationResult) {
                latitude = locationResult.getLastLocation().getLatitude() + "";
                longitude = locationResult.getLastLocation().getLongitude() + "";
                Log.e("onLocationResult lat", latitude);
                Log.e("onLocationResult Lon", longitude);
            }

            @Override
            public void onLocationAvailability(LocationAvailability locationAvailability) {
            }
        }, null);
    }

    @Override
    public void onConnectionSuspended(int i) {
        mGoogleApiClient.connect();
    }

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = "
                + result.getErrorCode());
    }
// fused location provider ends
}

Upvotes: 3

Views: 5080

Answers (3)

Idhanosi Yerimah
Idhanosi Yerimah

Reputation: 121

Building on the updates and previous answers, I was finally able to get it working.

By calling fusedLocationProvider to getLastKnownLocation in the onStartJob of my job scheduler.

See the following code

ComponentName jobServiceComponent = new ComponentName(context, LocationJobService.class);
    JobInfo.Builder jobInfoBuilder = new JobInfo.Builder(0, jobServiceComponent);
    jobInfoBuilder.setPeriodic(Constants.JOB_SERVICE_INTERVAL);
    jobInfoBuilder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY);
    jobInfoBuilder.setRequiresCharging(false);
    jobInfoBuilder.setRequiresDeviceIdle(false);
    jobInfoBuilder.setPersisted(true);

    JobScheduler scheduler = (JobScheduler) context.getSystemService(JOB_SCHEDULER_SERVICE);
    int resultCode = scheduler.schedule(jobInfoBuilder.build());

And to start this job scheduler

@Override
public boolean onStartJob(JobParameters params) {
    updateBackgroundLocation(params);
    return true;
}

private void updateBackgroundLocation(JobParameters params) {

    if (jobCancelled) { return; }
    new Thread(new Runnable() {
        @Override
        public void run() {
            updateLocation(params);
        }
    }).start();

}

private void updateLocation(JobParameters params) {

    LocationServices.getFusedLocationProviderClient(getApplicationContext())
            .getLastLocation().addOnSuccessListener(location -> {
                //perform your update here with last known location.

    }).addOnFailureListener(e -> {
        e.printStackTrace();
    });
    jobFinished(params, true);
}

@Override
public boolean onStopJob(JobParameters params) {
    jobCancelled = true;
    return false;
}

Upvotes: 1

Alex
Alex

Reputation: 31

I don't know if you are still trying to solve this issue, but the problem is the job scheduler always gets the wrong data when the app is destroyed (old data). The solution might be to buffer sqlite database on your phone. Because the database isn't wiped when destroying the app, you can put your newest GPS data into the database and then let the scheduler take it from there. It should work just fine.

please be kind, its my first time trying to help ;)

Upvotes: 3

Dhaval Solanki
Dhaval Solanki

Reputation: 4705

You need to put your fused location service logic in Job service, Because when you close your app your location listener also destroyed, So you need to get new location in your job service when your app is not available in foreground or background, When your job service called your decided time you need to implement logic of getting location, Also logic of update location,

You can get location by using last known location.

Upvotes: 2

Related Questions