locationListener не работает через 30 секунд на службе переднего плана - PullRequest
1 голос
/ 07 ноября 2019

Я создал службу, которая находит, а затем сохраняет координаты пользователя в базе данных SQLite.

public class GPS_Service extends Service {

DatabaseHelper myDb;

private LocationListener locationListener;
private LocationManager locationManager;

private String latitude, longitude;

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

    myDb = new DatabaseHelper(this);

}

@SuppressLint("MissingPermission")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,0, notificationIntent, 0);

    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Service")
            .setContentText("Coordinates Location Running")
            .setContentIntent(pendingIntent)
            .build();

    startForeground(1, notification);

    locationListener = new LocationListener() {

        @Override
        public void onLocationChanged(Location location) {

            Log.d("myTag", "Hello");

            latitude = String.valueOf(location.getLatitude());
            longitude = String.valueOf(location.getLongitude());

            insertCoordinates(latitude, longitude);

            Intent i = new Intent("location_update");
            i.putExtra("latitude", latitude);
            i.putExtra("longitude",longitude);

            sendBroadcast(i);

        }

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

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

            Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);

        }

    };

    locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 0, locationListener);

    return START_NOT_STICKY;

}

@Override
public void onDestroy() {

    super.onDestroy();

    if(locationManager != null)
        locationManager.removeUpdates(locationListener);

}

private void insertCoordinates(String latitude, String longitude) {

    boolean inserted = myDb.insertData(latitude, longitude); //Insert coordinates

    //Check if insertion is completed
    if(inserted)
        Toast.makeText(GPS_Service.this, "Coordinates Inserted", Toast.LENGTH_SHORT).show();
    else
        Toast.makeText(GPS_Service.this, "Coordinates Not Inserted", Toast.LENGTH_SHORT).show();

}

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

}

Я могу запустить или остановить службу из MainActivity, например:

private void enable_buttons() {

    buttonStartService.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Intent serviceIntent = new Intent(getApplicationContext(), GPS_Service.class);

            //Checks if the SDK version is higher than 26 to act accordingly
            ContextCompat.startForegroundService(MainActivity.this, serviceIntent);

        }
    });

    buttonStopService.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Intent serviceIntent = new Intent(MainActivity.this, GPS_Service.class);
            stopService(serviceIntent);

        }
    });

}

Проблема в том, что когда я запускаю эту службу, если я полностью закрываю приложение или оставляю его в фоновом режиме, locationListener будет работать в течение 30 секунд, а затем остановится. Если я снова открою приложение, служба продолжит работать с того места, где оно остановилось. Также я проверил в опциях разработчика, работает ли сервис, и это действительно так, хотя locationListener не выдает ожидаемых результатов. Есть идеи?

Ответы [ 2 ]

0 голосов
/ 09 ноября 2019

TL; DR:

Добавьте android:foregroundServiceType="location" к вашей Service's заявленной декларации.

ОБЪЯСНЕНИЕ

Это новое поведение для Android 10 точно такое же, как вы описали: несмотря на то, что вы можете использовать службу переднего плана, через 30 секунд после того, как ваше приложение покидает экран - обновления местоположения прекращаются.

Возможно, вызаметил, что устройства Android 10 предоставляют пользователю два новых выбора при предоставлении разрешений на местоположение (для устаревших приложений (API <29) или приложений, которые объявляют разрешение <code>ACCESS_BACKGROUND_LOCATION):

  • "Разрешить всеtime "
  • " Разрешить только во время использования этого приложения "

" Разрешить только во время использования этого приложения "фактически означает" Разрешить, пока приложение видно на экране ». Это потому, что у пользователя теперь есть возможность выборочного удаления доступа к местоположению - даже к приоритетному сервису - на основе этих критериев. Пользователи могут изменить этот параметр в любое время , даже если ваше приложение работает.

Документы Android объясняют, что решение android:foregroundServiceType="location" предназначено для вашеготочный вариант использования: приложения, подобные "Google Maps", которые имеют службу переднего плана, но, как ожидается, продолжат обработку данных о местоположении, если пользователь переключится на другое приложение. Этот метод называется «продолжение действия, инициированного пользователем», и он позволяет получать обновления местоположения даже после помещения приложения в «фон».

(Документы, похоже, расширяют определениетермин «фон», здесь. В прошлом, если у вас была служба переднего плана, ваше приложение рассматривалось «на переднем плане» - по крайней мере, для целей приоритета задач, Doze и т. д. Теперь кажется, что приложениесчитается "в фоновом режиме", что касается доступа к местоположению, если он не был на экране в течение последних 30 секунд.)

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

ДРУГИЕ РЕШЕНИЯ ДЛЯ УСТРОЙСТВ ANDROID 10

В качестве альтернативы, вы могли бы объявить targetSdkVersion из 28или меньше, что позволит вашему приложению функционировать в «режиме совместимости».

У вас также есть возможность получить разрешение ACCESS_BACKGROUND_LOCATION, но docs предостережение против этого:

Если ваше приложение не требует доступа к местоположению при работе в фоновом режиме, настоятельно рекомендуется не запрашивать ACCESS_BACKGROUND_LOCATION ...

Этот подход нетребуется, для вашего случая использования, потому что ваш Activity используется для запуска вашего Service;Вы можете быть уверены, что ваше приложение было на экране хотя бы один раз, прежде чем Service начнет получать фоновые обновления местоположения. (По крайней мере, я предполагаю , что именно так ОС определяет начало «действия, инициированного пользователем». Предположительно, подход foregroundServiceType не будет работать, если вы запускаете Serviceот JobScheduler, или BroadcastReceiver, или чего-то еще.)

PS: Держитесь за этот код WakeLock. Вам нужно будет держать устройство в активном состоянии, если вы хотите получать обновления с постоянной скоростью 10 секунд.

0 голосов
/ 07 ноября 2019

Я действительно не вижу никаких проблем в коде, но я немного скептически отношусь к START_NOT_STICKY. Вместо этого попробуйте START_STICKY.

START_STICKY

Если процесс этой службы завершается во время ее запуска (после возврата из onStartCommand (Intent, int, int)), затем оставьте его вначальное состояние, но не сохраняйте это поставленное намерение. Позже система попытается заново создать сервис. Поскольку он находится в запущенном состоянии, он гарантирует вызов onStartCommand (Intent, int, int) после создания нового экземпляра службы;если нет ожидающих команд пуска, которые должны быть доставлены в службу, она будет вызываться с объектом с нулевым намерением, поэтому необходимо проверить это.

START_NOT_STICKY

Если процесс этой службы завершается во время ее запуска (после возврата из onStartCommand (Intent, int, int)), и нет новых намерений запуска, чтобы доставить его, затем выведите службу из состояния запуска ине создавайте заново до будущего явного вызова Context.startService (Intent). Служба не получит вызов onStartCommand (Intent, int, int) с нулевым Intent, поскольку он не будет перезапущен, если нет ожидающих Intents для доставки.

По мере того, как вы возвращаетесьSTART_NOT_STICKY, если процесс завершен, onStartCommand () больше не будет вызываться, где вы инициализируете и слушателя, и locationManager.

...