Pierwsze kroki z pakietem SDK Driver na Androida

Możesz użyć pakietu Driver SDK, aby zapewnić lepszą nawigację i śledzenie w aplikacjach Podróż i Postępy zamówienia. Pakiet Driver SDK zapewnia aktualizacje lokalizacji pojazdów i zadań w Fleet Engine na żądanie.

Pakiet Driver SDK informuje usługi Fleet Engine i usługi niestandardowe o lokalizacji i stanie pojazdu. Może to być na przykład ONLINE lub OFFLINE, a lokalizacja pojazdu zmienia się w miarę postępów podróży.

Minimalne wymagania systemowe

Na urządzeniu mobilnym musi być zainstalowany Android 6.0 (poziom interfejsu API 23) lub nowszy.

Konfiguracja Maven

Pakiet SDK sterowników w wersji 4.99 i nowszych jest dostępny w repozytorium Google Maven.

Gradle

Dodaj do pliku build.gradle te informacje:

repositories {
    ...
    google()
}

Maven

Dodaj do pliku pom.xml te informacje:

<project>
  ...
  <repositories>
    <repository>
      <id>google-maven-repository</id>
      <url>https://maven.google.com</url>
    </repository>
  </repositories>
  ...
</project>

Konfiguracja projektu

Aby korzystać z pakietu Driver SDK, Twoja aplikacja musi być kierowana na minSdkVersion na poziomie 23 lub wyższym.

Aby uruchomić aplikację stworzoną za pomocą pakietu Driver SDK, na urządzeniu z Androidem muszą być zainstalowane Usługi Google Play.

Konfigurowanie projektu programistycznego

Aby skonfigurować projekt programistyczny i uzyskać jego klucz interfejsu API w konsoli Google Cloud:

  1. Utwórz nowy projekt Google Cloud Console lub wybierz istniejący projekt do użycia z pakietem Driver SDK. Zaczekaj kilka minut, aż nowy projekt pojawi się w konsoli Google Cloud.

  2. Aby uruchomić aplikację demonstracyjną, projekt musi mieć dostęp do pakietu Maps SDK na Androida. W konsoli Google Cloud wybierz Interfejsy API i usługi > Biblioteka, a następnie wyszukaj i włącz pakiet SDK Map Google na Androida.

  3. Aby uzyskać klucz interfejsu API projektu, wybierz Interfejsy API i usługi > Dane logowania > Utwórz dane logowania > Klucz interfejsu API. Więcej informacji o uzyskiwaniu klucza interfejsu API znajdziesz w artykule Uzyskiwanie klucza interfejsu API.

Dodaj do aplikacji pakiet SDK Driver

Pakiet Driver SDK jest dostępny w repozytorium Google Maven. Repozytorium zawiera pliki modelu obiektu projektu (.pom) z pakietu SDK i dokumenty Javadocs. Aby dodać Driver SDK do aplikacji:

  1. Dodaj podaną niżej zależność do konfiguracji Gradle lub Maven, zastępując zmienną VERSION_NUMBER odpowiednią wersją pakietu Driver SDK.

    Gradle

    Dodaj do konta build.gradle:

    dependencies {
      ...
      implementation 'com.google.android.libraries.mapsplatform.transportation:transportation-driver:VERSION_NUMBER'
    }
    

    Maven

    Dodaj do konta pom.xml:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.mapsplatform.transportation</groupId>
        <artifactId>transportation-driver</artifactId>
        <version>VERSION_NUMBER</version>
      </dependency>
    </dependencies>
    

Dodawanie klucza interfejsu API do aplikacji

Po dodaniu pakietu SDK sterownika do aplikacji dodaj do niej klucz interfejsu API. Musisz użyć klucza interfejsu API projektu uzyskanego podczas konfigurowania projektu programistycznego.

Z tej sekcji dowiesz się, jak przechowywać klucz interfejsu API, aby można było bezpieczniej się do niego odwoływać w aplikacji. Nie sprawdzaj klucza interfejsu API w systemie kontroli wersji. Powinien znajdować się w pliku local.properties, który znajduje się w katalogu głównym projektu. Więcej informacji o pliku local.properties znajdziesz w artykule Pliki właściwości Gradle.

Aby ułatwić sobie to zadanie, możesz użyć wtyczki do obsługi obiektów tajnych w Gradle na Androida.

Aby zainstalować wtyczkę i zapisać klucz interfejsu API:

  1. Otwórz plik build.gradle głównego poziomu i dodaj ten kod do elementu dependencies w obszarze buildscript.

    Zakręcony

    buildscript {
        dependencies {
            // ...
            classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0"
        }
    }
    

    Kotlin

    buildscript {
        dependencies {
            // ...
            classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.0")
        }
    }
    
  2. Otwórz plik build.gradle na poziomie aplikacji i dodaj do elementu plugins podany niżej kod.

    Zakręcony

    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
    

    Kotlin

    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    
  3. Jeśli używasz Android Studio, zsynchronizuj projekt z Gradle.

  4. Otwórz local.properties w katalogu na poziomie projektu, a następnie dodaj ten kod. Zastąp YOUR_API_KEY swoim kluczem interfejsu API.

    MAPS_API_KEY=YOUR_API_KEY
    
  5. W pliku AndroidManifest.xml przejdź do com.google.android.geo.API_KEY i zaktualizuj atrybut android:value w ten sposób:

    <meta-data
        android:name="com.google.android.geo.API_KEY"
        android:value="${MAPS_API_KEY}" />
    

Ten przykład przedstawia pełny plik manifestu przykładowej aplikacji:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.driverapidemo">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/_AppTheme">

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}" />

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Uwzględnij wymagane informacje o atrybucji w aplikacji

Jeśli korzystasz w aplikacji z pakietu Driver SDK, w sekcji informacji prawnych dotyczących aplikacji musisz umieścić tekst źródła oraz licencje open source. Informacje o autorach najlepiej dodać jako niezależne pozycje w menu lub pozycję Informacje.

Wymagany tekst atrybucji i licencje open source znajdziesz w pliku ZIP z pakietem Driver SDK:

  • NOTICE.txt
  • LICENSES.txt

Zależności

Jeśli do optymalizowania kompilacji używasz ProGuard, do pliku konfiguracji ProGuard może być konieczne dodanie tych wierszy:

-dontwarn com.google.**
-dontwarn okio.**

Minimalny obsługiwany poziom interfejsu API to 23.

Inicjowanie pakietu SDK

Do zainicjowania obiektu DriverContext wymagany jest identyfikator dostawcy (zwykle identyfikator projektu Google Cloud). Więcej informacji o konfigurowaniu projektu Google Cloud znajdziesz w artykule Uwierzytelnianie i autoryzacja.

Przed użyciem pakietu Driver SDK musisz najpierw zainicjować ten pakiet. Aby zainicjować pakiet SDK:

  1. Uzyskaj obiekt Navigator z NavigationApi.

    Java

    NavigationApi.getNavigator(
        this, // Activity
        new NavigationApi.NavigatorListener() {
          @Override
          public void onNavigatorReady(Navigator navigator) {
            // Keep a reference to the Navigator (used to configure and start nav)
            this.navigator = navigator;
          }
        }
    );
    

    Kotlin

    NavigationApi.getNavigator(
      this, // Activity
      object : NavigatorListener() {
        override fun onNavigatorReady(navigator: Navigator) {
          // Keep a reference to the Navigator (used to configure and start nav)
          this@myActivity.navigator = navigator
        }
      },
    )
    
  2. Utwórz obiekt DriverContext i wypełnij wymagane pola.

    Java

    DriverContext driverContext = DriverContext.builder(application)
        .setProviderId(providerId)
        .setVehicleId(vehicleId)
        .setAuthTokenFactory(authTokenFactory)
        .setNavigator(navigator)
        .setRoadSnappedLocationProvider(
            NavigationApi.getRoadSnappedLocationProvider(application))
        .build();
    

    Kotlin

    val driverContext =
      DriverContext.builder(application)
        .setProviderId(providerId)
        .setVehicleId(vehicleId)
        .setAuthTokenFactory(authTokenFactory)
        .setNavigator(navigator)
        .setRoadSnappedLocationProvider(NavigationApi.getRoadSnappedLocationProvider(application))
        .build()
    
  3. Użyj obiektu DriverContext do zainicjowania *DriverApi.

    Java

    RidesharingDriverApi ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext);
    

    Kotlin

    val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext)
    
  4. Pobierz RidesharingVehicleReporter z obiektu API. (*VehicleReporter powoduje rozszerzenie NavigationVehicleReporter).

    Java

    RidesharingVehicleReporter vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter();
    

    Kotlin

    val vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter()
    

Uwierzytelniam w sieci AuthTokenFactory

Gdy pakiet Driver SDK generuje aktualizacje lokalizacji, musi je wysłać do serwera Fleet Engine. Aby uwierzytelnić te żądania, pakiet SDK sterowników wywoła metodę AuthTokenFactory dostarczaną przez element wywołujący. Fabryka jest odpowiedzialna za generowanie tokenów uwierzytelniania w czasie aktualizacji lokalizacji.

Sposób generowania tokenów zależy od sytuacji każdego dewelopera. Jednak wdrożenie prawdopodobnie będzie wymagało:

  • pobranie tokena uwierzytelniania, np. w formacie JSON, z serwera HTTPS;
  • analizowanie i zapisywanie tokena w pamięci podręcznej
  • odśwież token, gdy straci ważność

Informacje o tokenach, których oczekuje serwer Fleet Engine, znajdziesz w sekcji Tworzenie tokena internetowego JSON (JWT) do autoryzacji.

Oto szkielet implementacji obiektu AuthTokenFactory:

Java

class JsonAuthTokenFactory implements AuthTokenFactory {
  private String token;  // initially null
  private long expiryTimeMs = 0;

  // This method is called on a thread whose only responsibility is to send
  // location updates. Blocking is OK, but just know that no location updates
  // can occur until this method returns.
  @Override
  public String getToken(AuthTokenContext authTokenContext) {
    if (System.currentTimeMillis() > expiryTimeMs) {
      // The token has expired, go get a new one.
      fetchNewToken(authTokenContext.getVehicleId());
    }
    return token;
  }

  private void fetchNewToken(String vehicleId) {
    String url =
        new Uri.Builder()
            .scheme("https")
            .authority("yourauthserver.example")
            .appendPath("token")
            .appendQueryParameter("vehicleId", vehicleId)
            .build()
            .toString();

    try (Reader r = new InputStreamReader(new URL(url).openStream())) {
      com.google.gson.JsonObject obj
          = com.google.gson.JsonParser.parseReader(r).getAsJsonObject();
      token = obj.get("Token").getAsString();
      expiryTimeMs = obj.get("TokenExpiryMs").getAsLong();

      // The expiry time could be an hour from now, but just to try and avoid
      // passing expired tokens, we subtract 10 minutes from that time.
      expiryTimeMs -= 10 * 60 * 1000;
    } catch (IOException e) {
      // It's OK to throw exceptions here. The StatusListener you passed to
      // create the DriverContext class will be notified and passed along the failed
      // update warning.
      throw new RuntimeException("Could not get auth token", e);
    }
  }
}

Kotlin

class JsonAuthTokenFactory : AuthTokenFactory() {

  private var token: String = ""
  private var expiryTimeMs: Long = 0

  // This method is called on a thread whose only responsibility is to send
  // location updates. Blocking is OK, but just know that no location updates
  // can occur until this method returns.
  override fun getToken(context: AuthTokenContext): String {
    if (System.currentTimeMillis() > expiryTimeMs) {
      // The token has expired, go get a new one.
      fetchNewToken(authTokenContext.getVehicleId())
    }
     return token
  }

  fun fetchNewToken(vehicleId: String) {
    val url =
      Uri.Builder()
        .scheme("https")
        .authority("yourauthserver.example")
        .appendPath("token")
        .appendQueryParameter("vehicleId", vehicleId)
        .build()
        .toString()

    try {
      val reader = InputStreamReader(URL(url).openStream())

      reader.use {
        val obj = com.google.gson.JsonParser.parseReader(r).getAsJsonObject()

        token = obj.get("ServiceToken").getAsString()
        expiryTimeMs = obj.get("TokenExpiryMs").getAsLong()

        // The expiry time could be an hour from now, but just to try and avoid
        // passing expired tokens, we subtract 10 minutes from that time.
        expiryTimeMs -= 10 * 60 * 1000
      }
    } catch (e: IOException) {
      // It's OK to throw exceptions here. The StatusListener you passed to
      // create the DriverContext class will be notified and passed along the failed
      // update warning.
      throw RuntimeException("Could not get auth token", e)
    }
  }
}

W tej implementacji używa on wbudowanego klienta HTTP Java do pobierania tokena w formacie JSON z serwera uwierzytelniania dewelopera. Token zostaje zapisany do ponownego użycia. Token jest pobierany ponownie, jeśli do wygaśnięcia starego tokena minęło 10 minut.

Twoja implementacja może działać inaczej, np. używać wątku w tle do odświeżania tokenów.

Wyjątki w AuthTokenFactory będą traktowane jako przejściowe, chyba że będą się powtarzać. Po kilku próbach pakiet SDK sterownika założy, że błąd jest trwały, i przestanie próbować wysyłać aktualizacje.

Raportowanie stanu i błędów w StatusListener

Ponieważ pakiet SDK sterownika wykonuje działania w tle, używaj interfejsu StatusListener do wywoływania powiadomień po wystąpieniu określonych zdarzeń, np. błędów, ostrzeżeń czy komunikatów debugowania. Błędy mogą być przejściowe (np. BACKEND_CONNECTIVITY_ERROR) lub powodować trwałe zatrzymanie aktualizacji lokalizacji (np. VEHICLE_NOT_FOUND, co wskazuje na błąd konfiguracji).

Podajesz opcjonalną implementację StatusListener, taką jak:

Java

class MyStatusListener implements StatusListener {
  /** Called when background status is updated, during actions such as location reporting. */
  @Override
  public void updateStatus(
      StatusLevel statusLevel, StatusCode statusCode, String statusMsg) {
    // Status handling stuff goes here.
    // StatusLevel may be DEBUG, INFO, WARNING, or ERROR.
    // StatusCode may be DEFAULT, UNKNOWN_ERROR, VEHICLE_NOT_FOUND,
    // BACKEND_CONNECTIVITY_ERROR, or PERMISSION_DENIED.
  }
}

Kotlin

class MyStatusListener : StatusListener() {
  /** Called when background status is updated, during actions such as location reporting. */
  override fun updateStatus(statusLevel: StatusLevel, statusCode: StatusCode, statusMsg: String) {
    // Status handling stuff goes here.
    // StatusLevel may be DEBUG, INFO, WARNING, or ERROR.
    // StatusCode may be DEFAULT, UNKNOWN_ERROR, VEHICLE_NOT_FOUND,
    // BACKEND_CONNECTIVITY_ERROR, or PERMISSION_DENIED.
  }
}

Uwagi na temat SSL/TLS

Wewnętrznie implementacja pakietu Driver SDK używa SSL/TLS do bezpiecznej komunikacji z serwerem Fleet Engine. Starsze wersje Androida (interfejsy API w wersji 19 lub starszej) mogą wymagać poprawki SecurityProvider, aby mogły komunikować się z serwerem. Więcej informacji o korzystaniu z protokołu SSL na Androidzie znajdziesz w tym artykule. Artykuł zawiera też przykładowy kod do zainstalowania poprawek u dostawcy zabezpieczeń.

Włączam aktualizacje lokalizacji

Gdy masz instancję *VehicleReporter, włączenie aktualizacji lokalizacji jest proste:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();

Kotlin

val reporter = ...

reporter.enableLocationTracking()

Aktualizacje lokalizacji są wysyłane w regularnych odstępach czasu, gdy stan pojazdu to ONLINE. Pamiętaj, że wywołanie reporter.enableLocationTracking() nie powoduje automatycznego ustawienia stanu pojazdu na ONLINE. Musisz wyraźnie ustawić stan pojazdu.

Domyślna częstotliwość raportowania wynosi 10 sekund. Okres raportowania można zmienić za pomocą parametru reporter.setLocationReportingInterval(long, TimeUnit). Minimalny obsługiwany interwał aktualizacji to 5 sekund. Częstsze aktualizacje mogą powodować wolniejsze przesyłanie żądań i błędów.

Wyłączanie aktualizacji lokalizacji

Po zakończeniu zmiany przez kierowcę możesz zatrzymać aktualizacje lokalizacji i oznaczyć pojazd jako offline, dzwoniąc do kierowcy DeliveryVehicleReporter.disableLocationTracking lub RidesharingVehicleReporter.disableLocationTracking.

To wywołanie spowoduje zaplanowanie ostatniej aktualizacji z natychmiastową dostawą, co oznacza, że pojazd jest offline. Ta aktualizacja nie będzie zawierać informacji o lokalizacji użytkownika.

Ustawianie stanu pojazdu

Gdy aktualizacje lokalizacji są włączone, ustawienie stanu pojazdu na ONLINE spowoduje, że pojazd będzie dostępny w odpowiedzi na zapytania SearchVehicles. Podobnie oznaczenie pojazdu jako OFFLINE spowoduje oznaczenie go jako niedostępnego.

Stan pojazdu możesz ustawić po stronie serwera (patrz Aktualizowanie pojazdu) lub bezpośrednio w pakiecie SDK kierowcy:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();
reporter.setVehicleState(VehicleState.ONLINE);

Kotlin

val reporter = ...

reporter.enableLocationTracking()
reporter.setVehicleState(VehicleState.ONLINE)

Po włączeniu aktualizacji lokalizacji wywołanie setVehicleState zostanie rozpowszechnione przy następnej aktualizacji lokalizacji.

Jeśli oznaczysz pojazd jako ONLINE, gdy śledzenie lokalizacji nie jest włączone, wyświetli się IllegalStateException. Pojazd można oznaczyć jako OFFLINE, jeśli śledzenie lokalizacji nie jest jeszcze włączone lub jawnie wyłączone. Spowoduje to natychmiastowe aktualizacje. Wywołanie RidesharingVehicleReporter.disableLocationTracking() ustawi stan pojazdu na OFFLINE.

Pamiętaj, że interfejs setVehicleState zwraca wyniki natychmiast, a aktualizacje są przeprowadzane w wątku aktualizacji lokalizacji. Podobnie jak w przypadku błędów w przypadku aktualizacji lokalizacji, błędy związane z aktualizacją stanu pojazdu są rozpowszechniane przy użyciu opcjonalnie podanego w polu DriverContext parametru StatusListener.