แฟล็กฟีเจอร์และโหมด Canary

Media3 เปิดตัวการเพิ่มประสิทธิภาพใหม่ การปรับปรุงลักษณะการทำงาน และการปรับปรุงสถาปัตยกรรมในไลบรารีต่างๆ แม้ว่าการเปลี่ยนแปลงเหล่านี้จะได้รับการทดสอบและยืนยันอย่างละเอียดก่อนเผยแพร่ แต่ Media3 ก็มีโครงสร้างพื้นฐานแฟล็กฟีเจอร์แบบรวมศูนย์ (androidx.media3.common.Flags) ที่เน้นโหมด Canary เพื่อให้แอปพลิเคชันควบคุมวิธีและเวลาที่จะนำลักษณะการทำงานใหม่ๆ มาใช้

หน้านี้อธิบายวิธีการทำงานของโหมด Canary และแฟล็กฟีเจอร์ รวมถึงวิธีใช้ในแอปพลิเคชันเพื่อลดความเสี่ยงในการอัปเกรดในรุ่นต่างๆ เรียกใช้การเปิดตัวที่มีการควบคุม และกำหนดค่าการย้อนกลับฉุกเฉิน

ภาพรวม

โหมด Canary จะจัดกลุ่มการปรับปรุงไลบรารีที่ได้รับการยืนยันและเปิดตัวใหม่ไว้ภายใต้ปุ่มเปิด/ปิดส่วนกลางแบบรวม ดังนี้

  • เปิดใช้โดยค่าเริ่มต้น: ใน Media3 รุ่นมาตรฐาน โหมด Canary จะเปิดใช้โดยค่าเริ่มต้น (true) ซึ่งช่วยให้แอปของคุณได้รับประโยชน์จากการปรับปรุงประสิทธิภาพ การเพิ่มประสิทธิภาพ และความสามารถใหม่ๆ ที่ปลอดภัยโดยอัตโนมัติ
  • ความสามารถในการเลือกไม่ใช้และการย้อนกลับ: แอปพลิเคชันสามารถปิดใช้โหมด Canary ทั่วโลกหรือลบล้างแฟล็กฟีเจอร์ที่เฉพาะเจาะจงทีละรายการขณะรันไทม์ได้
  • การเดินสายที่ซับซ้อนเป็นศูนย์: ระบบจะจัดการแฟล็กผ่านคลาส Flags ส่วนกลางในโมดูล common ซึ่งช่วยลดความจำเป็นในการใช้ Setter หรือออบเจ็กต์การกำหนดค่าที่กำหนดเองในคอมโพเนนต์ต่างๆ

เหตุใดจึงควรใช้แฟล็กในแอป

แม้ว่าโหมด Canary จะทำงานอยู่โดยค่าเริ่มต้น แต่การผสานรวม Flags กับการกำหนดค่าหรือเฟรมเวิร์กการทดสอบของแอปก็มีประโยชน์หลายประการ ดังนี้

1. การลดความเสี่ยงในการอัปเกรดเวอร์ชัน

การอัปเกรดไลบรารีเวอร์ชันหลักหรือเวอร์ชันย่อยในเวอร์ชันที่ใช้งานจริงอาจทำให้เกิดการโต้ตอบที่ไม่คาดคิดในอุปกรณ์ OEM บางรุ่นหรือไปป์ไลน์สื่อที่กำหนดเอง เมื่ออัปเดต Media3 คุณสามารถใช้โหมด Canary และการกำหนดค่าระยะไกลเพื่อให้ขั้นตอนการอัปเกรดปลอดภัยยิ่งขึ้นในรุ่นต่างๆ ดังนี้

  1. จัดส่งการอัปเดตโดยปิดใช้โหมด Canary: การเผยแพร่ Media3 เวอร์ชันใหม่โดยปิดใช้โหมด Canary จะคงชุดฟีเจอร์ของเวอร์ชันก่อนหน้าไว้ (โดยเปิดใช้ Canary) ซึ่งช่วยให้คุณยืนยันความเสถียรของไลบรารีพื้นฐานและการแก้ไขข้อบกพร่องทั่วไปในเวอร์ชันที่ใช้งานจริงได้ก่อนที่จะนำลักษณะการทำงานใหม่มาใช้
  2. เปิดใช้โหมด Canary แบบค่อยเป็นค่อยไป: ใช้การกำหนดค่าระยะไกลเพื่อเปิดใช้โหมด Canary เป็นระยะๆ ในกลุ่มประชากรตามรุ่นของผู้ใช้ (เช่น 5% → 25% → 100%) หรือทำการทดสอบ A/B เพื่อวัดผลกระทบของเมตริก
  3. ย้อนกลับหากจำเป็น: หากเกิดการถดถอยในอุปกรณ์บางเครื่อง ให้ปิดใช้โหมด Canary หรือรหัส Flag ที่เฉพาะเจาะจงจากระยะไกลโดยไม่ต้องเผยแพร่แอปฉุกเฉิน
  4. ใช้เป็นค่าเริ่มต้น: เมื่อยืนยันแล้ว ให้เปิดใช้โหมด Canary ไว้โดยค่าเริ่มต้นจนกว่าจะอัปเกรดเวอร์ชันถัดไป ซึ่งฟีเจอร์ Canary ที่ยืนยันก่อนหน้านี้จะกลายเป็นฟีเจอร์ถาวร

Kotlin

// Enable or disable Canary mode globally (defaults to true).
Flags.setCanaryModeEnabled(false)

Java

// Enable or disable Canary mode globally (defaults to true).
Flags.setCanaryModeEnabled(false);

2. การเปิดตัวแบบทีละขั้นที่มีการควบคุมและการทดสอบ A/B

แทนที่จะแสดงลักษณะการทำงานใหม่ของไลบรารีแก่ผู้ใช้ 100% พร้อมกัน คุณสามารถเชื่อมต่อ Flags กับการกำหนดค่าระยะไกลหรือระบบการทดสอบได้

คุณสามารถเปิดใช้โหมด Canary เป็นระยะ (เช่น ผู้ใช้ 5% → 25% → 100%) หรือทำการทดสอบ A/B เพื่อวัดผลกระทบของการเพิ่มประสิทธิภาพใหม่ๆ ต่อเมตริกการเล่นหลัก เช่น เวลาในการตอบสนองเมื่อเริ่มต้น อัตราการบัฟเฟอร์ซ้ำ และการเล่นไม่สำเร็จ

3. คิลสวิตช์ระยะไกลและการย้อนกลับอย่างรวดเร็ว

หากเกิดการถดถอยในเวอร์ชันที่ใช้งานจริงในอุปกรณ์รุ่นหรือระบบปฏิบัติการเวอร์ชันใดเวอร์ชันหนึ่ง คุณสามารถปิดใช้โหมด Canary (หรือรหัสแฟล็กที่รับผิดชอบ) จากระยะไกลได้โดยใช้การกำหนดค่าฝั่งเซิร์ฟเวอร์ ซึ่งจะเปลี่ยนกลับไลบรารีเป็นลักษณะการทำงานสำรองเดิมที่เสถียรสำหรับผู้ใช้ที่ได้รับผลกระทบทันที โดยไม่จำเป็นต้องใช้การแก้ไขด่วนสำหรับแอปฉุกเฉินหรือการเผยแพร่ไบนารี

การกำหนดค่าโหมด Canary และแฟล็ก

Flags API มีเมธอดในการควบคุมโหมด Canary ทั่วโลกและการลบล้างแฟล็กแต่ละรายการ

โหมด Canary ทั่วโลก

วิธีเปิดหรือปิดใช้การเพิ่มประสิทธิภาพทั้งหมดที่ครอบคลุมโดยโหมด Canary

Kotlin

// Enable or disable Canary mode globally (defaults to true).
Flags.setCanaryModeEnabled(false)

Java

// Enable or disable Canary mode globally (defaults to true).
Flags.setCanaryModeEnabled(false);

การลบล้างแฟล็กแต่ละรายการ

นอกจากนี้ คุณยังเปิดหรือปิดใช้แฟล็กที่เฉพาะเจาะจงอย่างชัดแจ้งได้โดยไม่ขึ้นอยู่กับการตั้งค่าโหมด Canary ทั่วโลก ดังนี้

Kotlin

// Explicitly enable an experimental flag.
Flags.enableFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION)

// Explicitly disable a flag.
Flags.disableFlag(Flags.FLAG_DYNAMIC_SCHEDULING)

Java

// Explicitly enable an experimental flag.
Flags.enableFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION);

// Explicitly disable a flag.
Flags.disableFlag(Flags.FLAG_DYNAMIC_SCHEDULING);

การลบล้างที่ชัดแจ้งจะมีผลเหนือกฎเริ่มต้นของโหมด Canary ทั่วโลกเสมอ

กำลังตรวจสอบสถานะการแจ้งว่าไม่เหมาะสม

หากต้องการตรวจสอบว่าโหมด Canary หรือแฟล็กที่เฉพาะเจาะจงทำงานอยู่ในโค้ดหรือไม่ ให้ทำดังนี้

Kotlin

// Query whether Canary mode is active globally.
val isCanaryActive = Flags.isCanaryModeEnabled()

// Query whether a specific flag is active.
val isFeatureActive = Flags.isEnabled(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION)

Java

// Query whether Canary mode is active globally.
boolean isCanaryActive = Flags.isCanaryModeEnabled();

// Query whether a specific flag is active.
boolean isFeatureActive = Flags.isEnabled(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION);

ลำดับการแก้ไขปัญหาการแจ้งว่าไม่เหมาะสม

เมื่อ Media3 ตรวจสอบว่ามีการเปิดใช้ฟีเจอร์ขณะรันไทม์หรือไม่ ระบบจะแก้ไขสถานะตามลำดับต่อไปนี้

  1. การลบล้างรันไทม์ที่ชัดเจน: หากมีการเรียกใช้ enableFlag หรือ disableFlag สำหรับรหัสแฟล็ก ระบบจะแสดงผลค่าการลบล้างนั้น
  2. สถานะไลบรารีแบบคงที่: หากแฟล็กถูกปิดใช้แบบคงที่ (เช่น ฟีเจอร์ที่อยู่ระหว่างการพัฒนาในช่วงแรกซึ่งยังไม่พร้อมสำหรับโหมด Canary) หรือเปิดใช้แบบคงที่ (ฟีเจอร์ที่จบการศึกษาจากโหมด Canary โดยยังคงมีการสำรองข้อมูลการเลือกไม่ใช้ชั่วคราว) ระบบจะแสดงผลค่าแบบคงที่นั้น
  3. สถานะโหมด Canary: มิฉะนั้น แฟล็กจะเป็นไปตามการตั้งค่าโหมด Canary ทั่วโลก (เปิดใช้หากโหมด Canary เป็น true และปิดใช้หากเป็น false)

ข้อกำหนดในการเริ่มต้นใช้งาน

กำหนดค่าแฟล็กก่อนสร้างอินสแตนซ์คอมโพเนนต์ Media3 (เช่น ExoPlayer, Transformer หรือ MediaSession) ดังนี้

  • การตั้งค่าตั้งแต่เนิ่นๆ: กำหนดค่า Flags ระหว่างการเริ่มต้นแอปพลิเคชัน (เช่น ภายใน Application.onCreate) หรือก่อนเริ่มต้นเลเยอร์สื่อของแอป
  • ความปลอดภัยของเทรด: คอมโพเนนต์ Media3 จะอ่านแฟล็กระหว่างการสร้างอินสแตนซ์และเผยแพร่ค่าเหล่านั้นไปยังเทรดการเล่นและการประมวลผลภายใน ตรวจสอบว่าการกำหนดค่าแฟล็กเกิดขึ้นก่อนสร้างคอมโพเนนต์ Media3 ระบบไม่รองรับการแก้ไขแฟล็กขณะที่เพลเยอร์หรือ Transformer กำลังทำงานอยู่

การผสานรวมกับการกำหนดค่าระยะไกล

สถาปัตยกรรมที่แนะนำคือการแสดงพารามิเตอร์ 3 รายการในแพลตฟอร์มการกำหนดค่าระยะไกล ดังนี้

  • media3_canary_mode_enabled (บูลีน): การเปิด/ปิดส่วนกลางสำหรับโหมด Canary (ค่าเริ่มต้นคือ true)
  • media3_force_enable_flags (รายการจำนวนเต็ม): รหัสแฟล็กที่เฉพาะเจาะจงเพื่อบังคับให้เปิดใช้สำหรับการทดลองที่กำหนดเป้าหมาย
  • media3_force_disable_flags (รายการจำนวนเต็ม): รหัสแฟล็กที่เฉพาะเจาะจงเพื่อบังคับปิดใช้หากพบปัญหา

ใช้ค่าเหล่านี้ระหว่างการเริ่มต้นก่อนสร้างอินสแตนซ์โปรแกรมเล่น

Kotlin

fun initializeMedia3Flags(
  canaryModeEnabled: Boolean,
  forceEnabledFlags: List<Int>,
  forceDisabledFlags: List<Int>,
) {
  // 1. Set global Canary mode state.
  Flags.setCanaryModeEnabled(canaryModeEnabled)

  // 2. Apply explicit force-enable overrides.
  for (flagId in forceEnabledFlags) {
    Flags.enableFlag(flagId)
  }

  // 3. Apply explicit force-disable overrides (remote kill switch).
  for (flagId in forceDisabledFlags) {
    Flags.disableFlag(flagId)
  }
}

Java

public static void initializeMedia3Flags(
    boolean canaryModeEnabled,
    List<Integer> forceEnabledFlags,
    List<Integer> forceDisabledFlags) {
  // 1. Set global Canary mode state.
  Flags.setCanaryModeEnabled(canaryModeEnabled);

  // 2. Apply explicit force-enable overrides.
  for (int flagId : forceEnabledFlags) {
    Flags.enableFlag(flagId);
  }

  // 3. Apply explicit force-disable overrides (remote kill switch).
  for (int flagId : forceDisabledFlags) {
    Flags.disableFlag(flagId);
  }
}

การทดสอบด้วยแฟล็ก

เมื่อเขียน Unit Test หรือการทดสอบ Robolectric สำหรับเส้นทางโค้ดที่ได้รับผลกระทบจากแฟล็กฟีเจอร์ ให้ใช้ androidx.media3.test.utils.Media3FlagsRule (มีอยู่ในโมดูล media3-test-utils) เพื่อให้แน่ใจว่าการทดสอบจะไม่รบกวนสถานะคงที่ของกันและกัน

การล้างข้อมูลอัตโนมัติด้วย Media3FlagsRule

การเพิ่ม Media3FlagsRule จะช่วยให้มั่นใจได้ว่าการลบล้างแฟล็กและการแก้ไขโหมด Canary ทั้งหมดจะรีเซ็ตโดยอัตโนมัติหลังจากการทดสอบแต่ละครั้ง

Kotlin

class MyPlayerTest {

  @get:Rule val flagsRule = Media3FlagsRule(this)

  @Test
  fun playback_withFeatureFlagEnabled() {
    Flags.enableFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION)
    // Run test with the flag enabled...
  }
}

Java

public static class MyPlayerTest {

  @Rule public final Media3FlagsRule flagsRule = new Media3FlagsRule(this);

  @Test
  public void playback_withFeatureFlagEnabled() {
    Flags.enableFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION);
    // Run test with the flag enabled...
  }
}

การทดสอบพารามิเตอร์

คุณสามารถรวมคำอธิบายประกอบ Media3FlagsRule กับ @BindFlag และ @BindCanaryMode เพื่อทำการทดสอบที่กำหนดพารามิเตอร์ในสถานะแฟล็กต่างๆ ได้ดังนี้

Kotlin

class MyParameterizedPlayerTest {

  @BindCanaryMode var canaryMode: Boolean = false

  @BindFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION) var perStreamProgression: Boolean = false

  @get:Rule val flagsRule = Media3FlagsRule(this)

  @Test
  fun playback() {
    // Flags are automatically set for each test parameter combination.
  }
}

Java

public static class MyParameterizedPlayerTest {

  @BindCanaryMode public boolean canaryMode;

  @BindFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION)
  public boolean perStreamProgression;

  @Rule public final Media3FlagsRule flagsRule = new Media3FlagsRule(this);

  @Test
  public void playback() {
    // Flags are automatically set for each test parameter combination.
  }
}

การวินิจฉัยและการบันทึก

Media3 มีเครื่องมือวินิจฉัยเพื่อยืนยันแฟล็กที่ใช้งานอยู่และสถานะโหมด Canary ระหว่างการเริ่มต้นการเล่นและที่รันไทม์

บันทึกการเริ่มต้น

เมื่ออินสแตนซ์ ExoPlayer เริ่มต้น ระบบจะพิมพ์สถานะ Canary ที่ใช้งานอยู่และการลบล้างที่ชัดเจนไปยัง logcat ควบคู่ไปกับเวอร์ชันไลบรารี ดังนี้

ExoPlayerImpl: Init 59a18ab [AndroidXMedia3/1.10.1] [flame, Pixel 4, Google, 33] [Canary:ON]

หากการลบล้างทำงานอยู่ ระบบจะแสดงเฉพาะค่าที่เบี่ยงเบนไปจากสถานะเริ่มต้น ดังนี้

ExoPlayerImpl: Init 59a18ab [AndroidXMedia3/1.10.1] [flame, Pixel 4, Google, 33] [Canary:ON, Overrides: enabled=[1], disabled=[3]]

การตรวจสอบแบบเป็นโปรแกรม

คุณสามารถดึงข้อมูลสตริงการวินิจฉัยที่จัดรูปแบบแล้วหรือรายการการลบล้างการค้นหาแบบเป็นโปรแกรมได้ (เช่น เพื่อรวมสถานะแฟล็กในรายงานข้อขัดข้องหรือข้อมูลวิเคราะห์ของแอป)

Kotlin

// Get formatted string representing active Canary state and overrides.
val flagLogString = Flags.getLogString()

// Query explicit override IDs if needed.
val enabledFlags = Flags.getEnabledOverrides()
val disabledFlags = Flags.getDisabledOverrides()

Java

// Get formatted string representing active Canary state and overrides.
String flagLogString = Flags.getLogString();

// Query explicit override IDs if needed.
int[] enabledFlags = Flags.getEnabledOverrides();
int[] disabledFlags = Flags.getDisabledOverrides();

วงจรของการแจ้งว่าไม่เหมาะสม

แฟล็กใน Media3 ออกแบบมาให้เป็นแบบชั่วคราว ดังนี้

  1. การพัฒนา: มีการเพิ่มการเพิ่มประสิทธิภาพหรือฟีเจอร์ใหม่หลังรหัสแฟล็กใน Flags.java
  2. โหมด Canary: เมื่อได้รับการยืนยันและพร้อมใช้งานทั่วไปแล้ว ฟีเจอร์จะทำงานภายใต้โหมด Canary ในทุกรุ่น
  3. การโปรโมตและการล้างข้อมูล: หลังจากระยะเวลาทดสอบที่พิสูจน์ความเสถียรในเวอร์ชันที่ใช้งานจริงแล้ว ฟีเจอร์จะได้รับการโปรโมตให้เปิดใช้ถาวรในไลบรารี และระบบจะนำค่าคงที่ของแฟล็ก เส้นทางโค้ดสำรอง และการตรวจสอบแบบมีเงื่อนไขออก

หากพบปัญหาเกี่ยวกับแฟล็กฟีเจอร์ โปรดส่งรายงานในเครื่องมือติดตามปัญหา AndroidX Media3 GitHub เพื่อให้ทีมแก้ไขปัญหาก่อนที่จะโปรโมตอย่างถาวร