Android O убивает мой Сервис, как только он уходит на задний план - PullRequest
0 голосов
/ 05 июня 2018

Я прочитал много других подобных вопросов и проверил документацию по Android относительно пределов фонового выполнения (https://developer.android.com/about/versions/oreo/background#services),) и все еще не смог найти решение. Я также попробовал большинство настроек телефона.

Проблема: у меня есть приложение, которое используется для сбора данных о местоположении в фоновом режиме. Для этого я использую службу. В настоящее время она собирает данные о местоположении каждые 5 секунд. Она работает отличнона моем Nexus 5 (API 23), однако он работает только на моем Nexus 5X (API 27), когда приложение находится на переднем плане. Как только оно выходит в фоновом режиме, оно останавливается. Так что это не имеет ничего общего с долговыполнение заданий в фоновом режиме, как только я ухожу из приложения, сервис немедленно останавливается.

Вот мой код класса обслуживания:

import android.app.Service;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;

import app.khash.climbcollector.DataBase.DataContract.DataEntry;

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

private LocationRequest mLocationRequest;
private GoogleApiClient mGoogleApiClient;
private static final String TAG = GPSService.class.getSimpleName();

@Override
public void onCreate() {
    super.onCreate();
    buildGoogleApiClient();
}//onCreate


@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (!mGoogleApiClient.isConnected())
        mGoogleApiClient.connect();
    return START_STICKY;
}//onStartCommand


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

@Override
public void onConnectionSuspended(int i) {
    Log.i(TAG, "onConnectionSuspended " + i);

}//onConnectionSuspended

@Override
public void onLocationChanged(Location location) {

    //insert location to the database passing in the location object
    insertDataToDb(location);
}//onLocationChanged

//method for adding the location data to db
private void insertDataToDb(Location location) {

    final DateFormat dateFormat = new SimpleDateFormat("MM.dd.yyyy 'at' HH:mm:ss z");

    //Current date and time using the format declared at the beginning
    final String currentDateTime = dateFormat.format(Calendar.getInstance().getTime());

    double lat = location.getLatitude();
    double lng = location.getLongitude();
    double alt = location.getAltitude();

    SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
    String key = getString(R.string.route_name_intent_extra);
    String routeName = sharedPref.getString(key, "default");

    // Create a new map of values,
    ContentValues values = new ContentValues();
    values.put(DataEntry.COLUMN_DATA_LATITUDE, lat);
    values.put(DataEntry.COLUMN_DATA_LONGITUDE, lng);
    values.put(DataEntry.COLUMN_DATA_ALTITUDE, alt);
    values.put(DataEntry.COLUMN_DATA_DATE, currentDateTime);
    values.put(DataEntry.COLUMN_DATA_ROUTE_NAME, routeName);


    // Insert a new location into the provider, returning the content URI for the new location.
    Uri newUri = getContentResolver().insert(DataEntry.CONTENT_URI, values);

    // Show a toast message depending on whether or not the insertion was successful
    if (newUri == null) {
        // If the new content URI is null, then there was an error with insertion.
        Log.v(TAG, "error saving data");
    } else {
        //since the insert method return the Uri of the row created, we can extract the ID of
        //the new row using the parseID method with our newUri as an input. This method gets the
        //last segment of the Uri, which is our new ID in this case and we store it in an object
        // And add it to the confirmation method.
        String id = String.valueOf(ContentUris.parseId(newUri));
        // Otherwise, the insertion was successful and we can log
        Log.v(TAG, "Successfully added: " + id);
    }

}//insertDataToDb

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

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    Log.i(TAG, "onConnectionFailed ");

}//onConnectionFailed

private void initLocationRequest() {
    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(5000);
    mLocationRequest.setFastestInterval(2000);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

}//initLocationRequest

private void startLocationUpdate() {
    initLocationRequest();

    //check for location permission
    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;
    }//check permission

    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}//startLocationUpdate

private void stopLocationUpdate() {
    LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}

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

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

    mGoogleApiClient.disconnect();
}//onDestroy


}//GPSService

Я звоню в службу со своего основногодействие с использованием этого:

Intent serviceStartIntent = new Intent(this, GPSService.class);
startService(serviceStartIntent);

И остановите его, используя этот код:

Intent serviceStopIntent = new Intent(this, GPSService.class);
stopService(serviceStopIntent);

Вот код, который я пытался использовать JobScheduler.

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

    private String TAG = GPSJobService.class.getSimpleName();
    private LocationRequest mLocationRequest;
    private GoogleApiClient mGoogleApiClient;

    private JobParameters mParam;


    @Override
    public void onCreate() {
        Log.v(TAG, "onCreate called");
        super.onCreate();
        buildGoogleApiClient();
    }//onCreate

    @Override
    public void onDestroy() {
        Log.v(TAG, "onDestroy called");
        super.onDestroy();
        mGoogleApiClient.disconnect();
    }//onDestroy

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.v(TAG, "onStartCommand called");
        if (!mGoogleApiClient.isConnected())
            mGoogleApiClient.connect();
        return START_STICKY;
    }//onStartCommand

    @Override
    public boolean onStartJob(JobParameters params) {
        Log.v(TAG, "onStartJob called");

        if (!mGoogleApiClient.isConnected())
            mGoogleApiClient.connect();

        mParam = params;

        return true;
    }//onStartJob

    @Override
    public void onLocationChanged(Location location) {
        //insert location to the database passing in the location object
        insertDataToDb(location);
    }//onLocationChanged

    private void initLocationRequest() {
        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(5000);
        mLocationRequest.setFastestInterval(2000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

    }//initLocationRequest

    private void startLocationUpdate() {
        initLocationRequest();

        //check for location permission
        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;
        }//check permission

        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    }//startLocationUpdate

    //method for adding the location data to db
    private void insertDataToDb(Location location) {

        final DateFormat dateFormat = new SimpleDateFormat("MM.dd.yyyy 'at' HH:mm:ss z");

        //Current date and time using the format declared at the beginning
        final String currentDateTime = dateFormat.format(Calendar.getInstance().getTime());

        double lat = location.getLatitude();
        double lng = location.getLongitude();
        double alt = location.getAltitude();

        SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
        String key = getString(R.string.route_name_intent_extra);
        String routeName = sharedPref.getString(key, "default");

        // Create a new map of values,
        ContentValues values = new ContentValues();
        values.put(DataEntry.COLUMN_DATA_LATITUDE, lat);
        values.put(DataEntry.COLUMN_DATA_LONGITUDE, lng);
        values.put(DataEntry.COLUMN_DATA_ALTITUDE, alt);
        values.put(DataEntry.COLUMN_DATA_DATE, currentDateTime);
        values.put(DataEntry.COLUMN_DATA_ROUTE_NAME, routeName);


        // Insert a new location into the provider, returning the content URI for the new location.
        Uri newUri = getContentResolver().insert(DataEntry.CONTENT_URI, values);

        // Show a toast message depending on whether or not the insertion was successful
        if (newUri == null) {
            // If the new content URI is null, then there was an error with insertion.
            Log.v(TAG, "error saving data");
        } else {
            //since the insert method return the Uri of the row created, we can extract the ID of
            //the new row using the parseID method with our newUri as an input. This method gets the
            //last segment of the Uri, which is our new ID in this case and we store it in an object
            // And add it to the confirmation method.
            String id = String.valueOf(ContentUris.parseId(newUri));
            // Otherwise, the insertion was successful and we can log
            Log.v(TAG, "Successfully added: " + id);
            //finish the job
            jobFinished(mParam, true);
        }

    }//insertDataToDb

    @Override
    public boolean onStopJob(JobParameters params) {
        Log.v(TAG, "onStopJob called");
        return false;
    }//onStopJob

    @Override
    public void onConnected(@Nullable Bundle bundle) {
        startLocationUpdate();
    }//onConnected

    @Override
    public void onConnectionSuspended(int i) {
        Log.v(TAG, "onConnectionSuspended called");
    }//onConnectionSuspended

    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        Log.v(TAG, "onConnectionFailed called");
    }//onConnectionFailed

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

Я называю это в своей основной деятельности следующим образом:

            case R.id.bttn_job_start:
                ComponentName serviceComponent = new ComponentName(this, GPSJobService.class);

                JobInfo.Builder builder = new JobInfo.Builder(mJobId, serviceComponent);

                builder.setMinimumLatency(5000);

                builder.setBackoffCriteria(5000, JobInfo.BACKOFF_POLICY_LINEAR);

                //repeat every 5 seconds
//                builder.setPeriodic(5000);

                JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);

                jobScheduler.schedule(builder.build());

                Toast.makeText(this, "Started", Toast.LENGTH_SHORT).show();


                break;

Я пытался либо установить критерии задержки и отката, либо установить их как периодические (закомментировано прямо сейчас).Оба метода работают, пока приложение находится на переднем плане.Ни одна из них не работает, когда приложение переходит в фоновый режим.

Как я упоминал ранее, оно отлично работает на моем Nexus 5, но не на Nexus 5X.

Любые предложения о том, как я могу решить эту проблему?

Спасибо,

1 Ответ

0 голосов
/ 05 июня 2018

Это ведет себя как ожидалось.Начиная с Android o, когда приложение находится на переднем плане, оно может свободно создавать и запускать как передний, так и фоновый сервисы.Когда приложение переходит в фоновый режим, оно имеет окно в несколько минут (по моим наблюдениям оно составляет около 1–2 минут), в котором ему по-прежнему разрешено создавать и использовать службы.Система останавливает фоновые службы приложения, как если бы приложение вызывало методы Service.stopSelf() служб.

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

Если задание необходимо выполнить в более поздний момент времени, когда соблюдены определенные ограничения, рассмотрите возможность использования JobScheduler или WorkManager .

...