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

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

בזכות ה-SDK של הנהג, שירותי ה-Flet 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>

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

כדי להשתמש ב-SDK של Drive, האפליקציה צריכה לטרגט ל-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.

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

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

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

    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 של מנהל ההתקן תלוי ב-Navigation SDK. התלות הזו מוגדרת כך שאם דרושה גרסה ספציפית של Navigation SDK, היא צריכה להיות מוגדרת באופן מפורש בקובץ תצורת ה-build כמו בדוגמה הבאה. השמטת גוש הקוד שהוזכר תאפשר לפרויקט להוריד תמיד את הגרסה האחרונה של ה-API של הניווט בגרסה הראשית. שימו לב שההתנהגויות המשולבות של הגרסאות העדכניות של 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 זמין במאמר קבצים של מאפייני גראנד.

כדי לייעל את המשימה הזו, תוכלו להשתמש בפלאגין של 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>

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

אם השתמשת ב-Drive 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, צריך לאתחל את הניווט 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. כדי לאמת את הבקשות האלה, ה-SDK של Drive יבצע קריאה למכונה AuthTokenFactory שסופקה על ידי המתקשר. המפעל אחראי ליצירת אסימוני האימות בזמן עדכון המיקום.

האופן שבו נוצרים אסימונים ספציפיים יהיה ספציפי למצבו של כל מפתח. עם זאת, סביר להניח שתצטרכו לבצע את ההטמעה:

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

לפרטים על האסימונים הצפויים על ידי שרת Fleet Engine, ראו יצירת JSON Web Token (JWT) להרשאה.

הנה יישום בסיסי של 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 דקות מזמן התפוגה שלו.

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

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

דיווח סטטוס ושגיאות עם StatusListener

מכיוון שה-Drive 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 יסמן את הרכב כלא זמין.

אפשר להגדיר את מצב הרכב בצד השרת (ראו עדכון כלי רכב) או ישירות ב-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.