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

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

ערכת ה-SDK של Drive עוזרת לשמור על קשר עם השירותים של 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, ממשקי API שירותים > לספרייה ולאחר מכן לחפש ולהפעיל את ה-SDK של מפות Google עבור Android.

  3. כדי לקבל מפתח API לפרויקט, בוחרים באפשרות ממשקי API שירותים > פרטי כניסה > יצירת פרטי כניסה > מפתח API. מידע נוסף על קבלת מפתח API זמין במאמר קבלת מפתח API

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

ה-Driver SDK זמין ממאגר Google Maven. מאגר כולל את קובצי Project Object Model (.pom) ואת Javadocs של ה-SDK. כדי להוסיף את 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 של מנהל התקן תלוי ב-Navigation SDK, התלות הזו מוגדרת כך שאם נדרשת גרסה ספציפית של Navigation SDK, היא צריכה מוגדר באופן מפורש בקובץ תצורת build, כמו בדוגמה הבאה, אם לא תשמיט את בלוק הקוד שצוין, הפרויקט תמיד ירד את הגרסה האחרונה של SDK ניווט בגרסה הראשית. חשוב לשים לב שההתנהגויות המשולבות של הגרסאות האחרונות של Driver SDK ה-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 באפליקציה, צריך לכלול את ה-SDK. טקסט ייחוס ורישיונות קוד פתוח כחלק מההודעות המשפטיות של האפליקציה . מומלץ לכלול את הקרדיטים האלה כאפשרות עצמאית בתפריט, או חלק מהאפשרות מידע כללי בתפריט.

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

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

יחסי תלות

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

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

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

אתחול ה-SDK

צריך להזין מזהה ספק (בדרך כלל מזהה הפרויקט ב-Google Cloud) כדי: צריך לאתחל את האובייקט DriverContext. לפרטים נוספים על הגדרת לפרויקט ב-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. המפעל אחראי על יצירת אסימוני האימות במיקום שעת העדכון.

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

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

ניתן למצוא פרטים על האסימונים שנדרשים על ידי שרת Fleet Engine בכתובת יצירת אסימון רשת מבוסס JSON (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 דקות של מועד התפוגה.

יכול להיות שההטמעה תתבצע באופן שונה, כמו שימוש ב-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.