Объединенный провайдер местоположения с JobScheduler

У меня есть объединенный код провайдера местоположения в классе MainActivity, предоставляющий значения широты и долготы, которые передаются в класс JobService с помощью persistableBundle. Он отлично работает, когда пользователь использует приложение (т.е. приложение находится на переднем плане). Как только приложение удаляется или уничтожается, последнее обновленное значение из MainActivity постоянно загружается через планировщик заданий (т. е. планировщик заданий все время получает одно и то же значение, поставщик объединенного местоположения не работает). Что мне сделать, чтобы оно работало, даже если приложение не на переднем плане? (PS. Это работает, когда приложение свернуто. То есть его можно увидеть в списке последних приложений, но проблема возникает, как только оно удаляется из списка)

MainActivity.класс

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

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;
    }
}

Обновление 1:

пытался реализовать плавное местоположение в Jobservice, но не работает

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
}

person Amrita Stha    schedule 11.09.2017    source источник


Ответы (4)


Я не знаю, пытаетесь ли вы решить эту проблему, но проблема в том, что планировщик заданий всегда получает неправильные данные при уничтожении приложения (старые данные). Решением может быть буферизация базы данных sqlite на вашем телефоне. Поскольку база данных не стирается при уничтожении приложения, вы можете поместить свои самые новые данные GPS в базу данных, а затем позволить планировщику взять их оттуда. Он должен работать нормально.

Пожалуйста, будьте добры, я первый раз пытаюсь помочь ;)

person Alex    schedule 18.03.2019

Вам нужно поместить логику службы плавного определения местоположения в службу работы, потому что, когда вы закрываете свое приложение, ваш прослушиватель местоположения также уничтожается, поэтому вам нужно получить новое местоположение в вашей службе работы, когда ваше приложение недоступно на переднем плане или в фоновом режиме, когда ваша работа сервис называется выбранное вами время, когда вам нужно реализовать логику получения местоположения, а также логику обновления местоположения,

Вы можете получить местоположение, используя последнее известное местоположение.

person Dhaval Solanki    schedule 11.09.2017
comment
да, я уже пробовал это, но это не работает. Посмотрите на обновление 1 в приведенном выше вопросе. - person Amrita Stha; 11.09.2017
comment
Я продублировал вопрос здесь: stackoverflow.com/questions/46153025/ - person Amrita Stha; 11.09.2017
comment
На самом деле, я пытался, но я могу решить эту проблему, у меня та же проблема с обновлением местоположения через определенный интервал, но я могу получить. Поэтому я использовал одну стороннюю библиотеку, которая помогает мне, поэтому, пожалуйста, попробуйте. github.com/mcharmas/Android-ReactiveLocation - person Dhaval Solanki; 11.09.2017

Используйте этот сервис для непрерывного определения местоположения, даже если приложение находится в фоновом режиме. Используйте этот класс вместо вашего Jobscheduler

    public class LocationTracker extends Service implements
        GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, com.google.android.gms.location.LocationListener,android.location.LocationListener {

    private GoogleApiClient mGoogleApiClient;
    private Location mLastLocation;
    private LocationRequest mLocationRequest;

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 100; // 100 meters
    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 *2; // 1 minute

    //Location Request code
    private final int REQUEST_LOCATION = 2;

    //Location manager for location services
    private LocationManager mLocationManager;


    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        getLocation();
    }


    @Override
    public void onConnected(Bundle bundle) {
        mLocationRequest = LocationRequest.create();
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest.setInterval(60000*2);// Update location every second
        mLocationRequest.setSmallestDisplacement(100);

        mLocationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

        createLocationRequest();
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
                    mGoogleApiClient);
            if (mLastLocation != null) {
            updateProviderLocation(mLastLocation);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        if (mGoogleApiClient == null) {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();


        }

        return null;
    }

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

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


    }
    private void createLocationRequest() {
        Log.i("TAG", "CreateLocationRequest");
        mLocationRequest = new LocationRequest();
        long UPDATE_INTERVAL = 60 * 1000 *2;
        mLocationRequest.setInterval(UPDATE_INTERVAL);
        long FASTEST_INTERVAL = 10000;
        mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                .addLocationRequest(mLocationRequest);
        //**************************
        builder.setAlwaysShow(true); //this is the key ingredient
        //**************************

    }
    @Override
    public void onConnectionSuspended(int i) {


    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
            mGoogleApiClient.connect();
        return super.onStartCommand(intent, flags, startId);
    }

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



    @Override
    public void onLocationChanged(Location location) {
       //Log.d("Location", location.getLatitude() + "," + location.getLongitude());
      try {


            if (mLastLocation.getLatitude()!=location.getLatitude()||mLastLocation.getLongitude()!=location.getLongitude()) {
                updateProviderLocation(location);
                mLastLocation = location;
            }



        }catch (Exception e){

      }


    }

    @Override
    public void onStatusChanged(String s, int i, Bundle bundle) {

    }

    @Override
    public void onProviderEnabled(String s) {

    }

    @Override
    public void onProviderDisabled(String s) {

    }


    private void updateProviderLocation(Location location){
       //Upload to your server

    }




    private void stopLocationUpdates() {

        try {
            if (mGoogleApiClient.isConnected())
                LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

            if (mLocationManager != null) {
                mLocationManager.removeUpdates(this);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }


    public void getLocation() {
        try {


            // getting GPS status
            Boolean isGPSEnabled = mLocationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);

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

            if (!isGPSEnabled && !isNetworkEnabled) {
                Log.e("Location", "No provider enabled");
            } else {
                if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
                }

                if (isGPSEnabled) {

                    if (mLocationManager != null) {
                        mLocationManager.requestLocationUpdates(
                                LocationManager.GPS_PROVIDER,
                                MIN_TIME_BW_UPDATES,
                                MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                        Log.d("GPS Enabled", "GPS Enabled");
                    }


                }else if (isNetworkEnabled) {
                    if (mLocationManager != null) {
                            mLocationManager.requestLocationUpdates(
                                    LocationManager.NETWORK_PROVIDER,
                                    MIN_TIME_BW_UPDATES,
                                    MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                            Log.d("Network", "Network");
                    }
                }
                // if GPS Enabled get lat/long using GPS Services

            }

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


    }



}
person Vinayak B    schedule 11.09.2017
comment
да, я использовал это, но мне нужны данные о местоположении, когда приложение также уничтожено. У меня такой же код в MainActivity. Взглянуть. Но мне это нужно через jobScheduler. И я пытался применить его в jobService, но не работает - person Amrita Stha; 11.09.2017
comment
Я продублировал вопрос здесь: stackoverflow.com/questions/46153025/ - person Amrita Stha; 11.09.2017
comment
Фоновая служба больше не поддерживается после версии Android 8.0, поэтому планировщик заданий - хороший. - person Dhaval Solanki; 11.09.2017

Основываясь на обновлениях и предыдущих ответах, я наконец смог заставить его работать.

Путем вызова fusedLocationProvider для getLastKnownLocation в onStartJob моего планировщика заданий.

См. следующий код

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());

И запустить этот планировщик заданий

@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;
}
person Idhanosi Yerimah    schedule 22.11.2019