Tworzenie i monitorowanie geofencingu

Geofencing łączy świadomość bieżącej lokalizacji użytkownika z informacjami o jego odległości od lokalizacji, które mogą być interesujące. Aby oznaczyć interesującą Cię lokalizację, podaj jej szerokość i długość geograficzną. Aby dostosować odległość od lokalizacji, dodaj promień. Szerokość, długość i promień wyznaczają geofencing, tworząc okrągły obszar lub ogrodzenie wokół interesującej Cię lokalizacji.

Możesz mieć wiele aktywnych geofence 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 zamieszanie przed wywołaniem zdarzenia. Możesz ograniczyć czas trwania dowolnego geofencingu, określając czas wygaśnięcia w milisekundach. Gdy geofence wygaśnie, usługi lokalizacyjne automatycznie go usuną.

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 przy zażądaniu monitorowania geofencingu jest poproszenie o niezbędne uprawnienia. Aby można było korzystać z geofencingu, aplikacja musi prosić o te uprawnienia:

Więcej informacji znajdziesz w przewodniku na temat wysyłania próśb 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 Geofencing. 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

Twoja aplikacja musi tworzyć i dodawać geofencje, używając klasy kreatora lokalizacji interfejsu API do tworzenia obiektów geofence i klasy wygodnej do ich dodawania. Poza tym, aby obsługiwać intencje wysyłane z usług lokalizacyjnych w przypadku przejścia na geofencing, możesz zdefiniować PendingIntent w sposób pokazany w tej sekcji.

Uwaga: na urządzeniach jednego użytkownika obowiązuje limit 100 geofence na aplikację. W przypadku urządzeń, na których jest wielu użytkowników, limit wynosi 100 geofencji na aplikację na użytkownika.

Tworzenie obiektów geofence

Najpierw za pomocą Geofence.Builder utwórz geofence, ustawiając odpowiedni promień, czas trwania i typy przejść dla geofencingu. 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());

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

Określanie obszarów geograficznych i aktywatorów początkowych

Ten fragment kodu używa klasy GeofencingRequest i zagnieżdżonej klasy GeofencingRequestBuilder do określenia obszarów geograficznych, które mają być monitorowane, i do określenia sposobu 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 aktywatorów geofencingu. Przejście GEOFENCE_TRANSITION_ENTER jest wywoływane, gdy urządzenie wchodzi w interakcję geofencingu, a przejście GEOFENCE_TRANSITION_EXIT jest wywoływane, gdy urządzenie opuści geofence. Określenie INITIAL_TRIGGER_ENTER informuje usługi lokalizacyjne, że interfejs GEOFENCE_TRANSITION_ENTER powinien być aktywowany, jeśli urządzenie znajduje się już w obrębie geofencingu.

W wielu przypadkach lepiej jest użyć zamiast niego INITIAL_TRIGGER_DWELL, który wywołuje zdarzenia tylko wtedy, gdy użytkownik zatrzymuje się na określony czas w ramach geofencingu. Takie podejście może ograniczyć „spam alertów” powodowany przez duże powiadomienia o dużej liczbie powiadomień, gdy urządzenie na chwilę wejdzie w interakcję i je z niego opuści. Inną strategią uzyskiwania najlepszych wyników w przypadku geofencingu jest ustawienie minimalnego promienia 100 metrów. Takie działanie pozwala określić dokładność lokalizacji w typowych sieciach Wi-Fi, a także zmniejszyć zużycie energii przez urządzenie.

Zdefiniuj odbiornik na potrzeby przejść geofence

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 to dobry sposób na obsługę przejść geofencingu. BroadcastReceiver otrzymuje aktualizacje, gdy wystąpi zdarzenie, na przykład przejście do geofencingu lub z niego, i może rozpocząć długotrwałą pracę w tle.

Ten fragment kodu pokazuje, jak zdefiniować PendingIntent, który zaczyna się 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ć geofencje, użyj metody GeofencingClient.addGeofences(). Podaj obiekt GeofencingRequest i PendingIntent. Ten fragment kodu ilustruje 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ść geofencingu

Gdy usługi lokalizacyjne wykryją, że użytkownik włączył geofence lub go opuścił, wysyłają geografię (Intent) w obiekcie PendingIntent uwzględnionym w żądaniu dodania geofence. Odbiornik transmisji, taki jak GeofenceBroadcastReceiver, zauważa, że został wywołany element Intent, dzięki czemu może uzyskać zdarzenie geofencingu z intencji, określić typ przejścia lub przejść geofencingu i określić, która ze zdefiniowanych geofencingu została aktywowana. Odbiornik komunikatów może przekierować 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. Aby dowiedzieć się, jak dostosować aplikację do tych limitów odpowiedzi, przeczytaj artykuł o limitach lokalizacji w tle.

Ten fragment kodu pokazuje, jak zdefiniować BroadcastReceiver, który publikuje powiadomienie w przypadku przejścia z 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. Usługa następnie wysyła powiadomienie i zapisuje szczegóły przejścia.

Zatrzymaj monitorowanie geofencingu

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 geofencingu w głównym działaniu używanym do dodawania i usuwania geofencingu. Usunięcie geofence natychmiast zatrzymuje działanie. Interfejs API udostępnia metody usuwania geofencingu według identyfikatorów żądań lub przez usuwanie obszarów geograficznych powiązanych z danym obiektem PendingIntent.

Ten fragment kodu usuwa geofence przez PendingIntent, zatrzymując wszystkie kolejne powiadomienia przy otwieraniu i zamykaniu dodanych wcześniej geofence:

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 połączyć z innymi funkcjami wykorzystującymi lokalizację, na przykład okresowymi aktualizacjami lokalizacji. Więcej informacji znajdziesz w innych lekcjach tych zajęć.

Korzystanie ze sprawdzonych metod dotyczących geofencingu

W tej sekcji znajdziesz zalecenia dotyczące używania geofencingu z interfejsami API lokalizacji na Androidzie.

Zmniejsz zużycie energii

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

  • Ustaw większą wartość czasu reagowania na powiadomienia. Pozwala to zmniejszyć zużycie energii przez zwiększenie czasu oczekiwania alertów geofencingu. Jeśli np. ustawisz wartość reagowania na 5 minut, aplikacja będzie sprawdzać alert o wejściu lub wyjściu tylko co 5 minut. Ustawienie niższych wartości nie musi oznaczać, że użytkownicy będą powiadamiani w tym okresie (jeśli na przykład ustawisz wartość na 5 sekund, alert może zostać wysłany trochę później).

  • Użyj większego promienia geofence w lokalizacjach, w których użytkownik spędza dużo czasu, np. w domu lub pracy. Większy promień nie bezpośrednio zmniejsza zużyciem energii, ale zmniejsza częstotliwość, z jaką aplikacja sprawdza wejście lub wyjście z domu, skutecznie zmniejszając ogólne zużycie energii.

Wybierz optymalny promień dla geofencingu

Aby uzyskać najlepsze wyniki, minimalny promień geofencingu powinien wynosić od 100 do 150 metrów. Gdy dostępna jest sieć Wi-Fi, dokładność lokalizacji wynosi zwykle 20–50 metrów. Jeśli dostępna jest lokalizacja wewnątrz budynków, dokładność pomiaru może wynosić maksymalnie 5 metrów. Jeśli nie wiesz, że w obrębie geofencingu dostępna jest lokalizacja wewnątrz obiektu, przyjmijmy, że dokładność lokalizacji Wi-Fi wynosi około 50 metrów.

Jeśli lokalizacja Wi-Fi jest niedostępna (np. gdy jedziesz na obszarze wiejskim), dokładność lokalizacji może być mniejsza. Dokładność może wynosić od kilkuset do kilku kilometrów. W takich przypadkach należy utworzyć granice geograficzne, korzystając z większego promienia.

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

Ponieważ podczas korzystania z geofencingu aplikacja uzyskuje dostęp do lokalizacji w tle, zastanów się, jakie korzyści zapewnia użytkownikom. Wyjaśnij im, dlaczego aplikacja potrzebuje tego dostępu, aby zwiększyć przejrzystość i zrozumienie użytkowników.

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

Używanie typu przejścia bez kontaktu w celu ograniczenia spamu w alertach

Jeśli otrzymasz dużą liczbę alertów podczas przejeżdżania przez chwilę obok geofencingu, najlepszym sposobem na ograniczenie ich liczby jest użycie typu przejścia GEOFENCE_TRANSITION_DWELL zamiast GEOFENCE_TRANSITION_ENTER. Dzięki temu alert dotyczący mieszkania jest wysyłany tylko wtedy, gdy użytkownik zatrzyma się w obrębie geofence w określonym czasie. Aby wybrać czas trwania, ustaw opóźnienie ruchu przychodzącego.

Ponowna rejestracja geofencingu tylko wtedy, gdy jest to wymagane

Zarejestrowane geofencje są przechowywane w procesie com.google.process.location należącym do pakietu com.google.android.gms. Aplikacja nie musi nic robić w przypadku tych zdarzeń, ponieważ system przywraca geofencing po tych zdarzeniach:

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

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

  • Urządzenie zostało zrestartowane. Aplikacja powinna nasłuchiwać zakończenia uruchamiania urządzenia, a następnie ponownie zarejestrować wymagane geofence.
  • Aplikacja zostanie odinstalowana i zainstalowana ponownie.
  • 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 (Androida Network Location Provider).

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

Jeśli geofencing nie są aktywowane, gdy urządzenie wchodzi w interakcję geofencingu (nie aktywowany jest alert GEOFENCE_TRANSITION_ENTER), najpierw sprawdź, czy geofencing są prawidłowo zarejestrowane w sposób opisany w tym przewodniku.

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

  • Dokładna lokalizacja w obrębie geofencingu nie jest dostępna lub geofence jest za mały. 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 zwiększyć dokładność lokalizacji, więc jeśli jest wyłączone, aplikacja może nigdy nie otrzymywać alertów geofencingu w zależności od kilku ustawień, takich jak promień geofencingu, model urządzenia czy wersja Androida. Począwszy od Androida 4.3 (poziom interfejsu API 18) dodaliśmy możliwość „trybu Tylko skanowanie Wi-Fi”, który pozwala użytkownikom wyłączyć Wi-Fi, zachowując jednocześnie dostęp do sieci Wi-Fi w dobrej lokalizacji. Warto, aby użytkownik zobaczył prośbę i podał skrót, który umożliwia włączenie trybu skanowania Wi-Fi lub tylko Wi-Fi, jeśli oba te tryby są wyłączone. Użyj SettingsClient, aby sprawdzić, czy 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łać funkcji WifiManager.setEnabled() bezpośrednio, 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 niezawodnego połączenia sieciowego. W przypadku braku niezawodnego połączenia do transmisji danych alerty mogą nie zostać wygenerowane. Wynika to z faktu, że usługa geofence zależy od dostawcy lokalizacji sieci, co z kolei wymaga połączenia do transmisji danych.
  • Alerty mogą być opóźnione. Usługa geofencingu nie pyta o lokalizację w sposób ciągły, więc należy liczyć się z pewnymi opóźnieniami podczas otrzymywania alertów. Zazwyczaj opóźnienie wynosi mniej niż 2 minuty, 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 było przez dłuższy czas 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 stref geograficznych.