การตรวจสอบความพร้อมใช้งานของ API ด้วย ModuleInstallClient

ตามที่อธิบายไว้ในบทความภาพรวมของบริการ Google Play SDK ที่ขับเคลื่อนโดยบริการ Google Play สนับสนุนโดยบริการในอุปกรณ์ในอุปกรณ์ Android ที่ผ่านการรับรองจาก Google เพื่อประหยัดพื้นที่เก็บข้อมูลและหน่วยความจำสำหรับอุปกรณ์ทั้งหมด บริการบางอย่างจะติดตั้งแบบออนดีมานด์เมื่อเห็นได้ชัดว่าอุปกรณ์เครื่องใดเครื่องหนึ่งจำเป็นต้องใช้ฟังก์ชันการทำงานที่เกี่ยวข้อง ตัวอย่างเช่น ML Kit ให้ตัวเลือกนี้ไว้ เมื่อใช้รุ่นในบริการ Google Play

กรณีที่พบบ่อยที่สุดคือการดาวน์โหลดและติดตั้งบริการ (หรือ "โมดูล") ควบคู่ไปกับแอปที่ต้องใช้บริการดังกล่าว โดยอิงตามทรัพยากร Dependency ใน AndroidManifest.xml ของ SDK เพื่อการควบคุมที่มากขึ้น API การติดตั้งโมดูลมีความสามารถในการตรวจสอบความพร้อมใช้งานของโมดูล ขอการติดตั้งโมดูล ตรวจสอบสถานะคำขอ และจัดการข้อผิดพลาด

ทำตามขั้นตอนด้านล่างเพื่อให้แน่ใจว่า API จะพร้อมใช้งานใน ModuleInstallClient โปรดทราบว่าข้อมูลโค้ดด้านล่างใช้ TensorFlow Lite SDK (play-services-tflite-java) เป็นไลบรารีตัวอย่าง แต่ขั้นตอนเหล่านี้ใช้ได้กับไลบรารีทั้งหมดที่ผสานรวมกับ OptionalModuleApi คู่มือนี้จะอัปเดตข้อมูลเพิ่มเติมเมื่อ SDK จำนวนมากขึ้นรองรับ

ก่อนเริ่มต้น

หากต้องการเตรียมแอป ให้ทำตามขั้นตอนในส่วนต่อไปนี้

ข้อกำหนดเบื้องต้นของแอป

ตรวจสอบว่าไฟล์บิลด์ของแอปใช้ค่าต่อไปนี้

  • minSdkVersion ตั้งแต่ 19 ขึ้นไป

กำหนดค่าแอป

  1. ในไฟล์ settings.gradle ระดับบนสุด ให้ใส่ที่เก็บ Maven ของ Google และที่เก็บส่วนกลาง Maven ไว้ในบล็อก dependencyResolutionManagement

    dependencyResolutionManagement {
        repositories {
            google()
            mavenCentral()
        }
    }
    
  2. ในไฟล์บิลด์ Gradle ของโมดูล (โดยปกติคือ app/build.gradle) ให้เพิ่มทรัพยากร Dependency ของบริการ Google Play สำหรับ play-services-base และ play-services-tflite-java ดังนี้

    dependencies {
      implementation 'com.google.android.gms:play-services-base:18.3.0'
      implementation 'com.google.android.gms:play-services-tflite-java:16.2.0-beta02'
    }
    

ตรวจสอบความพร้อมใช้งานของโมดูล

  1. รับอินสแตนซ์ของ ModuleInstallClient:

    Kotlin

    val moduleInstallClient = ModuleInstall.getClient(context)
    

    Java

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
    
  2. ตรวจสอบความพร้อมใช้งานของโมดูลที่ไม่บังคับโดยใช้ OptionalModuleApi ของโมดูลต่อไปนี้

    Kotlin

    val optionalModuleApi = TfLite.getClient(context)
    moduleInstallClient
      .areModulesAvailable(optionalModuleApi)
      .addOnSuccessListener {
        if (it.areModulesAvailable()) {
          // Modules are present on the device...
        } else {
          // Modules are not present on the device...
        }
      }
      .addOnFailureListener {
        // Handle failure...
      }
    

    Java

    OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
    moduleInstallClient
        .areModulesAvailable(optionalModuleApi)
        .addOnSuccessListener(
            response -> {
              if (response.areModulesAvailable()) {
                // Modules are present on the device...
              } else {
                // Modules are not present on the device...
              }
            })
        .addOnFailureListener(
            e -> {
              // Handle failure…
            });
    

ส่งคำขอติดตั้งที่มีการเลื่อนเวลา

  1. รับอินสแตนซ์ของ ModuleInstallClient:

    Kotlin

    val moduleInstallClient = ModuleInstall.getClient(context)
    

    Java

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
    
  2. ส่งคำขอที่เลื่อนเวลาออกไป ดังนี้

    Kotlin

    val optionalModuleApi = TfLite.getClient(context)
    moduleInstallClient.deferredInstall(optionalModuleApi)
    

    Java

    OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
    moduleInstallClient.deferredInstall(optionalModuleApi);
    

ส่งคำขอติดตั้งโมดูลแบบเร่งด่วน

  1. รับอินสแตนซ์ของ ModuleInstallClient:

    Kotlin

    val moduleInstallClient = ModuleInstall.getClient(context)
    

    Java

    ModuleInstallClient moduleInstallClient = ModuleInstall.getClient(context);
    
  2. (ไม่บังคับ) สร้าง InstallStatusListener เพื่อจัดการการอัปเดตสถานะการติดตั้ง

    หากต้องการตรวจสอบความคืบหน้าในการดาวน์โหลดใน UI ที่กำหนดเอง (เช่น แถบความคืบหน้า) คุณจะสร้าง InstallStatusListener เพื่อรับการอัปเดตสถานะการติดตั้งได้

    Kotlin

    inner class ModuleInstallProgressListener : InstallStatusListener {
      override fun onInstallStatusUpdated(update: ModuleInstallStatusUpdate) {
        // Progress info is only set when modules are in the progress of downloading.
        update.progressInfo?.let {
          val progress = (it.bytesDownloaded * 100 / it.totalBytesToDownload).toInt()
          // Set the progress for the progress bar.
          progressBar.setProgress(progress)
        }
    
        if (isTerminateState(update.installState)) {
          moduleInstallClient.unregisterListener(this)
        }
      }
    
      fun isTerminateState(@InstallState state: Int): Boolean {
        return state == STATE_CANCELED || state == STATE_COMPLETED || state == STATE_FAILED
      }
    }
    
    val listener = ModuleInstallProgressListener()
    

    Java

    static final class ModuleInstallProgressListener implements InstallStatusListener {
        @Override
        public void onInstallStatusUpdated(ModuleInstallStatusUpdate update) {
          ProgressInfo progressInfo = update.getProgressInfo();
          // Progress info is only set when modules are in the progress of downloading.
          if (progressInfo != null) {
            int progress =
                (int)
                    (progressInfo.getBytesDownloaded() * 100 / progressInfo.getTotalBytesToDownload());
            // Set the progress for the progress bar.
            progressBar.setProgress(progress);
          }
          // Handle failure status maybe…
    
          // Unregister listener when there are no more install status updates.
          if (isTerminateState(update.getInstallState())) {
    
            moduleInstallClient.unregisterListener(this);
          }
        }
    
        public boolean isTerminateState(@InstallState int state) {
          return state == STATE_CANCELED || state == STATE_COMPLETED || state == STATE_FAILED;
        }
      }
    
    InstallStatusListener listener = new ModuleInstallProgressListener();
    
  3. กำหนดค่า ModuleInstallRequest และเพิ่ม OptionalModuleApi ลงในคำขอ

    Kotlin

    val optionalModuleApi = TfLite.getClient(context)
    val moduleInstallRequest =
      ModuleInstallRequest.newBuilder()
        .addApi(optionalModuleApi)
        // Add more APIs if you would like to request multiple optional modules.
        // .addApi(...)
        // Set the listener if you need to monitor the download progress.
        // .setListener(listener)
        .build()
    

    Java

    OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
    ModuleInstallRequest moduleInstallRequest =
        ModuleInstallRequest.newBuilder()
            .addApi(optionalModuleApi)
            // Add more API if you would like to request multiple optional modules
            //.addApi(...)
            // Set the listener if you need to monitor the download progress
            //.setListener(listener)
            .build();
    
  4. ส่งคำขอติดตั้ง

    Kotlin

    moduleInstallClient
      .installModules(moduleInstallRequest)
      .addOnSuccessListener {
        if (it.areModulesAlreadyInstalled()) {
          // Modules are already installed when the request is sent.
        }
      }
      .addOnFailureListener {
        // Handle failure…
      }
    

    Java

    moduleInstallClient.installModules(moduleInstallRequest)
        .addOnSuccessListener(
            response -> {
              if (response.areModulesAlreadyInstalled()) {
                // Modules are already installed when the request is sent.
              }
            })
        .addOnFailureListener(
            e -> {
              // Handle failure...
            });
    

การทดสอบในเครื่องกับ FakeModuleInstallClient

SDK บริการ Google Play มี FakeModuleInstallClient เพื่อให้คุณจำลองผลลัพธ์ของ API การติดตั้งโมดูลในการทดสอบโดยใช้การแทรก Dependency

ข้อกำหนดเบื้องต้นของแอป

กำหนดค่าแอปให้ใช้เฟรมเวิร์กการแทรกทรัพยากร Dependency

แทนที่ ModuleInstallClient ด้วย FakeModuleInstallClient ในการทดสอบ

  1. เพิ่มการอ้างอิง:

    ในไฟล์บิลด์ Gradle ของโมดูล (โดยปกติคือ app/build.gradle) ให้เพิ่มทรัพยากร Dependency ของบริการ Google Play สำหรับ play-services-base-testing ในการทดสอบ

      dependencies {
        // other dependencies...
    
        testImplementation 'com.google.android.gms:play-services-base-testing:16.0.0'
      }
    
  2. สร้างโมดูล Hilt เพื่อระบุ ModuleInstallClient:

    Kotlin

    @Module
    @InstallIn(ActivityComponent::class)
    object ModuleInstallModule {
    
      @Provides
      fun provideModuleInstallClient(
        @ActivityContext context: Context
      ): ModuleInstallClient = ModuleInstall.getClient(context)
    }
    

    Java

    @Module
    @InstallIn(ActivityComponent.class)
    public class ModuleInstallModule {
      @Provides
      public static ModuleInstallClient provideModuleInstallClient(
        @ActivityContext Context context) {
        return ModuleInstall.getClient(context);
      }
    }
    
  3. แทรก ModuleInstallClient ในกิจกรรม:

    Kotlin

    @AndroidEntryPoint
    class MyActivity: AppCompatActivity() {
      @Inject lateinit var moduleInstallClient: ModuleInstallClient
    
      ...
    }
    

    Java

    @AndroidEntryPoint
    public class MyActivity extends AppCompatActivity {
      @Inject ModuleInstallClient moduleInstallClient;
    
      ...
    }
    
  4. แทนที่การเชื่อมโยงในการทดสอบ:

    Kotlin

    @UninstallModules(ModuleInstallModule::class)
    @HiltAndroidTest
    class MyActivityTest {
      ...
      private val context:Context = ApplicationProvider.getApplicationContext()
      private val fakeModuleInstallClient = FakeModuleInstallClient(context)
      @BindValue @JvmField
      val moduleInstallClient: ModuleInstallClient = fakeModuleInstallClient
    
      ...
    }
    

    Java

    @UninstallModules(ModuleInstallModule.class)
    @HiltAndroidTest
    class MyActivityTest {
      ...
      private static final Context context = ApplicationProvider.getApplicationContext();
      private final FakeModuleInstallClient fakeModuleInstallClient = new FakeModuleInstallClient(context);
      @BindValue ModuleInstallClient moduleInstallClient = fakeModuleInstallClient;
    
      ...
    }
    

จำลองความพร้อมใช้งานของโมดูล

Kotlin

@Test
fun checkAvailability_available() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset()

  val availableModule = TfLite.getClient(context)
  fakeModuleInstallClient.setInstalledModules(api)

  // Verify the case where modules are already available...
}

@Test
fun checkAvailability_unavailable() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset()

  // Do not set any installed modules in the test.

  // Verify the case where modules unavailable on device...
}

@Test
fun checkAvailability_failed() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset()

  fakeModuleInstallClient.setModulesAvailabilityTask(Tasks.forException(RuntimeException()))

  // Verify the case where an RuntimeException happened when trying to get module's availability...
}

Java

@Test
public void checkAvailability_available() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
  fakeModuleInstallClient.setInstalledModules(api);

  // Verify the case where modules are already available...
}

@Test
public void checkAvailability_unavailable() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Do not set any installed modules in the test.

  // Verify the case where modules unavailable on device...
}

@Test
public void checkAvailability_failed() {
  fakeModuleInstallClient.setModulesAvailabilityTask(Tasks.forException(new RuntimeException()));

  // Verify the case where an RuntimeException happened when trying to get module's availability...
}

จำลองผลลัพธ์สำหรับคำขอติดตั้งที่เลื่อนเวลาออกไป

Kotlin

@Test
fun deferredInstall_success() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forResult(null))

  // Verify the case where the deferred install request has been sent successfully...
}

@Test
fun deferredInstall_failed() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forException(RuntimeException()))

  // Verify the case where an RuntimeException happened when trying to send the deferred install request...
}

Java

@Test
public void deferredInstall_success() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forResult(null));

  // Verify the case where the deferred install request has been sent successfully...
}

@Test
public void deferredInstall_failed() {
  fakeModuleInstallClient.setDeferredInstallTask(Tasks.forException(new RuntimeException()));

  // Verify the case where an RuntimeException happened when trying to send the deferred install request...
}

จำลองผลลัพธ์สำหรับคำขอติดตั้งแบบเร่งด่วน

Kotlin

@Test
fun installModules_alreadyExist() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
  fakeModuleInstallClient.setInstalledModules(api);

  // Verify the case where the modules already exist when sending the install request...
}

@Test
fun installModules_withoutListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Verify the case where the urgent install request has been sent successfully...
}

@Test
fun installModules_withListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Generates a ModuleInstallResponse and set it as the result for installModules().
  val moduleInstallResponse = FakeModuleInstallUtil.generateModuleInstallResponse()
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forResult(moduleInstallResponse))

  // Verify the case where the urgent install request has been sent successfully...

  // Generates some fake ModuleInstallStatusUpdate and send it to listener.
  val update = FakeModuleInstallUtil.createModuleInstallStatusUpdate(
    moduleInstallResponse.sessionId, STATE_COMPLETED)
  fakeModuleInstallClient.sendInstallUpdates(listOf(update))

  // Verify the corresponding updates are handled correctly...
}

@Test
fun installModules_failed() {
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forException(RuntimeException()))

  // Verify the case where an RuntimeException happened when trying to send the urgent install request...
}

Java

@Test
public void installModules_alreadyExist() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  OptionalModuleApi optionalModuleApi = TfLite.getClient(context);
  fakeModuleInstallClient.setInstalledModules(api);

  // Verify the case where the modules already exist when sending the install request...
}

@Test
public void installModules_withoutListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Verify the case where the urgent install request has been sent successfully...
}

@Test
public void installModules_withListener() {
  // Reset any previously installed modules.
  fakeModuleInstallClient.reset();

  // Generates a ModuleInstallResponse and set it as the result for installModules().
  ModuleInstallResponse moduleInstallResponse =
      FakeModuleInstallUtil.generateModuleInstallResponse();
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forResult(moduleInstallResponse));

  // Verify the case where the urgent install request has been sent successfully...

  // Generates some fake ModuleInstallStatusUpdate and send it to listener.
  ModuleInstallStatusUpdate update = FakeModuleInstallUtil.createModuleInstallStatusUpdate(
      moduleInstallResponse.getSessionId(), STATE_COMPLETED);
  fakeModuleInstallClient.sendInstallUpdates(ImmutableList.of(update));

  // Verify the corresponding updates are handled correctly...
}

@Test
public void installModules_failed() {
  fakeModuleInstallClient.setInstallModulesTask(Tasks.forException(new RuntimeException()));

  // Verify the case where an RuntimeException happened when trying to send the urgent install request...
}