Media3는 라이브러리 전반에 걸쳐 새로운 최적화, 동작 개선, 아키텍처 개선사항을 도입합니다. 이러한 변경사항은 출시 전에 철저히 테스트되고 검증되지만, Media3에는 카나리아 모드를 중심으로 한 중앙 집중식 기능 플래그 인프라 (androidx.media3.common.Flags)가 포함되어 있어 애플리케이션이 새로운 동작을 채택하는 방식과 시기를 제어할 수 있습니다.
이 페이지에서는 카나리아 모드와 기능 플래그의 작동 방식과 이를 애플리케이션에서 사용하여 출시 전반에서 업그레이드 위험을 줄이고, 제어된 출시를 실행하고, 긴급 롤백을 구성하는 방법을 설명합니다.
개요
카나리아 모드는 새로 도입되고 검증된 라이브러리 개선사항을 통합된 전역 전환 버튼 아래에 그룹화합니다.
- 기본적으로 사용 설정됨: 표준 Media3 출시에서는 카나리아 모드가 기본적으로 사용 설정됩니다 (
true). 이렇게 하면 앱이 안전한 성능 개선, 최적화, 새로운 기능을 자동으로 활용할 수 있습니다. - 선택 해제 및 롤백 기능: 애플리케이션은 런타임에 카나리아 모드를 전역적으로 사용 중지하거나 특정 기능 플래그를 개별적으로 재정의할 수 있습니다.
- 복잡한 연결 없음: 플래그는
common모듈의 중앙Flags클래스를 통해 관리되므로 여러 구성요소에서 맞춤 설정자나 구성 객체가 필요하지 않습니다.
앱에서 플래그를 사용하는 이유
카나리아 모드는 기본적으로 활성화되어 있지만 Flags를 앱의 구성 또는 실험 프레임워크와 통합하면 다음과 같은 이점이 있습니다.
1. 버전 업그레이드 위험 완화
프로덕션에서 주 또는 부 라이브러리 버전을 업그레이드하면 특정 OEM 기기 또는 맞춤 미디어 파이프라인에서 예기치 않은 상호작용이 발생할 수 있습니다. Media3를 업데이트할 때 카나리아 모드와 원격 구성을 사용하면 출시 버전 전반에서 더 안전한 업그레이드 흐름을 구현할 수 있습니다.
- 카나리아 모드를 사용 중지한 상태로 업데이트 출시: 카나리아 모드를 사용 중지한 상태로 새 Media3 버전을 출시하면 이전 출시 (카나리아 사용 설정됨)의 기능 세트가 대략적으로 유지되므로 새로운 동작을 채택하기 전에 프로덕션에서 기준 라이브러리 안정성과 일반적인 버그 수정을 확인할 수 있습니다.
- 점진적으로 카나리아 모드 사용 설정: 원격 구성을 사용하여 사용자 집단 전반에서 단계적으로 카나리아 모드를 사용 설정하거나 (예: 5% → 25% → 100%) A/B 실험을 실행하여 측정항목 영향을 측정합니다.
- 필요한 경우 롤백: 특정 기기에서 회귀가 발생하면 긴급 앱 출시 없이 원격으로 카나리아 모드 또는 특정 플래그 ID를 사용 중지합니다.
- 기본값으로 채택: 확인되면 다음 버전 업그레이드까지 카나리아 모드를 기본적으로 사용 설정된 상태로 유지합니다. 이 업그레이드에서 이전에 확인된 카나리아 기능이 영구적으로 적용됩니다.
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 테스트 제어
한 번에 모든 사용자에게 새로운 라이브러리 동작을 노출하는 대신 Flags를 원격 구성 또는 실험 시스템에 연결할 수 있습니다.
카나리아 모드를 단계별로 사용 설정하거나 (예: 사용자 5% → 25% → 100%) A/B 실험을 실행하여 새로운 최적화가 시작 지연 시간, 재버퍼링 비율, 재생 실패와 같은 핵심 재생 측정항목에 미치는 영향을 측정할 수 있습니다.
3. 원격 킬 스위치 및 빠른 롤백
특정 기기 모델 또는 OS 버전의 프로덕션에서 회귀가 발생하면 서버 측 구성을 사용하여 카나리아 모드 (또는 담당하는 특정 플래그 ID)를 원격으로 사용 중지할 수 있습니다. 이렇게 하면 긴급 앱 핫픽스나 바이너리 출시 없이도 영향을 받는 사용자의 라이브러리가 안정적인 기존 대체 동작으로 즉시 되돌아갑니다.
카나리아 모드 및 플래그 구성
Flags API는 전역 카나리아 모드와 개별 플래그 재정의를 제어하는 메서드를 제공합니다.
전역 카나리아 모드
카나리아 모드에서 지원하는 모든 개선 기능을 사용 설정 또는 중지하려면 다음 단계를 따르세요.
Kotlin
// Enable or disable Canary mode globally (defaults to true). Flags.setCanaryModeEnabled(false)
자바
// Enable or disable Canary mode globally (defaults to true). Flags.setCanaryModeEnabled(false);
개별 플래그 재정의
전역 카나리아 모드 설정과 별개로 특정 플래그를 명시적으로 사용 설정하거나 사용 중지할 수도 있습니다.
Kotlin
// Explicitly enable an experimental flag. Flags.enableFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION) // Explicitly disable a flag. Flags.disableFlag(Flags.FLAG_DYNAMIC_SCHEDULING)
자바
// Explicitly enable an experimental flag. Flags.enableFlag(Flags.FLAG_PER_STREAM_MEDIA_PROGRESSION); // Explicitly disable a flag. Flags.disableFlag(Flags.FLAG_DYNAMIC_SCHEDULING);
명시적 재정의는 항상 전역 카나리아 모드 기본값보다 우선 적용됩니다.
플래그 상태 확인
코드에서 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)
자바
// 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는 런타임에 기능이 사용 설정되어 있는지 확인할 때 다음 순서로 상태를 확인합니다.
- 명시적 런타임 재정의: 플래그 ID에 대해
enableFlag또는disableFlag가 호출된 경우 해당 재정의 값이 반환됩니다. - 정적 라이브러리 상태: 플래그가 정적으로 사용 중지되거나 (예: 아직 Canary 모드에 준비되지 않은 초기 개발 기능) 정적으로 사용 설정된 경우 (Canary 모드에서 임시 선택 해제 대체 기능을 유지하는 기능) 정적 값이 반환됩니다.
- 카나리아 모드 상태: 그렇지 않으면 플래그가 전역 카나리아 모드 설정을 따릅니다 (카나리아 모드가
true이면 사용 설정되고false이면 사용 중지됨).
초기화 요구사항
Media3 구성요소 (예: ExoPlayer, Transformer, MediaSession)를 인스턴스화하기 전에 플래그를 구성합니다.
- 조기 설정: 애플리케이션 시작 중에(예:
Application.onCreate내에서) 또는 앱의 미디어 레이어를 초기화하기 전에Flags를 구성합니다. - 스레드 안전성: Media3 구성요소는 인스턴스화 중에 플래그를 읽고 이러한 값을 내부 재생 및 처리 스레드에 전파합니다. Media3 구성요소를 만들기 전에 플래그 구성이 이루어지도록 합니다. 플레이어나 변환기가 활발하게 실행되는 동안 플래그를 수정하는 것은 지원되지 않습니다.
원격 구성과 통합
권장되는 아키텍처는 원격 구성 플랫폼에서 다음 세 가지 매개변수를 노출하는 것입니다.
media3_canary_mode_enabled(불리언): 카나리아 모드의 전역 전환(기본값은true)media3_force_enable_flags(정수 목록): 타겟팅된 무료 체험판에 강제 사용 설정할 특정 플래그 ID입니다.media3_force_disable_flags(정수 목록): 문제가 발견되면 강제로 사용 중지할 특정 플래그 ID입니다.
플레이어 인스턴스를 만들기 전에 시작 중에 다음 값을 적용합니다.
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) } }
자바
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); } }
플래그를 사용한 테스트
기능 플래그의 영향을 받는 코드 경로의 단위 테스트 또는 Robolectric 테스트를 작성할 때는 androidx.media3.test.utils.Media3FlagsRule(media3-test-utils 모듈에서 사용 가능)를 사용하여 테스트가 서로의 정적 상태를 오염시키지 않도록 합니다.
Media3FlagsRule을 사용한 자동 정리
Media3FlagsRule를 추가하면 각 테스트 후에 모든 플래그 재정의 및 카나리아 모드 수정이 자동으로 재설정됩니다.
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... } }
자바
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. } }
자바
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()
자바
// 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의 플래그는 임시로 설계되었습니다.
- 개발: 새로운 최적화 또는 기능이
Flags.java의 플래그 ID 뒤에 추가됩니다. - 카나리아 모드: 확인되고 일반 사용 준비가 완료되면 이 기능은 여러 버전에 걸쳐 카나리아 모드에서 활성화됩니다.
- 승격 및 정리: 프로덕션에서 안정성을 입증하는 소크 기간이 지나면 기능이 라이브러리에서 영구적으로 사용 설정되도록 승격되고 플래그 상수, 대체 코드 경로, 조건부 검사가 삭제됩니다.
기능 플래그에 문제가 발생하면 AndroidX Media3 GitHub Issue Tracker에 신고를 제출하여 영구적으로 승격되기 전에 팀에서 문제를 해결할 수 있도록 하세요.