Tworzenie i monitorowanie geofencingu

Funkcja geofencingu łączy informacje o bieżącej lokalizacji użytkownika ze świadomością odległości użytkownika od lokalizacji, które mogą być interesujące. Aby oznaczyć interesującą lokalizację, podaj jej szerokość i długość geograficzną. Aby dostosować odległość od lokalizacji, dodaj promień. Szerokość, długość i promień wyznaczają geofencing, czyli ogrodzenie wokół interesującej Cię lokalizacji.

Możesz mieć wiele aktywnych geofencingu, z limitem 100 na aplikację na użytkownika urządzenia. W przypadku każdego geofencingu możesz poprosić usługi lokalizacyjne o wysyłanie zdarzeń wejścia i wyjścia. Możesz też określić w obszarze geofencingu czas oczekiwania (lub siedlisko) przed uruchomieniem zdarzenia. Możesz ograniczyć czas trwania geofencingu, podając czas wygaśnięcia w milisekundach. Po wygaśnięciu geofencingu usługi lokalizacyjne zostaną automatycznie usunięte.

Z tej lekcji dowiesz się, jak dodawać i usuwać geofencing, a następnie nasłuchiwać przejść geofencingu za pomocą interfejsu BroadcastReceiver.

Skonfiguruj monitorowanie geofencingu

Pierwszym krokiem w żądaniu monitorowania geofencingu jest prośba o przyznanie niezbędnych uprawnień. Aby korzystać z geofencingu, aplikacja musi żądać:

Więcej informacji znajdziesz w przewodniku dotyczącym prośby o dostęp do lokalizacji.

Jeśli chcesz używać BroadcastReceiver do nasłuchiwania przejść geofencingu, dodaj element zawierający nazwę usługi. Ten element musi być elementem podrzędnym elementu <application>:

<application
   android:allowBackup="true">
   ...
   <receiver android:name=".GeofenceBroadcastReceiver"/>
<application/>

Aby uzyskać dostęp do interfejsów API lokalizacji, musisz utworzyć instancję klienta Geofencing. Aby dowiedzieć się, jak nawiązać połączenie z klientem:

Kotlin

lateinit var geofencingClient: GeofencingClient

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this)
}

Java

private GeofencingClient geofencingClient;

@Override
public void onCreate(Bundle savedInstanceState) {
    // ...
    geofencingClient = LocationServices.getGeofencingClient(this);
}

Tworzenie i dodawanie geofencingu

Twoja aplikacja musi tworzyć i dodawać geofencing za pomocą klasy konstruktora interfejsu Location API do tworzenia obiektów Geofence oraz klasy wygody do ich dodawania. Aby obsługiwać intencje wysyłane z usług lokalizacyjnych w momencie przenoszenia geofencingu, możesz zdefiniować PendingIntent zgodnie z instrukcjami w tej sekcji.

Uwaga: na urządzeniach z jednym użytkownikiem obowiązuje limit 100 geofencingu na aplikację. W przypadku urządzeń z wieloma użytkownikami obowiązuje limit 100 geofencingu na aplikację na użytkownika urządzenia.

Tworzenie obiektów geofence

Najpierw użyj funkcji Geofence.Builder, aby utworzyć geofence i ustawić odpowiedni promień, czas trwania oraz typy przejść. Aby np. wypełnić obiekt listy:

Kotlin

geofenceList.add(Geofence.Builder()
        // Set the request ID of the geofence. This is a string to identify this
        // geofence.
        .setRequestId(entry.key)

        // Set the circular region of this geofence.
        .setCircularRegion(
                entry.value.latitude,
                entry.value.longitude,
                Constants.GEOFENCE_RADIUS_IN_METERS
        )

        // Set the expiration duration of the geofence. This geofence gets automatically
        // removed after this period of time.
        .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)

        // Set the transition types of interest. Alerts are only generated for these
        // transition. We track entry and exit transitions in this sample.
        .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER or Geofence.GEOFENCE_TRANSITION_EXIT)

        // Create the geofence.
        .build())

Java

geofenceList.add(new Geofence.Builder()
    // Set the request ID of the geofence. This is a string to identify this
    // geofence.
    .setRequestId(entry.getKey())

    .setCircularRegion(
            entry.getValue().latitude,
            entry.getValue().longitude,
            Constants.GEOFENCE_RADIUS_IN_METERS
    )
    .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
    .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
            Geofence.GEOFENCE_TRANSITION_EXIT)
    .build());

Ten przykład pobiera dane z pliku ze stałymi. W praktyce aplikacje mogą dynamicznie tworzyć geofencing na podstawie lokalizacji użytkownika.

Określanie geofencingu i aktywatorów początkowych

Ten fragment kodu używa klasy GeofencingRequest i jej zagnieżdżonej klasy GeofencingRequestBuilder do określania geofencingu i wywoływania powiązanych zdarzeń geofencingu:

Kotlin

private fun getGeofencingRequest(): GeofencingRequest {
    return GeofencingRequest.Builder().apply {
        setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
        addGeofences(geofenceList)
    }.build()
}

Java

private GeofencingRequest getGeofencingRequest() {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    builder.addGeofences(geofenceList);
    return builder.build();
}

Ten przykład pokazuje użycie 2 reguł geofencingu. Przejście GEOFENCE_TRANSITION_ENTER jest aktywowane, gdy urządzenie znajdzie się w strefie geofencingu, a przejście GEOFENCE_TRANSITION_EXIT – gdy urządzenie opuści geofencing. Podanie INITIAL_TRIGGER_ENTER informuje usługi lokalizacyjne, że funkcja GEOFENCE_TRANSITION_ENTER powinna zostać aktywowana, jeśli urządzenie znajduje się już wewnątrz geofencingu.

W wielu przypadkach lepiej użyć elementu INITIAL_TRIGGER_DWELL, który wywołuje zdarzenia tylko wtedy, gdy użytkownik zatrzymuje się na określony czas w obrębie geofencingu. To podejście może pomóc ograniczyć „alert spamowy” spowodowany generowaniem dużej liczby powiadomień, które są wysyłane, gdy urządzenie na krótko wchodzi lub opuszcza granice geograficzne. Kolejną strategią pozwalającą uzyskać jak najlepsze wyniki geofencingu jest ustawienie minimalnego promienia 100 metrów. Pomaga to uwzględnić dokładność lokalizacji typowych sieci Wi-Fi, a także zmniejsza zużycie energii przez urządzenia.

Definiowanie odbiornika dla przejść geofencingu

Wiadomość Intent wysłana z usług lokalizacyjnych może wywoływać różne działania w aplikacji, ale nie powinno się rozpoczynać działania ani fragmentu, ponieważ komponenty powinny być widoczne tylko w odpowiedzi na działanie użytkownika. W wielu przypadkach użycie geofencingu to BroadcastReceiver dobry sposób. BroadcastReceiver otrzymuje aktualizacje po wystąpieniu zdarzenia, na przykład przejście do geofencingu lub z niego wyjść, i może rozpocząć długotrwałą pracę w tle.

Ten fragment kodu pokazuje, jak zdefiniować element PendingIntent, który zaczyna się od BroadcastReceiver:

Kotlin

class MainActivity : AppCompatActivity() {

    // ...

    private val geofencePendingIntent: PendingIntent by lazy {
        val intent = Intent(this, GeofenceBroadcastReceiver::class.java)
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
        // addGeofences() and removeGeofences().
        PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    // ...

    private PendingIntent getGeofencePendingIntent() {
        // Reuse the PendingIntent if we already have it.
        if (geofencePendingIntent != null) {
            return geofencePendingIntent;
        }
        Intent intent = new Intent(this, GeofenceBroadcastReceiver.class);
        // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
        // calling addGeofences() and removeGeofences().
        geofencePendingIntent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.
                FLAG_UPDATE_CURRENT);
        return geofencePendingIntent;
    }

Dodaj geofence

Aby dodać geofencing, użyj metody GeofencingClient.addGeofences(). Podaj obiekt GeofencingRequest i PendingIntent. Ten fragment kodu pokazuje przetwarzanie wyników:

Kotlin

geofencingClient?.addGeofences(getGeofencingRequest(), geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences added
        // ...
    }
    addOnFailureListener {
        // Failed to add geofences
        // ...
    }
}

Java

geofencingClient.addGeofences(getGeofencingRequest(), getGeofencePendingIntent())
        .addOnSuccessListener(this, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Geofences added
                // ...
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Failed to add geofences
                // ...
            }
        });

Obsługuj przenoszenie geofencingu

Gdy usługi lokalizacyjne wykryje, że użytkownik wszedł do geofencingu lub z niego zamknął, wysyła obiekt Intent znajdujący się w elemencie PendingIntent zawartym w prośbie o dodanie geofencingu. Odbiornik transmisji, taki jak GeofenceBroadcastReceiver, zauważa, że funkcja Intent została wywołana, i może następnie uzyskać zdarzenie geofencingu z intencji, określić typ przejść geofencingu i określić, które ze zdefiniowanych geofencingu zostały aktywowane. Odbiornik może poinstruować aplikację, aby rozpoczęła pracę w tle lub, w razie potrzeby, wysłać powiadomienie jako dane wyjściowe.

Uwaga: w Androidzie 8.0 (poziom interfejsu API 26) i nowszych, jeśli aplikacja działa w tle podczas monitorowania geofencingu, urządzenie odpowiada na zdarzenia geofencingu co kilka minut. Informacje o tym, jak dostosować aplikację do tych limitów odpowiedzi, znajdziesz w sekcji Limity lokalizacji w tle.

Fragment kodu poniżej pokazuje, jak zdefiniować element BroadcastReceiver, który publikuje powiadomienie, gdy następuje przejście geofencingu. Gdy użytkownik kliknie powiadomienie, pojawi się główna aktywność w aplikacji:

Kotlin

class GeofenceBroadcastReceiver : BroadcastReceiver() {
    // ...
    override fun onReceive(context: Context?, intent: Intent?) {
        val geofencingEvent = GeofencingEvent.fromIntent(intent)
        if (geofencingEvent.hasError()) {
            val errorMessage = GeofenceStatusCodes
                    .getStatusCodeString(geofencingEvent.errorCode)
            Log.e(TAG, errorMessage)
            return
        }

        // Get the transition type.
        val geofenceTransition = geofencingEvent.geofenceTransition

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER |
                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            val triggeringGeofences = geofencingEvent.triggeringGeofences

            // Get the transition details as a String.
            val geofenceTransitionDetails = getGeofenceTransitionDetails(
                    this,
                    geofenceTransition,
                    triggeringGeofences
            )

            // Send notification and log the transition details.
            sendNotification(geofenceTransitionDetails)
            Log.i(TAG, geofenceTransitionDetails)
        } else {
            // Log the error.
            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                    geofenceTransition))
        }
    }
}

Java

public class GeofenceBroadcastReceiver extends BroadcastReceiver {
    // ...
    protected void onReceive(Context context, Intent intent) {
        GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
        if (geofencingEvent.hasError()) {
            String errorMessage = GeofenceStatusCodes
                    .getStatusCodeString(geofencingEvent.getErrorCode());
            Log.e(TAG, errorMessage);
            return;
        }

        // Get the transition type.
        int geofenceTransition = geofencingEvent.getGeofenceTransition();

        // Test that the reported transition was of interest.
        if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

            // Get the geofences that were triggered. A single event can trigger
            // multiple geofences.
            List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();

            // Get the transition details as a String.
            String geofenceTransitionDetails = getGeofenceTransitionDetails(
                    this,
                    geofenceTransition,
                    triggeringGeofences
            );

            // Send notification and log the transition details.
            sendNotification(geofenceTransitionDetails);
            Log.i(TAG, geofenceTransitionDetails);
        } else {
            // Log the error.
            Log.e(TAG, getString(R.string.geofence_transition_invalid_type,
                    geofenceTransition));
        }
    }
}

Po wykryciu zdarzenia przejścia za pomocą interfejsu PendingIntent BroadcastReceiver uzyskuje typ przejścia geofencingu i sprawdza, czy jest to jedno ze zdarzeń używanych przez aplikację do aktywowania powiadomień – w tym przypadku GEOFENCE_TRANSITION_ENTER lub GEOFENCE_TRANSITION_EXIT. Następnie usługa wysyła powiadomienie i rejestruje szczegóły przeniesienia.

Wyłącz monitorowanie geofencingu

Wyłączenie monitorowania geofencingu, gdy nie jest już potrzebne lub pożądane może pomóc oszczędzać baterię i cykle procesora na urządzeniu. Możesz wyłączyć monitorowanie geofencingu w głównej aktywności używanej do dodawania i usuwania geofencingu. Usunięcie geofencingu spowoduje jego natychmiastowe zatrzymanie. Interfejs API udostępnia metody usuwania geofencingu według identyfikatorów żądań lub usuwania geofencingu powiązanego z danym PendingIntent.

Ten fragment kodu usuwa geofence do PendingIntent, zatrzymując wszystkie kolejne powiadomienia, gdy urządzenie wejdzie na wcześniej dodane granice geograficzne lub je z niego wyjdzie:

Kotlin

geofencingClient?.removeGeofences(geofencePendingIntent)?.run {
    addOnSuccessListener {
        // Geofences removed
        // ...
    }
    addOnFailureListener {
        // Failed to remove geofences
        // ...
    }
}

Java

geofencingClient.removeGeofences(getGeofencePendingIntent())
        .addOnSuccessListener(this, new OnSuccessListener<Void>() {
            @Override
            public void onSuccess(Void aVoid) {
                // Geofences removed
                // ...
            }
        })
        .addOnFailureListener(this, new OnFailureListener() {
            @Override
            public void onFailure(@NonNull Exception e) {
                // Failed to remove geofences
                // ...
            }
        });

Geofencing można połączyć z innymi funkcjami rozpoznającymi lokalizację, takimi jak okresowe aktualizacje lokalizacji. Więcej informacji znajdziesz w innych lekcjach w ramach tych zajęć.

Korzystanie ze sprawdzonych metod geofencingu

W tej sekcji znajdziesz zalecenia dotyczące korzystania z geofencingu w interfejsach API lokalizacji na Androida.

Zmniejsz zużycie energii

Aby zoptymalizować zużycie energii w aplikacjach korzystających z geofencingu, możesz używać tych metod:

  • Ustaw wydajność powiadomień na wyższą wartość. Zwiększa to zużycie energii przez wydłużenie czasu oczekiwania na alerty geofencingu. Jeśli np. ustawisz czas reakcji na 5 minut, aplikacja będzie sprawdzać alerty przy wejściu lub wyjściu co 5 minut. Ustawienie niższych wartości nie oznacza, że użytkownicy zostaną powiadomieni w tym przedziale czasu (np. jeśli ustawisz wartość na 5 sekund, otrzymanie alertu może potrwać trochę dłużej).

  • Ustaw większy promień geofencingu w przypadku lokalizacji, w których użytkownik spędza dużo czasu, np. w domu lub w pracy. Większy promień nie ogranicza bezpośrednio zużycia energii, ale zmniejsza częstotliwość, z jaką aplikacja sprawdza wejścia lub wyjścia, i skutecznie zmniejsza ogólne zużycie energii.

Wybierz optymalny promień dla geofencingu

Aby uzyskać najlepsze wyniki, minimalny promień geofencingu powinien wynosić 100–150 metrów. Gdy sieć Wi-Fi jest dostępna, dokładność lokalizacji wynosi zwykle 20–50 metrów. Jeśli dostępna jest funkcja lokalizacji wewnętrznych, dokładność może wynosić maksymalnie 5 metrów. Jeśli nie wiesz, że lokalizacja wewnątrz geofencingu jest dostępna, załóżmy, że dokładność lokalizacji na podstawie sieci Wi-Fi wynosi około 50 metrów.

Gdy sieć Wi-Fi jest niedostępna (np. kiedy poruszasz się po obszarach wiejskich), dokładność lokalizacji spada. Zakres dokładności może wynosić od kilkuset do kilku kilometrów. W takich przypadkach musisz utworzyć granice geograficzne, korzystając z większego promienia.

Wyjaśnij użytkownikom, dlaczego Twoja aplikacja używa geofencingu

Aplikacja uzyskuje dostęp do lokalizacji w tle, gdy używasz geofencingu, dlatego zastanów się, w jaki sposób aplikacja przynosi korzyści użytkownikom. Wyjaśnij im, dlaczego aplikacja potrzebuje tego dostępu, aby zwiększyć przejrzystość i zrozumieć.

Więcej informacji o sprawdzonych metodach uzyskiwania dostępu do lokalizacji, w tym geofencingu, znajdziesz na stronie ze sprawdzonymi metodami ochrony prywatności.

Użyj typu przejścia w czasie oczekiwania, aby ograniczyć spam w alertach

Jeśli podczas krótkiej przejażdżki obok geofencingu otrzymujesz dużą liczbę alertów, najlepszym sposobem na ich zmniejszenie jest użycie typu przejścia GEOFENCE_TRANSITION_DWELL zamiast GEOFENCE_TRANSITION_ENTER. Dzięki temu alert dotyczący mieszkania będzie wysyłany tylko wtedy, gdy użytkownik zatrzyma się w obrębie geofencingu na określony czas. Czas trwania możesz wybrać, ustawiając opóźnienie związane z przedłużaniem się.

Zarejestruj ponownie geofencing tylko wtedy, gdy jest to wymagane

Zarejestrowane geofencing są przechowywane w procesie com.google.process.location należącym do pakietu com.google.android.gms. Nie musi ona obsługiwać tych zdarzeń, ponieważ po nich system przywraca geofencing:

  • Usługi Google Play zostały uaktualnione.
  • Usługi Google Play są zamykane i ponownie uruchamiane przez system z powodu ograniczenia zasobów.
  • Proces lokalizacji ulega awarii.

Aplikacja musi ponownie zarejestrować geofence, jeśli nadal są potrzebne po tych zdarzeniach, ponieważ system nie może przywrócić geofencingu w tych przypadkach:

  • Urządzenie zostanie zrestartowane. Aplikacja powinna nasłuchiwać zakończenia uruchamiania urządzenia, a następnie ponownie zarejestrować wymagane geofencing.
  • Aplikacja zostanie odinstalowana i zainstalowana ponownie.
  • Dane aplikacji zostaną wyczyszczone.
  • Dane Usług Google Play zostaną wyczyszczone.
  • Aplikacja otrzymała alert GEOFENCE_NOT_AVAILABLE. Zwykle dzieje się to po wyłączeniu NLP (dostawcy lokalizacji sieciowej w Androidzie).

Rozwiązywanie problemów ze zdarzeniem wejścia do geofencingu

Jeśli geofencing nie jest aktywowany, gdy urządzenie znajduje się w obszarze geofencingu (alert GEOFENCE_TRANSITION_ENTER nie jest wywoływany), najpierw sprawdź, czy geofencing jest prawidłowo zarejestrowany, zgodnie z opisem w tym przewodniku.

Oto kilka możliwych powodów, dla których alerty nie działają zgodnie z oczekiwaniami:

  • Dokładna lokalizacja jest niedostępna w obrębie geofencingu lub funkcja jest za mała. Na większości urządzeń usługa geofence używa tylko lokalizacji sieciowej do uruchamiania geofencingu. Usługa korzysta z tego podejścia, ponieważ lokalizacja sieciowa zużywa znacznie mniej energii, zajmuje mniej czasu na określenie konkretnych lokalizacji, a przede wszystkim jest dostępna wewnątrz budynków.
  • Wi-Fi na urządzeniu jest wyłączone. Włączenie Wi-Fi może znacznie zwiększyć dokładność lokalizacji. Jeśli zostanie ono wyłączone, aplikacja może nigdy nie otrzymywać alertów geofencingu. To zależy od kilku ustawień, w tym promienia geofencingu, modelu urządzenia i wersji Androida. Począwszy od Androida 4.3 (poziom interfejsu API 18), dodaliśmy funkcję „Trybu „Tylko skanowanie Wi-Fi”, który pozwala użytkownikom na wyłączanie Wi-Fi, ale pozwala uzyskać dobrą lokalizację sieci. Warto wysłać użytkownikowi prośbę i udostępnić skrót, który włącza tryb „Tylko Wi-Fi” lub „Tylko Wi-Fi”, jeśli obie te opcje są wyłączone. Użyj narzędzia SettingsClient, aby upewnić się, że ustawienia systemu urządzenia są prawidłowo skonfigurowane pod kątem optymalnego wykrywania lokalizacji.

    Uwaga: jeśli Twoja aplikacja jest kierowana na Androida 10 (poziom interfejsu API 29) lub nowszego, nie możesz wywoływać bezpośrednio WifiManager.setEnabled(), chyba że jest to aplikacja systemowa lub kontroler zasad dotyczących urządzeń (DPC). Zamiast tego użyj panelu ustawień.

  • W obrębie geofencingu nie ma stabilnego połączenia sieciowego. Przy braku stabilnego połączenia do transmisji danych alerty mogą nie być generowane. Dzieje się tak, ponieważ usługa geofence zależy od dostawcy lokalizacji sieciowej, który z kolei wymaga połączenia do transmisji danych.
  • Alerty mogą być opóźnione. Usługa geofencingu nie wysyła stale zapytań o lokalizację, więc otrzymywanie alertów może powodować pewne opóźnienia. Zwykle opóźnienie wynosi mniej niż 2 minuty, a nawet mniejsze, gdy urządzenie jest w ruchu. Jeśli obowiązują limity lokalizacji w tle, opóźnienie wynosi średnio około 2–3 minuty. Jeśli przez dłuższy czas urządzenie jest nieruchome, opóźnienie może się zwiększyć (do 6 minut).

Dodatkowe materiały

Więcej informacji o geofencingu znajdziesz w tych materiałach:

Próbki

Przykładowa aplikacja do tworzenia i monitorowania geofencingu.