Tworzenie i monitorowanie geofencingu

Geofencing łączy świadomość bieżącej lokalizacji użytkownika ze świadomością jego odległości od miejsc, które mogą go interesować. Aby oznaczyć lokalizację, która Cię interesuje, podaj jej szerokość i długość geograficzną. Aby dostosować zbliżony obszar dla lokalizacji, dodaj promień. Szerokość i długość geograficzna oraz promień określają geofence, czyli tworzą okrągły obszar lub ogrodzenie wokół interesującej lokalizacji.

Możesz mieć wiele aktywnych geofence, ale maksymalnie 100 na aplikację i użytkownika urządzenia. W przypadku każdego geofence możesz poprosić Usługi lokalizacyjne o wysyłanie zdarzeń wejścia i wyjścia lub określić czas oczekiwania w obszarze geofence, czyli czas przebywania, przed wywołaniem zdarzenia. Możesz ograniczyć czas trwania dowolnego obszaru geograficznego, określając czas ważności w milisekundach. Po wygaśnięciu geofence usługi lokalizacyjne automatycznie ją usuwają.

Z tej lekcji dowiesz się, jak dodawać i usuwać geofence, a potem nasłuchiwać przejść między nimi za pomocą BroadcastReceiver.

Uwaga: na urządzeniach z Wear interfejsy Geofencing API 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 geofencingu

Pierwszym krokiem w procesie wysyłania prośby o monitorowanie obszaru jest poproszenie o niezbędne uprawnienia. Aby korzystać z geofencingu, aplikacja musi wysyłać te żądania:

Więcej informacji znajdziesz w przewodniku na temat próśb o przyznanie uprawnień do lokalizacji.

Jeśli chcesz użyć BroadcastReceiver do nasłuchiwania przejść między obszarami, 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

Aplikacja musi tworzyć i dodawać geofence za pomocą klasy narzędziowej interfejsu API lokalizacji do tworzenia obiektów Geofence oraz klasy pomocniczej do ich dodawania. Aby obsługiwać intencje wysyłane z Usług lokalizacyjnych, gdy nastąpią przejścia w geofence, możesz też zdefiniować PendingIntent, jak pokazano w tej sekcji.

Uwaga: na urządzeniach z jednym użytkownikiem obowiązuje limit 100 geofence na aplikację. Na urządzeniach z wieloma użytkownikami limit wynosi 100 geofence na aplikację na użytkownika urządzenia.

Tworzenie obiektów geofence

Najpierw użyj Geofence.Builder, aby utworzyć geofence, ustawiając żądany promień, czas trwania i typy przejść dla geofence. 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 stałych. W praktyce aplikacje mogą dynamicznie tworzyć geofence na podstawie lokalizacji użytkownika.

Określanie geofence i początkowych aktywatorów

W tym fragmencie kodu użyto klasy GeofencingRequest i jej zagnieżdżonej klasy GeofencingRequestBuilder, aby określić obszary geograficzne do monitorowania i sposób wywoływania powiązanych zdarzeń związanych z obszarami geograficznymi:

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ł dotyczących geofencingu. Przejście GEOFENCE_TRANSITION_ENTER jest wywoływane, gdy urządzenie wchodzi na obszar objęty geofencingiem, a przejście GEOFENCE_TRANSITION_EXIT – gdy go opuszcza. Określenie INITIAL_TRIGGER_ENTER informuje usługi lokalizacyjne, że GEOFENCE_TRANSITION_ENTER ma być wywoływane, jeśli urządzenie znajduje się już w obszarze.

W wielu przypadkach lepiej jest używać INITIAL_TRIGGER_DWELL, które wywołuje zdarzenia tylko wtedy, gdy użytkownik zatrzyma się na określony czas w geofence. Takie podejście może pomóc w ograniczeniu „spamu z alertami” wynikającego z dużej liczby powiadomień, gdy urządzenie na krótko wchodzi na obszar geofence i go opuszcza. Inną strategią, która pozwala uzyskać najlepsze wyniki w przypadku geofencingu, jest ustawienie minimalnego promienia wynoszącego 100 metrów. Pomaga to uwzględnić dokładność lokalizacji typowych sieci Wi-Fi, a także zmniejszyć zużycie energii przez urządzenie.

Definiowanie odbiornika transmisji dla przejść między obszarami

Intent wysłany przez Usługi lokalizacyjne może wywoływać różne działania w aplikacji, ale nie powinien uruchamiać aktywności ani fragmentu, ponieważ komponenty powinny stawać się widoczne tylko w odpowiedzi na działanie użytkownika. W wielu przypadkach BroadcastReceiver to dobry sposób na obsługę przejścia przez geofence. A BroadcastReceiver otrzymuje aktualizacje, gdy wystąpi zdarzenie, np. wejście na obszar lub opuszczenie go, 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, jak przetwarzać wyniki:

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ł na obszar geofence lub go opuścił, wysyłają Intent zawarte w PendingIntent, które zostały uwzględnione w żądaniu dodania obszarów geofence. Odbiornik transmisji, np. GeofenceBroadcastReceiver, zauważa, że wywołano Intent, a następnie może uzyskać zdarzenie geofencingu z intencji, określić typ przejścia geofencingu i ustalić, który z określonych geofence został wywołany. Odbiornik transmisji może nakazać aplikacji rozpoczęcie pracy w tle lub, w razie potrzeby, wysłać powiadomienie.

Uwaga: na Androidzie 8.0 (API na poziomie 26) i nowszych, jeśli aplikacja działa w tle podczas monitorowania obszaru geograficznego, urządzenie reaguje na zdarzenia związane z geofencingiem co kilka minut. Aby dowiedzieć się, jak dostosować aplikację do tych limitów odpowiedzi, przeczytaj artykuł Limity lokalizacji w tle.

Poniższy fragment kodu pokazuje, jak zdefiniować element BroadcastReceiver, który publikuje powiadomienie, gdy nastąpi przejście geofence. 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 funkcja BroadcastReceiver pobiera typ przejścia przez geofence i sprawdza, czy jest to jedno ze zdarzeń, których aplikacja używa do wywoływania powiadomień – w tym przypadku GEOFENCE_TRANSITION_ENTER lub GEOFENCE_TRANSITION_EXIT. Usługa wysyła powiadomienie i rejestruje szczegóły przejścia.

Zatrzymywanie monitorowania geofence

Zatrzymanie monitorowania geofence, gdy nie jest już potrzebne lub pożądane, może pomóc oszczędzać baterię i cykle procesora na urządzeniu. Możesz zatrzymać monitorowanie geofence w głównej aktywności używanej do dodawania i usuwania geofence. Usunięcie geofence natychmiast zatrzymuje jego działanie. Interfejs API udostępnia metody usuwania geofence’ów na podstawie identyfikatorów żądań lub przez usunięcie geofence’ów powiązanych z określonym PendingIntent.

Poniższy fragment kodu usuwa geofence według PendingIntent, co powoduje zatrzymanie wszystkich dalszych powiadomień, gdy urządzenie wchodzi na wcześniej dodane geofence 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żesz łączyć z innymi funkcjami opartymi na lokalizacji, takimi jak okresowe aktualizacje lokalizacji. Więcej informacji znajdziesz w innych lekcjach z tego kursu.

Stosuj sprawdzone metody dotyczące geofencingu

W tej sekcji przedstawiamy zalecenia dotyczące korzystania z geofencingu w przypadku interfejsów API lokalizacji na Androidzie.

Zmniejsz zużycie energii

Aby zoptymalizować zużycie energii w aplikacjach korzystających z geofencingu, możesz zastosować te techniki:

  • Ustaw wyższą wartość reakcji na powiadomienia. Zmniejsza to zużycie energii, ponieważ zwiększa opóźnienie alertów dotyczących geofencingu. Jeśli na przykład ustawisz wartość czułości na 5 minut, aplikacja będzie sprawdzać alerty o wejściu lub wyjściu tylko raz na 5 minut. Ustawienie niższych wartości niekoniecznie oznacza, że użytkownicy otrzymają powiadomienie w tym przedziale czasu (np. jeśli ustawisz wartość 5 sekund, otrzymanie alertu może potrwać nieco dłużej).

  • W przypadku lokalizacji, w których użytkownik spędza dużo czasu, np. w domu lub w pracy, używaj większego promienia geofence. Większy promień nie zmniejsza bezpośrednio zużycia energii, ale ogranicza częstotliwość sprawdzania przez aplikację, czy użytkownik wchodzi na dany obszar lub go opuszcza, co skutecznie obniża ogólne zużycie energii.

Wybieranie optymalnego promienia geofence

Aby uzyskać najlepsze wyniki, ustaw minimalny promień geofence na 100–150 metrów. Gdy dostępna jest sieć Wi-Fi, dokładność lokalizacji wynosi zwykle od 20 do 50 metrów. Gdy dostępna jest lokalizacja w pomieszczeniu, zakres dokładności może wynosić nawet 5 metrów. Jeśli nie wiesz, czy w danym obszarze jest dostępna lokalizacja w pomieszczeniach, przyjmij, że dokładność lokalizacji Wi-Fi wynosi około 50 metrów.

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

Wyjaśnij użytkownikom, dlaczego Twoja aplikacja korzysta z geofencingu

Aplikacja korzysta z lokalizacji w tle, gdy używasz geofencingu, więc zastanów się, jakie korzyści przynosi to użytkownikom. Wyjaśnij użytkownikom, dlaczego Twoja aplikacja potrzebuje tego dostępu, aby zwiększyć ich świadomość i przejrzystość.

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 „zatrzymanie”, aby ograniczyć spamowanie alertami

Jeśli podczas krótkiego przejazdu obok obszaru otrzymasz dużą liczbę alertów, najlepszym sposobem na ich ograniczenie jest użycie typu przejścia GEOFENCE_TRANSITION_DWELL zamiast GEOFENCE_TRANSITION_ENTER. Dzięki temu alert o przebywaniu w określonym miejscu jest wysyłany tylko wtedy, gdy użytkownik zatrzyma się w geofence na określony czas. Możesz wybrać czas trwania, ustawiając opóźnienie.

Ponowna rejestracja geofence tylko wtedy, gdy jest to wymagane

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

  • Usługi Google Play zostaną zaktualizowane.
  • Usługi Google Play są zamykane i ponownie uruchamiane przez system z powodu ograniczeń zasobów.
  • Proces lokalizacji ulega awarii.

Aplikacja musi ponownie zarejestrować geofence, jeśli są one nadal potrzebne po wystąpieniu tych zdarzeń, ponieważ system nie może ich odzyskać w tych przypadkach:

  • Urządzenie uruchomi się ponownie. Aplikacja powinna nasłuchiwać działania związanego z zakończeniem uruchamiania urządzenia, a następnie ponownie zarejestrować wymagane geofence.
  • Aplikacja zostanie odinstalowana i ponownie zainstalowana.
  • Dane aplikacji zostaną wyczyszczone.
  • Dane usług Google Play zostaną wyczyszczone.
  • Aplikacja otrzymała GEOFENCE_NOT_AVAILABLE alert. Zwykle dzieje się tak po wyłączeniu NLP (dostawcy lokalizacji sieciowej na Androidzie).

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

Jeśli geofence nie są aktywowane, gdy urządzenie wchodzi w ich obszar ( GEOFENCE_TRANSITION_ENTER alert nie jest wywoływany), najpierw upewnij się, że geofence są prawidłowo zarejestrowane zgodnie z tym przewodnikiem.

Oto kilka możliwych przyczyn nieprawidłowego działania alertów:

  • Dokładna lokalizacja jest niedostępna w Twojej strefie lub strefa jest zbyt mała. Na większości urządzeń usługa geofencingu używa do wywoływania geofencingu tylko lokalizacji sieciowej. Usługa korzysta z tego podejścia, ponieważ lokalizacja sieciowa zużywa znacznie mniej energii, szybciej uzyskuje dyskretne 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 zwiększyć dokładność lokalizacji, więc jeśli Wi-Fi jest wyłączone, aplikacja może nigdy nie otrzymywać alertów o geofencingu. Zależy to od kilku ustawień, w tym od promienia geofencingu, modelu urządzenia lub wersji Androida. Od Androida 4.3 (poziom API 18) dodaliśmy funkcję „Tryb skanowania Wi-Fi”, która umożliwia użytkownikom wyłączenie Wi-Fi przy jednoczesnym zachowaniu dobrej lokalizacji sieciowej. Jeśli obie te opcje są wyłączone, warto poprosić użytkownika o ich włączenie i udostępnić mu skrót do włączenia Wi-Fi lub trybu skanowania Wi-Fi. Użyj SettingsClient, aby mieć pewność, że ustawienia systemowe urządzenia są prawidłowo skonfigurowane pod kątem optymalnego wykrywania lokalizacji.

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

  • W Twojej strefie nie ma niezawodnego połączenia sieciowego. Jeśli nie ma 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 z internetem.
  • Alerty mogą być opóźnione. Usługa geofence nie wysyła ciągłych zapytań o lokalizację, więc podczas otrzymywania alertów mogą występować opóźnienia. Zwykle opóźnienie wynosi mniej niż 2 minuty, a nawet mniej, gdy urządzenie jest w ruchu. Jeśli obowiązują limity lokalizacji w tle, opóźnienie wynosi średnio 2–3 minuty. Jeśli urządzenie pozostawało w bezruchu przez dłuższy czas, opóźnienie może się zwiększyć (nawet 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 geofence.