Tworzenie i monitorowanie geofencingu

Geofencing łączy ze sobą 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 API do geoogrodzenia nie wykorzystują efektywnie energii. Nie zalecamy używania tych interfejsów API na Wear. Więcej informacji znajdziesz w artykule Oszczędzanie energii i baterii.

Konfigurowanie monitorowania geoogrodzenia

Pierwszym krokiem w prośbie o monitorowanie geoogrodzenia jest prośba 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ć BroadcastReceiver do słuchania przejść w geofencingu, dodaj element określają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 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 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 wchodzi do geoogrodzenia, a  GEOFENCE_TRANSITION_EXIT przejście uruchamia się, gdy urządzenie opuszcza geoogrodzenia. Określanie 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 lokalizacji może wywoływać różne działania w aplikacji, ale nie powinien uruchamiać aktywności ani fragmentu, ponieważ komponenty powinny być widoczne tylko w odpowiedzi na działanie użytkownika. W wielu przypadkach BroadcastReceiver jest dobrym sposobem na przetwarzanie przejścia na podstawie geoogrodzenia. BroadcastReceiver otrzymuje aktualizacje, gdy wystą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ć PendingIntent, który uruchamia 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;
    }

Dodawanie geofence

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 geofencingu i ustalić, które z zdefiniowanych geofence zostały uruchomione. 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.

Poniższy fragment kodu pokazuje, jak zdefiniować element BroadcastReceiver, który publikuje powiadomienie, gdy nastąpi przejście między geofence’ami. Gdy użytkownik kliknie powiadomienie, pojawi się główna aktywność 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ą PendingIntent usługa BroadcastReceiver uzyskuje typ przejścia w geofenesie i sprawdza, czy jest to jedno ze zdarzeń, których aplikacja używa do uruchamiania 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

Zatrzymanie monitorowania geoogrodzenia, gdy nie jest już potrzebne lub nie jest już pożądane, może pomóc zaoszczędzić energię baterii i cykle procesora na 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 umożliwiające usuwanie geofence’ów za pomocą identyfikatorów żądań lub poprzez usunięcie geofence’ów powiązanych z danym PendingIntent.

Poniższy fragment kodu usuwa geoogrodzenia za pomocą funkcji PendingIntent, co powoduje zablokowanie wszystkich dalszych powiadomień, gdy urządzenie wchodzi do lub opuszcza wcześniej dodane geoogrodzenia:

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 w tym kursie.

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, wydłużając czas oczekiwania na alerty dotyczące geoogrodzenia. 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ń geoogrodzenia

Aby uzyskać najlepsze wyniki, ustaw minimalny promień geoogrodzenia na 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 „czas trwania” w celu ograniczenia spamu z alertami

Jeśli podczas krótkiego przejazdu 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. Czas trwania możesz wybrać, ustawiając opóźnienie w pojawianiu się treści.

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

Zarejestrowane geoogrodzenia 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ą zatrzymywane i ponownie uruchamiane przez system z powodu ograniczeń 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ć działania po uruchomieniu urządzenia, a potem ponownie zarejestrować wymagane geofence.
  • Aplikacja zostanie 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 w tym przewodniku.

Oto kilka możliwych przyczyn, 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 do aktywowania geofence korzysta tylko z lokalizacji sieciowej. Usługa korzysta z tego podejścia, ponieważ lokalizacja sieci zużywa znacznie mniej energii, szybciej uzyskuje poszczególne lokalizacje i co najważniejsze, jest dostępna w pomieszczeniach.
  • Wi-Fi jest wyłączone na urządzeniu. 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 Androida 4.3 (poziom interfejsu API 18) dodaliśmy funkcję „tylko skanowanie sieci Wi-Fi”, która umożliwia użytkownikom wyłączenie Wi-Fi, ale nadal zapewnia dobrą lokalizację 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ń.

  • Brak niezawodnego połączenia z siecią w strefie geograficznej. 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 korzystania z lokalizacji, opóźnienie wynosi średnio 2–3 minuty. Jeśli urządzenie było nieruchome przez dłuższy czas, opóźnienie może wzrosnąć (do 6 minut).

Dodatkowe materiały

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

Próbki

Próbna aplikacja do tworzenia i monitorowania geoogrodów.