Tworzenie i monitorowanie geofencingu

Geofencing łączy informacje o obecnej lokalizacji użytkownika z informacjami o odległości od miejsc, które mogą go zainteresować. Aby oznaczyć interesującą lokalizację, podaj jej szerokość i długość geograficzną. Aby dostosować zbliżenie do lokalizacji, dodaj promień. Szerokość i długość geograficzna oraz promień określają geofence, tworząc obszar wokół interesującej Cię lokalizacji.

Możesz mieć wiele aktywnych geofence’ów, ale ich liczba nie może przekroczyć 100 na aplikację i na użytkownika urządzenia. W przypadku każdego geoogrodzenia możesz poprosić Usługi lokalizacyjne o wysyłanie zdarzeń wejścia i wyjścia lub określić czas oczekiwania (czas przebywania) w obszarze geoogrodzenia przed wywołaniem zdarzenia. Możesz ograniczyć czas trwania dowolnej geofence, określając czas ważności w milisekundach. Po upływie czasu, na jaki ustawiono geofence, usługi lokalizacyjne automatycznie je usuwają.

Z tej lekcji dowiesz się, jak dodawać i usuwać geoogrodzenia oraz jak odbierać przejścia między geoogrodzeniami za pomocą BroadcastReceiver.

Uwaga: na urządzeniach Wear interfejsy Geofencing API nie zapewniają efektywnego wykorzystania energii. Nie zalecamy korzystania z tych interfejsów API na Wear. Więcej informacji znajdziesz w artykule Oszczędzanie energii i baterii.

Konfigurowanie monitorowania geofence

Pierwszym krokiem w prośbie o monitorowanie geoogrodzenia jest poproszenie o wymagane uprawnienia. Aby korzystać z geofencingu, aplikacja musi prosić o te dane:

Więcej informacji znajdziesz w przewodniku Prośba o dostęp do lokalizacji.

Jeśli chcesz używać interfejsu BroadcastReceiver do nasłuchiwania przejść geofencingu, dodaj element z 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 geofencingu. Aby dowiedzieć się, jak połączyć klienta:

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 geofence’ów

Aplikacja musi tworzyć i dodawać geoogrodzenia za pomocą klasy konstruktora interfejsu API lokalizacji do tworzenia obiektów Geofence oraz klasy ułatwiającej ich dodawanie. Aby obsłużyć intencje wysyłane przez Usługi lokalizacyjne po przejściu przez geofence, możesz zdefiniować PendingIntent, jak pokazano w tej sekcji.

Uwaga: na urządzeniach jednoużytkownikowych można ustawić maksymalnie 100 geofence’ów na aplikację. W przypadku urządzeń wieloużytkownikowych limit wynosi 100 geofence’ów na aplikację na każdego użytkownika.

Tworzenie obiektów geofence

Najpierw użyj Geofence.Builder, aby utworzyć geofence, ustawiając pożądany promień, czas trwania i typy przejść. Aby na przykład 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());

W tym przykładzie dane są pobierane z pliku z wartościami stałymi. W praktyce aplikacje mogą dynamicznie tworzyć geoogrodzenia na podstawie lokalizacji użytkownika.

Określanie geofence’ów i pierwotnych aktywatorów

Ten fragment kodu używa klasy GeofencingRequest i jej zagnieżdżonej klasy GeofencingRequestBuilder do określania geofence’ów do monitorowania i ustawiania sposobu uruchamiania powiązanych zdarzeń geofence:

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ł geofence. GEOFENCE_TRANSITION_ENTER przejście uruchamia się, gdy urządzenie wejdzie do geoogrodzenia, a  GEOFENCE_TRANSITION_EXIT przejście uruchamia się, gdy urządzenie opuści geoogrodzoną strefę. Podanie wartości INITIAL_TRIGGER_ENTER informuje usługi lokalizacyjne, że GEOFENCE_TRANSITION_ENTER powinny zostać uruchomione, jeśli urządzenie znajduje się już w geoogrodzeniu.

W wielu przypadkach lepiej jest użyć tagu INITIAL_TRIGGER_DWELL, który uruchamia zdarzenia tylko wtedy, gdy użytkownik zatrzymuje się w geoogrodzeniu przez określony czas. Takie podejście może pomóc w ograniczeniu „spamu z powiadomieniami” spowodowanego dużą liczbą powiadomień, gdy urządzenie na krótko wchodzi i wychodzi z geoogrodzenia. Inną strategią, która pozwoli Ci uzyskać najlepsze wyniki w przypadku geoogrodów, jest ustawienie minimalnego promienia 100 metrów. Pozwala to uwzględnić dokładność lokalizacji w typowych sieciach Wi-Fi, a także zmniejsza zużycie energii przez urządzenie.

Definiowanie odbiornika transmisji na potrzeby przejść w geofencingu

Intent wysłany z usług lokalizacyjnych może aktywować różne działania w aplikacji, ale nie powinno się uruchamiać aktywności ani fragmentów, ponieważ komponenty powinny być widoczne tylko w odpowiedzi na działanie użytkownika. W wielu przypadkach BroadcastReceiver jest dobrym sposobem na przetwarzanie zmian w geofencingu. BroadcastReceiver otrzymuje aktualizacje, gdy nastąpi zdarzenie, takie jak wejście lub wyjście z geoogrodzenia, i może rozpocząć długotrwałą pracę w tle.

Ten fragment kodu pokazuje, jak zdefiniować element PendingIntent, który inicjuje element 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 granice geograficzne

Aby dodać geofence, użyj metody GeofencingClient.addGeofences(). Podaj obiekt GeofencingRequestPendingIntent. 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ługa przejść przez geofence

Gdy usługi lokalizacyjne wykryją, że użytkownik wszedł do geoogrodzenia lub z niego wyszedł, wysyłają Intent zawarte w PendingIntent, które zostało dołączone do żądania dodania geoogrodzeń. Odbiornik transmisji, taki jak GeofenceBroadcastReceiver, zauważa, że wywołano Intent, a następnie może uzyskać zdarzenie geofencingu z intencji, określić typ przejścia w ramach geofence oraz ustalić, które z zdefiniowanych geofence zostało aktywowane. Odbiorca transmisji może zlecić aplikacji rozpoczęcie wykonywania pracy w tle lub, jeśli zechce, 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 geoogrodzenia, urządzenie reaguje na zdarzenia związane z geoogrodzeniem co kilka minut. Aby dowiedzieć się, jak dostosować aplikację do tych limitów odpowiedzi, przeczytaj artykuł Ograniczenia lokalizacji.

Ten fragment kodu pokazuje, jak zdefiniować BroadcastReceiver, który publikuje powiadomienie, gdy nastąpi 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 pobiera typ przejścia geofence 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 przejścia.

Zatrzymywanie monitorowania geoogrodzenia

Wyłączenie monitorowania geofencingu, gdy nie jest już potrzebne lub potrzebne, może pomóc zaoszczędzić energię baterii i cykli pracy procesora w urządzeniu. Możesz zatrzymać monitorowanie geofence w głównej aktywności służącej do dodawania i usuwania geofence. Usunięcie geofence powoduje natychmiastowe zatrzymanie monitorowania. Interfejs API udostępnia metody usuwania geofencingu według identyfikatorów żądań lub przez usuwanie obszarów geograficznych powiązanych z danym obiektem PendingIntent.

Poniższy fragment kodu usuwa geoogrodzenia o wartości PendingIntent, co powoduje zablokowanie wszystkich dalszych powiadomień, gdy urządzenie wchodzi do wcześniej dodanych geoogrodzeń lub z nich wychodzi:

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 łączyć z innymi funkcjami wykorzystującymi lokalizację, np. z okresowymi aktualizacjami lokalizacji. Więcej informacji znajdziesz w innych lekcjach tego kursu.

Stosuj sprawdzone metody geofencingu

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

Zmniejsz zużycie energii

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

  • Ustaw czas reakcji na powiadomienia na wyższą wartość. Dzięki temu zmniejszysz zużycie energii, zwiększając opóźnienie alertów dotyczących geofence. Jeśli na przykład ustawisz wartość szybkości reakcji na 5 minut, aplikacja będzie sprawdzać alerty dotyczące wjazdu lub wyjazdu tylko raz na 5 minut. Ustawienie niższych wartości niekoniecznie oznacza, że użytkownicy otrzymają powiadomienie w tym czasie (na przykład jeśli ustawisz wartość 5 sekund, może minąć trochę więcej czasu, zanim użytkownik otrzyma alert).

  • Użyj większego promienia geoogrodzenia w przypadku lokalizacji, w których użytkownik spędza dużo czasu, na przykład domu lub pracy. Większy promień nie zmniejsza bezpośrednio zużycia energii, ale zmniejsza częstotliwość sprawdzania przez aplikację, czy użytkownik wchodzi lub wychodzi z obszaru, co pozwala zmniejszyć ogólne zużycie energii.

Wybierz optymalny promień dla geofencingu

Aby uzyskać najlepsze wyniki, ustaw minimalny promień geofence w zakresie 100–150 metrów. Gdy dostępna jest sieć Wi-Fi, dokładność lokalizacji wynosi zwykle 20–50 metrów. Gdy dostępna jest lokalizacja wewnątrz budynku, dokładność może wynosić nawet 5 metrów. Jeśli nie wiesz, czy w obrębie geoogrodzenia jest dostępna lokalizacja wewnątrz budynku, załóż, że dokładność lokalizacji Wi-Fi wynosi około 50 metrów.

Gdy lokalizacja Wi-Fi jest niedostępna (na przykład podczas jazdy po obszarach wiejskich), dokładność lokalizacji spada. Dokładność może wynosić od kilkuset metrów do kilku kilometrów. W takich przypadkach należy utworzyć geofence o większym promieniu.

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

Ponieważ aplikacja korzysta z lokalizacji w tle, gdy używasz geofence, zastanów się, jakie korzyści przynosi ona użytkownikom. Wyjaśnij, dlaczego aplikacja potrzebuje tego dostępu, aby zwiększyć przejrzystość i zrozumienie.

Więcej informacji o sprawdzonych metodach związanych z dostępem do lokalizacji, w tym o geofencingu, znajdziesz na stronie Sprawdzone metody ochrony prywatności.

Używanie typu przejścia „przerwa”, aby ograniczyć spam z alertami

Jeśli po krótkim przejechaniu przez geofence otrzymujesz dużą liczbę alertów, najlepszym sposobem na zmniejszenie liczby alertów jest użycie typu przejścia GEOFENCE_TRANSITION_DWELL zamiast GEOFENCE_TRANSITION_ENTER. W ten sposób alert o zatrzymaniu jest wysyłany tylko wtedy, gdy użytkownik zatrzymuje się w geoogrodzeniu przez określony czas. Aby wybrać czas trwania, ustaw opóźnienie ruchu przychodzącego.

rejestrować geoogrodzenia ponownie tylko wtedy, gdy jest to konieczne;

Zarejestrowane geofencje są przechowywane w procesie com.google.process.location należącym do pakietu com.google.android.gms. Aplikacja nie musi nic robić, aby obsłużyć te zdarzenia, ponieważ system przywraca geofence po tych zdarzeniach:

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

Aplikacja musi ponownie zarejestrować geoogrodzenia, jeśli są one nadal potrzebne po tych zdarzeniach, ponieważ system nie może odzyskać geoogrodzeń w tych przypadkach:

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

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

Jeśli geofence nie są aktywowane, gdy urządzenie wchodzi na obszar geofence (nie jest aktywowany alert GEOFENCE_TRANSITION_ENTER), najpierw sprawdź, czy geofence są prawidłowo zarejestrowane zgodnie z instrukcjami podanymi w tym przewodniku.

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

  • Dokładna lokalizacja nie jest dostępna w geoogrodzeniu lub geoogrodzeniu jest za małe. Na większości urządzeń usługa geofence używa tylko lokalizacji sieciowej do uruchamiania geofencingu. Usługa wykorzystuje takie podejście, ponieważ lokalizacja sieciowa zużywa znacznie mniej energii, potrzeba mniej czasu na określenie konkretnych lokalizacji, a przede wszystkim działa wewnątrz budynków.
  • Wi-Fi na urządzeniu jest wyłączone. Włączenie Wi-Fi może znacznie poprawić dokładność lokalizacji, więc jeśli Wi-Fi jest wyłączone, aplikacja może nigdy nie otrzymywać alertów geofence, w zależności od kilku ustawień, w tym promienia geofence, modelu urządzenia lub wersji Androida. Od wersji Android 4.3 (poziom interfejsu API 18) dodaliśmy funkcję „tylko skanowanie sieci Wi-Fi”, która umożliwia użytkownikom wyłączenie Wi-Fi przy jednoczesnym uzyskaniu dobrej lokalizacji sieci. Dobrą praktyką jest wyświetlenie użytkownikowi prompta i podanie skrótu do włączenia trybu Wi-Fi lub tylko skanowania Wi-Fi, jeśli oba są wyłączone. Użyj funkcji SettingsClient, aby mieć pewność, ż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 funkcji WifiManager.setEnabled(), chyba że jest to aplikacja systemowa lub kontroler zasad urządzenia (DPC). Zamiast tego użyj panelu ustawień.

  • W strefie geograficznej nie ma niezawodnego połączenia z siecią. Jeśli nie ma stabilnego połączenia danych, alerty mogą nie być generowane. Dzieje się tak, ponieważ usługa geofence zależy od dostawcy lokalizacji sieci, który z kolei wymaga połączenia danych.
  • Alerty mogą być opóźnione. Usługa geofence nie wysyła ciągle zapytań o lokalizację, dlatego opóźnienia w przychodzeniu alertów są możliwe. Zwykle opóźnienie wynosi mniej niż 2 minuty, a nawet mniej, gdy urządzenie się porusza. Jeśli obowiązują ograniczenia dotyczące lokalizacji, opóźnienie wynosi średnio 2–3 minuty. Jeśli urządzenie było przez dłuższy czas nieruchome, opóźnienie może się zwiększyć (do 6 minut).

Dodatkowe materiały

Aby dowiedzieć się więcej o geofencingu, zapoznaj się z tymi materiałami:

Próbki

Przykładowa aplikacja do tworzenia i monitorowania stref geograficznych.