Фоновые геозоны действительно работают на Android 8+?

1

У меня есть приложение геозоны, которое я пытаюсь портировать, чтобы начать работать на Android 8+. Я прочитал учебник по этой теме и переключился на compile 'com.google.android.gms:play-services-location:16.0.0'.

События входа геозоны никогда не запускаются, когда приложение не находится на переднем плане. Я жду больше, чем две минуты, которые, по словам доктора, могут занять. Я ждал до 15 минут безрезультатно. Как только я вывожу приложение на передний план внутри геозоны, оно сразу срабатывает.

Я понимаю, что на Android 8+ существуют ограничения на фоновые сервисы, но в руководстве Google говорится, что нужно использовать IntentService, который должен быть заблокирован на Android 8+. ОБРАТНАЯ СВЯЗЬ РЕДАКТИРОВАТЬ: вышеупомянутое руководство абсолютно неправильно. Не следуйте за этим. IntentService не будет работать.

Я попытался следовать этому ответу, в котором говорится, что использовать новый GeofencingClient чтобы настроить свои намерения для решения этой проблемы. (Я не понимаю, почему это помогло бы, потому что он все еще использует IntentService для получения событий, и он не работает в моем коде.)

Этот ответ на связанный с этим вопрос здесь предполагает, что вы должны использовать BroadcastReceiver вместо IntentService, но я не понимаю, почему это также будет иметь значение, поскольку Android 8 накладывает те же ограничения на BroadcastReceivers в фоновом режиме, что и IntentServices. ОБРАТНАЯ СВЯЗЬ РЕДАКТИРОВАТЬ: Этот связанный ответ также является правильным. Хотя неявные BroadcastReceivers ограничены, явные, которые регистрируются во время выполнения для отправки запросов в конкретный пакет, не ограничены и работают должным образом.

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

Моя настройка геозон:

googleApiClient = new GoogleApiClient.Builder(this.context)
        .addApi(LocationServices.API)
        .addConnectionCallbacks(getServiceSetup())
        .addOnConnectionFailedListener(getServiceFailureStrategy())
        .build();
geofencingClient = LocationServices.getGeofencingClient(context);

Intent intent =  new Intent(context, mIntentService);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
// calling addGeofences() and removeGeofences().
geofencePendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.
            FLAG_UPDATE_CURRENT);

geofencingClient.addGeofences(geofences, geofencePendingIntent)
                .addOnSuccessListener(new OnSuccessListener<Void>() {
                 @Override
                   public void onSuccess(Void aVoid) {
                     getAddGeofencesCallback(runnable, geofencesToAdd).onResult(new Status(0));
                     Log.e(TAG, "Successfully added geofences with GeofencingClient");
                   }
                 })
                .addOnFailureListener(new OnFailureListener() {
                   @Override
                   public void onFailure(Exception e) {
                     getAddGeofencesCallback(runnable, geofencesToAdd).onResult(new Status(1));
                     Log.e(TAG, "Failed to add geofences with GeofencingClient", e);

IntentService:

public class GeofenceService extends IntentService {
    private static final String TAG = "GeofenceService";

    public GeofenceService() {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        GeofenceTransition transition = GeofenceServiceManager.getGeofenceTransition(intent);
        if (transition == null) {
            return;
        }

        GeofenceServiceManager.logd(
                TAG,
                "onHandleIntent. geofence: " +
                        GeofenceMessage.notificationTitleFor(this, transition)
        );
        processErrors(transition);
        sendBroadcast(transition);
    }

    private Intent generateBroadcast(GeofenceTransition transition) {
        Intent broadcastIntent = new Intent();

        broadcastIntent.addCategory(GeofenceUtils.CATEGORY_GEOFENCE_SERVICES)
                       .setAction(GeofenceUtils.actionNameFor(transition))
                       .putStringArrayListExtra(
                               GeofenceUtils.GEOFENCE_IDS,
                               new ArrayList<String>(transition.getIds())
                                               );

        return broadcastIntent;
    }

    private void sendBroadcast(GeofenceTransition transition) {
        if (transition.isError() || transition.unknownType()) {
            GeofenceServiceManager.logd(TAG, "geofence transition is error or unknown type.");
            return;
        }

        broadcastManager().sendBroadcast(generateBroadcast(transition));
    }

    private LocalBroadcastManager broadcastManager() {
        return LocalBroadcastManager.getInstance(this);
    }

    private void processErrors(GeofenceTransition transition) {
        if (!transition.isError()) {
            return;
        }

        Log.e(TAG, "geofence error: "+GeofenceMessage.errorDetailsFor(this, transition));

        Intent broadcastIntent = generateBroadcast(transition);
        broadcastIntent.putExtra(
                GeofenceUtils.GEOFENCE_STATUS,
                GeofenceMessage.errorMessageFor(this, transition)
                                );
        broadcastManager().sendBroadcast(broadcastIntent);
    }
}

AndroidManifest.xml:

...
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

      <service
          android:name=".geofence.GeofenceService"
          android:enabled="true"
          android:exported="false">
      </service>
...

РЕДАКТИРОВАТЬ 1: На основании задокументированных Google ограничений на фоновую обработку Android 8+, мне кажется, что это невозможно. Android 8+ убивает запущенные приложения в течение 10 минут после входа в фоновый режим и блокирует запуск служб, в том числе намеренных, когда не на переднем плане. Тем не менее, Google говорит:

Примечание. На Android 8.0 (уровень API 26) и выше, если приложение работает в фоновом режиме и отслеживает геозону, устройство реагирует на события геозоны через каждые несколько минут. Чтобы узнать, как адаптировать ваше приложение к этим пределам ответов, см. Фоновые ограничения местоположения.

Как это возможно с учетом задокументированных пределов выполнения фона?

РЕДАКТИРОВАТЬ 2: Я видел BroadcastReceiver, объявленный в манифесте работы для запуска приложения в фоновом режиме на Android 8+, используя намеренное обнаружение Bluetooth LE. Этот код выглядит так:

Intent intent = new Intent();
intent.setComponent(new ComponentName(context.getPackageName(), "com.example.MyBroadcastReceiver"));
<receiver android:name="com.example.MyBroadcastReceiver">
         </receiver>

Это работает для обнаружения BLE, чтобы запускать приложения в фоновом режиме. Являются ли системные цели, подобные приведенным выше, каким-то образом защищены от пределов фонового выполнения Android 8+ (в отличие от IntentServices) при доставке в BroadcastReceiver? Если да, будет ли это работать с геозонами? (Оповещение спойлера: Да, будет! Прочитайте принятый ответ ниже.)

Теги:
location
android-8.0-oreo
android-geofence
geofencing

2 ответа

1
Лучший ответ

Вам необходимо заменить:

geofencePendingIntent = PendingIntent.getService(context, 0, intent, PendingIntent.
            FLAG_UPDATE_CURRENT); 

с

Intent intent = new Intent(this, GeofenceBroadcastReceiver.class); 
mGeofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);

Для поддержки Android 8 вы должны использовать BroadcastReceiver для получения намерения геозоны, так как для пределов фонового выполнения (служба не может быть запущена в фоновом режиме)

см. для получения более подробной информации: https://github.com/googlesamples/android-play-location/commit/5f83047c8a462d7c619f6275b624e219b4622322

Пример использования Google Geofence: https://github.com/googlesamples/android-play-location/tree/master/Geofencing

  • 0
    Спасибо за предложение. Я тестирую переход на BroadcastReceiver, как вы предлагаете. Я вижу, что пример, на который вы ссылаетесь, делает это. К сожалению, на странице обучения Geofence на developer.google.com все еще говорится об использовании PendingIntent для запуска IntentService .
  • 0
    Это работает. Суть в том, что обучающая страница Google, которая говорит вам использовать PendingIntent к IntentService, указанному выше, полностью неверна. Явная регистрация BroadcastReceiver работает для запуска приложения в фоновом режиме.
1

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

Еще кое-что. Как вы тестировали геозоны?

  • 0
    Спасибо за совет о игнорировании оптимизации батареи. Я очень надеюсь, что это не нужно! Я тестирую, выводя приложение на передний план, когда он находится на расстоянии более 1 км от моей 100-метровой геозоны, затем помещаю его на задний план, нажимая кнопку «Домой», ожидая 1+ часа, затем иду к центру геозоны и ожидая до 15 минут мое локальное уведомление, которое никогда не срабатывает, даже если я освещаю экран. Когда я разблокирую телефон и вывожу приложение на передний план, оно сразу срабатывает.
  • 0
    Вы можете проверить с приложением поддельного местоположения, как Lockito. Это более стабильно, чем реальное местоположение. Вы можете получить неправильные результаты с реальным местоположением. Потому что запуск геозоны не стабилен в реальном сценарии. Вы можете быть уверены с поддельным местоположением.
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню