תחילת העבודה עם SDK של Drive ל-Android

תוכלו להשתמש ב-Driver SDK כדי לשפר את הניווט והמעקב לאפליקציה של הנסיעה ושל התקדמות ההזמנה. ה-Driver SDK מספק עדכונים לגבי מיקום הרכב ורשימת המשימות ל-Fleet Engine לגבי נסיעות ומשלוחים על פי דרישה.

ה-Driver SDK מאפשר לשירותי ה-Fleet Engine ולשירותים המותאמים אישית שלכם להיות מודעים למיקום ולמצב של הרכב. לדוגמה, הרכב יכול להיות ONLINE או OFFLINE, ומיקום הרכב משתנה במהלך הנסיעה.

דרישות מערכת מינימליות

במכשיר הנייד צריכה להיות מותקנת מערכת Android מגרסה 6.0 (API ברמה 23) ואילך.

הגדרת build ויחסי תלות

גרסאות 4.99 ואילך של Driver SDK זמינות ממאגר Google Maven.

Gradle

מוסיפים לקובץ build.gradle את הנתונים הבאים:

repositories {
    ...
    google()
}

Maven

מוסיפים לקובץ pom.xml את הנתונים הבאים:

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

הגדרות הפרויקט

כדי להשתמש ב-Driver SDK, האפליקציה צריכה לטרגט ל-minSdkVersion בגרסה 23 ואילך.

כדי להריץ אפליקציה שנוצרה באמצעות Driver SDK, צריך להתקין במכשיר ה-Android את Google Play Services.

הגדרת פרויקט פיתוח

כדי להגדיר את פרויקט הפיתוח ולקבל מפתח API לפרויקט במסוף Google Cloud:

  1. יוצרים פרויקט חדש במסוף Google Cloud או בוחרים פרויקט קיים לשימוש עם Driver SDK. ממתינים כמה דקות עד שהפרויקט החדש מופיע במסוף Google Cloud.

  2. כדי להריץ את אפליקציית ההדגמה, לפרויקט שלכם צריכה להיות גישה ל-SDK של מפות Google ל-Android. במסוף Google Cloud, בוחרים באפשרות APIs & Services > Library ואז מחפשים את ה-SDK של מפות Google ל-Android ומפעילים אותו.

  3. כדי לקבל מפתח API לפרויקט, בוחרים באפשרות APIs & Services > Credentials > Create credentials > API key. במאמר קבלת מפתח API תוכלו לקרוא מידע נוסף על קבלת מפתח API.

הוספת ה-Driver SDK לאפליקציה

ה-Driver SDK זמין ממאגר Google Maven. המאגר כולל את קובצי Project Object Model (.pom) של ה-SDK ואת Javadocs. כדי להוסיף את Driver SDK לאפליקציה:

  1. מוסיפים את התלות הבאה להגדרות של Gradle או Maven, ומחליפים את ה-placeholder הזה VERSION_NUMBER בגרסה הרצויה של Driver SDK.

    Gradle

    צריך להוסיף את הפרטים הבאים לbuild.gradle:

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

    Maven

    צריך להוסיף את הפרטים הבאים לpom.xml:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.mapsplatform.transportation</groupId>
        <artifactId>transportation-driver</artifactId>
        <version>VERSION_NUMBER</version>
      </dependency>
    </dependencies>
    
  2. ה-SDK של Drive תלוי ב-Navigation SDK. התלות הזו מוגדרת כך שאם נדרשת גרסה ספציפית של Navigation SDK, יש להגדיר אותה באופן מפורש בקובץ תצורת ה-build כמו בדוגמה הבאה. אם תשמיטו את בלוק הקוד שצוין, הפרויקט יוכל להוריד תמיד את הגרסה האחרונה של ה-Navigation SDK בגרסת הגרסה הראשית. שימו לב שההתנהגויות המשולבות של הגרסאות האחרונות של Driver SDK ו-Navigation SDK עברו בדיקות קפדניות לפני השקת הגרסאות.

    כדאי לארגן את תצורת התלות של סביבות הפיתוח ולפרסם בהתאם.

    Gradle

    צריך להוסיף את הפרטים הבאים לbuild.gradle:

    dependencies {
      ...
      implementation 'com.google.android.libraries.navigation:navigation:5.0.0'
    }
    

    Maven

    צריך להוסיף את הפרטים הבאים לpom.xml:

    <dependencies>
      ...
      <dependency>
        <groupId>com.google.android.libraries.navigation</groupId>
        <artifactId>navigation</artifactId>
        <version>5.0.0</version>
      </dependency>
    </dependencies>
    

הוספה של מפתח ה-API לאפליקציה

אחרי שמוסיפים את Driver SDK לאפליקציה, צריך להוסיף את מפתח ה-API לאפליקציה. כדי לעשות זאת, צריך להשתמש במפתח ה-API של הפרויקט שקיבלתם כשהגדרתם את פרויקט הפיתוח.

בקטע הזה מוסבר איך לאחסן את מפתח ה-API כדי שהאפליקציה תוכל להפנות אליו בצורה מאובטחת יותר. אל תבדקו את מפתח ה-API במערכת ניהול הגרסאות שלכם. צריך לאחסן אותו בקובץ local.properties, שנמצא בתיקיית השורש של הפרויקט. למידע נוסף על הקובץ local.properties ראו קובצי מאפייני Gradle.

כדי לייעל את המשימה הזו, אפשר להשתמש בפלאגין של Secrets Gradle ל-Android.

כדי להתקין את הפלאגין ולאחסן את מפתח ה-API:

  1. פותחים את קובץ build.gradle ברמה הבסיסית (root) ומוסיפים את הקוד הבא לרכיב dependencies בקטע buildscript.

    מגניב

    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. פותחים את הקובץ build.gradle ברמת האפליקציה ומוסיפים את הקוד הבא לרכיב plugins.

    מגניב

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

    Kotlin

    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
    
  3. אם אתם משתמשים ב-Android Studio, אתם יכולים לסנכרן את הפרויקט עם Gradle.

  4. פותחים את local.properties בספרייה ברמת הפרויקט ומוסיפים את הקוד הבא. מחליפים את YOUR_API_KEY במפתח ה-API שלכם.

    MAPS_API_KEY=YOUR_API_KEY
    
  5. בקובץ AndroidManifest.xml, עוברים אל com.google.android.geo.API_KEY ומעדכנים את המאפיין android:value באופן הבא:

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

בדוגמה הבאה מוצג מניפסט מלא של אפליקציה לדוגמה:

<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>

לכלול את הייחוסים הנדרשים באפליקציה

אם אתם משתמשים ב-Driver SDK באפליקציה, אתם צריכים לכלול את טקסט השיוך ורישיונות קוד פתוח כחלק מהקטע 'הודעות משפטיות' של האפליקציה. מומלץ לכלול את הקרדיטים האלה כאפשרות עצמאית בתפריט או כחלק מפריט בתפריט מידע כללי.

ניתן למצוא את פרטי הרישיונות בקובץ 'third_party_Licenses.txt' בקובץ ה-AAR מהארכיון.

במאמר https://developers.google.com/android/guides/opensource מוסבר איך מוסיפים הודעות בנושא קוד פתוח.

יחסי תלות

אם אתם משתמשים ב-ProGuard כדי לבצע אופטימיזציה של גרסאות ה-build שלכם, יכול להיות שתצטרכו להוסיף את השורות הבאות לקובץ התצורה של ProGuard:

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

רמת ה-API המינימלית הנתמכת היא 23.

אתחול ה-SDK

כדי לאתחל את האובייקט DriverContext, צריך לציין מזהה ספק (בדרך כלל מזהה הפרויקט ב-Google Cloud). למידע נוסף על הגדרת הפרויקט ב-Google Cloud, קראו את המאמר אימות והרשאה.

לפני השימוש ב-Driver SDK, צריך קודם לאתחל את ה-Navigation SDK. כדי לאתחל את ה-SDK:

  1. מקבלים אובייקט Navigator מה-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. יוצרים אובייקט DriverContext כדי לאכלס את שדות החובה.

    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. צריך להשתמש באובייקט DriverContext כדי לאתחל את *DriverApi.

    Java

    RidesharingDriverApi ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext);
    

    Kotlin

    val ridesharingDriverApi = RidesharingDriverApi.createInstance(driverContext)
    
  4. מקבלים את ה-RidesharingVehicleReporter מהאובייקט של ה-API. (*VehicleReporter נמשך NavigationVehicleReporter.)

    Java

    RidesharingVehicleReporter vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter();
    

    Kotlin

    val vehicleReporter = ridesharingDriverApi.getRidesharingVehicleReporter()
    

מתבצע אימות עם AuthTokenFactory

כש-Driver SDK יוצר עדכוני מיקום, הוא צריך לשלוח את העדכונים האלה לשרת Fleet Engine. כדי לאמת את הבקשות האלה, Driver SDK ישלח קריאה למופע של AuthTokenFactory שנמסר על ידי ה-callr. המפעל אחראי ליצור אסימוני אימות בזמן עדכון המיקום.

האופן שבו נוצרים אסימונים מדויקים יהיה ספציפי למצב של כל מפתח. אבל סביר להניח שיהיה צורך:

  • אחזור אסימון אימות משרת HTTPS, כנראה בפורמט JSON
  • לנתח את האסימון ולשמור אותו במטמון
  • לרענן את האסימון כשהתוקף שלו יפוג

במאמר יצירת אסימון אינטרנט מסוג JSON (JWT) לצורך הרשאה תוכלו לקרוא פרטים נוספים על האסימונים הנדרשים לשרת Fleet Engine.

זהו הטמעת בסיס של 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)
    }
  }
}

ההטמעה הספציפית הזו משתמשת בלקוח ה-HTTP המובנה ב-Java כדי לאחזר אסימון בפורמט JSON משרת האימות של המפתח. האסימון נשמר לשימוש חוזר. האסימון מאוחזר מחדש אם האסימון הישן נמצא תוך 10 דקות ממועד התפוגה שלו.

יכול להיות שההטמעה תתבצע באופן שונה, למשל שימוש ב-thread ברקע כדי לרענן אסימונים.

חריגים ב-AuthTokenFactory ייחשבו כארעיים, אלא אם הם יקרו שוב ושוב. לאחר מספר ניסיונות, ערכת ה-SDK של מנהל התקן תניח שהשגיאה היא קבועה ותפסיק לנסות לשלוח עדכונים.

דיווח סטטוס ושגיאות באמצעות StatusListener

מכיוון ש-Driver SDK מבצע פעולות ברקע, אפשר להשתמש ב-StatusListener כדי להפעיל התראות כשמתרחשים אירועים מסוימים, כמו שגיאות, אזהרות או הודעות ניפוי באגים. שגיאות עשויות להיות זמניות (כמו BACKEND_CONNECTIVITY_ERROR), או שעדכוני המיקום יופסקו באופן סופי (למשל VEHICLE_NOT_FOUND, מה שמעיד על שגיאה בהגדרות).

מספקים הטמעה אופציונלית של StatusListener, כמו בדוגמה הבאה:

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.
  }
}

הערות לגבי SSL/TLS

באופן פנימי, הטמעת ה-Driver SDK משתמשת ב-SSL/TLS כדי לתקשר באופן מאובטח עם שרת Fleet Engine. בגרסאות ישנות יותר של Android (גרסאות API 19 ומטה) יכול להיות שיהיה צורך בתיקון SecurityProvider כדי לתקשר עם השרת. מידע נוסף על עבודה עם SSL ב-Android זמין במאמר הזה. המאמר גם כולל דוגמאות קוד לתיקון ספק האבטחה.

הפעלת עדכוני מיקום

אחרי שיש לכם מכונה *VehicleReporter, הפעלת עדכוני המיקום מתבצעת ישירות:

Java

RidesharingVehicleReporter reporter = ...;

reporter.enableLocationTracking();

Kotlin

val reporter = ...

reporter.enableLocationTracking()

עדכוני המיקום נשלחים במרווחי זמן קבועים כשמצב הרכב הוא ONLINE. שימו לב שהפעלה של reporter.enableLocationTracking() לא מגדירה באופן אוטומטי את מצב הרכב ל-ONLINE. צריך להגדיר את מצב הרכב באופן מפורש.

כברירת מחדל, מרווח הזמן לדיווח הוא 10 שניות. אפשר לשנות את מרווח הזמן לדיווח באמצעות reporter.setLocationReportingInterval(long, TimeUnit). מרווח הזמן המינימלי הנתמך לעדכון הוא 5 שניות. עדכונים תכופים יותר עלולים לגרום לעיכובים ולשגיאות בבקשות ובשגיאות.

השבתה של עדכוני המיקום

בסיום החלפת המצב של הנהג, אפשר לעצור את עדכוני המיקום ולסמן את הרכב במצב אופליין באמצעות קריאה ל-DeliveryVehicleReporter.disableLocationTracking או ל-RidesharingVehicleReporter.disableLocationTracking.

השיחה תגרום לתזמון של עדכון סופי אחד למסירה מיידית, כך שהרכב במצב אופליין. העדכון הזה לא יכלול את מיקום המשתמש.

הגדרת מצב הרכב

כשעדכוני המיקום מופעלים, הגדרת מצב הרכב ל-ONLINE תאפשר לרכב להיות זמין לשאילתות SearchVehicles. באופן דומה, סימון רכב כ-OFFLINE יסמן את הרכב כלא זמין.

אפשר להגדיר את מצב הרכב בצד השרת (ראו עדכון רכב), או ישירות ב-Driver SDK:

Java

RidesharingVehicleReporter reporter = ...;

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

Kotlin

val reporter = ...

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

כאשר עדכוני המיקום מופעלים, הקריאה ל-setVehicleState תופץ בעדכון המיקום הבא.

כשמסמנים רכב כ-ONLINE כשהמעקב אחר המיקום לא מופעל, התוצאה תהיה IllegalStateException. אפשר לסמן רכב כ-OFFLINE כשהמעקב אחר המיקום עדיין לא מופעל או מושבת באופן מפורש. התוצאה תהיה עדכון מיידי. קריאה ל-RidesharingVehicleReporter.disableLocationTracking() תגדיר את מצב הרכב ל-OFFLINE.

שימו לב שהשדה setVehicleState חוזר מיד והעדכונים מתבצעים בשרשור של עדכון המיקום. בדומה לטיפול בשגיאות בעדכוני מיקומים, גם שגיאות בעדכון מצב הרכב מופצות באמצעות המאפיין StatusListener שמוגדר ב-DriverContext (אופציונלי).