AndroidX Media3 이전 가이드

현재 독립형 com.google.android.exoplayer2 라이브러리와 androidx.media를 사용하는 앱은 androidx.media3로 이전해야 합니다. 이전 스크립트를 사용하여 Gradle 빌드 파일, 자바 및 Kotlin 소스 파일, XML 레이아웃 파일을 ExoPlayer 2.19.1에서 AndroidX Media3 1.1.1로 이전합니다.

개요

이전하기 전에 다음 섹션을 검토하여 새 API의 이점, 이전할 API, 앱 프로젝트가 충족해야 하는 기본 요건을 자세히 알아보세요.

Jetpack Media3로 이전해야 하는 이유

  • ExoPlayer의 새로운 홈이며 com.google.android.exoplayer2는 중단됩니다.
  • MediaBrowser/MediaController를 사용하여 구성요소/프로세스 전반에서 Player API에 액세스합니다.
  • MediaSession 및 MediaController API의 확장 기능을 사용합니다.
  • 세분화된 액세스 제어로 재생 기능을 알립니다.
  • MediaSessionConnector 및 PlayerNotificationManager를 삭제하여 앱을 간소화합니다.
  • media-compat 클라이언트 API(MediaBrowserCompat/MediaControllerCompat/MediaMetadataCompat)와 하위 호환

AndroidX Media3로 이전할 미디어 API

  • ExoPlayer 및 확장 프로그램
    여기에는 중단된 mediasession 모듈을 제외한 기존 ExoPlayer 프로젝트의 모든 모듈이 포함됩니다. com.google.android.exoplayer2의 패키지에 종속되는 앱 또는 모듈은 이전 스크립트를 사용하여 이전할 수 있습니다.
  • MediaSessionConnector(androidx.media:media:1.4.3+의 androidx.media.* 패키지에 따라 다름)
    MediaSessionConnector를 삭제하고 대신 androidx.media3.session.MediaSession를 사용합니다.
  • MediaBrowserServiceCompat(androidx.media:media:1.4.3+의 androidx.media.* 패키지에 따라 다름)
    androidx.media.MediaBrowserServiceCompat의 서브클래스를 androidx.media3.session.MediaLibraryService로, MediaBrowserCompat.MediaItem를 사용하는 코드를 androidx.media3.common.MediaItem로 이전합니다.
  • MediaBrowserCompat(androidx.media:media:1.4.3+의 android.support.v4.media.* 패키지에 따라 다름)
    MediaBrowserCompat 또는 MediaControllerCompat를 사용하여 클라이언트 코드를 이전하여 androidx.media3.common.MediaItem와 함께 androidx.media3.session.MediaBrowser를 사용합니다.

기본 요건

  1. 프로젝트가 소스 제어 하에 있는지 확인

    스크립트 기반 마이그레이션 도구로 적용된 변경사항을 되돌릴 수 있는지 확인합니다. 아직 프로젝트를 소스 제어에 포함하지 않았다면 지금 시작하는 것이 좋습니다. 어떤 이유로든 이 작업을 수행하지 않으려면 마이그레이션을 시작하기 전에 프로젝트의 백업 사본을 만드세요.

  2. 앱 업데이트

    • 최신 버전의 ExoPlayer 라이브러리를 사용하도록 프로젝트를 업데이트하고 지원 중단된 메서드 호출을 삭제하는 것이 좋습니다. 마이그레이션에 스크립트를 사용하려면 업데이트할 버전을 스크립트에서 처리하는 버전과 일치시켜야 합니다.

    • 앱의 compileSdkVersion을 32 이상으로 늘립니다.

    • 앞서 언급한 업데이트된 종속 항목과 호환되는 최신 버전으로 Gradle 및 Android 스튜디오 Gradle 플러그인을 업그레이드합니다. 예를 들면 다음과 같습니다.

      • Android Gradle 플러그인 버전: 7.1.0
      • Gradle 버전: 7.4
    • 별표(*)를 사용하는 모든 와일드 카드 가져오기 문을 정규화된 가져오기 문으로 바꿉니다. 와일드 카드 가져오기 문을 삭제하고 Android 스튜디오를 사용하여 정규화된 문을 가져옵니다 (F2 - Alt/Enter, F2 - Alt/Enter, …).

    • com.google.android.exoplayer2.PlayerView에서 com.google.android.exoplayer2.StyledPlayerView로 마이그레이션 AndroidX Media3에는 com.google.android.exoplayer2.PlayerView에 상응하는 항목이 없으므로 이 작업이 필요합니다.

스크립트 지원을 사용하여 ExoPlayer 이전

이 스크립트는 com.google.android.exoplayer2에서 androidx.media3 아래의 새 패키지 및 모듈 구조로의 이전을 지원합니다. 스크립트는 프로젝트에 몇 가지 검증 검사를 적용하고 검증이 실패하면 경고를 출력합니다. 그렇지 않으면 자바 또는 Kotlin으로 작성된 Android Gradle 프로젝트의 리소스에 이름이 바뀐 클래스 및 패키지의 매핑을 적용합니다.

usage: ./media3-migration.sh [-p|-c|-d|-v]|[-m|-l [-x <path>] [-f] PROJECT_ROOT]
 PROJECT_ROOT: path to your project root (location of 'gradlew')
 -p: list package mappings and then exit
 -c: list class mappings (precedence over package mappings) and then exit
 -d: list dependency mappings and then exit
 -l: list files that will be considered for rewrite and then exit
 -x: exclude the path from the list of file to be changed: 'app/src/test'
 -m: migrate packages, classes and dependencies to AndroidX Media3
 -f: force the action even when validation fails
 -v: print the exoplayer2/media3 version strings of this script
 -h, --help: show this help text

마이그레이션 스크립트 사용

  1. 앱을 업데이트한 버전에 해당하는 GitHub의 ExoPlayer 프로젝트 태그에서 이전 스크립트를 다운로드합니다.

    curl -o media3-migration.sh \
      "https://raw.githubusercontent.com/google/ExoPlayer/r2.19.1/media3-migration.sh"
    
  2. 스크립트를 실행 가능하게 만듭니다.

    chmod 744 media3-migration.sh
    
  3. --help를 사용하여 스크립트를 실행하여 옵션에 대해 알아봅니다.

  4. -l를 사용하여 스크립트를 실행하여 이전을 위해 선택된 파일 집합을 나열합니다(-f를 사용하여 경고 없이 강제로 나열).

    ./media3-migration.sh -l -f /path/to/gradle/project/root
    
  5. -m를 사용하여 스크립트를 실행하여 패키지, 클래스, 모듈을 Media3에 매핑합니다. -m 옵션을 사용하여 스크립트를 실행하면 선택한 파일에 변경사항이 적용됩니다.

    • 변경하지 않고 검증 오류에서 중지
    ./media3-migration.sh -m /path/to/gradle/project/root
    
    • 강제 실행

    스크립트에서 기본 요건 위반을 발견하면 -f 플래그를 사용하여 마이그레이션을 강제할 수 있습니다.

    ./media3-migration.sh -m -f /path/to/gradle/project/root
    
 # list files selected for migration when excluding paths
 ./media3-migration.sh -l -x "app/src/test/" -x "service/" /path/to/project/root
 # migrate the selected files
 ./media3-migration.sh -m -x "app/src/test/" -x "service/" /path/to/project/root

-m 옵션으로 스크립트를 실행한 후 다음 수동 단계를 완료합니다.

  1. 스크립트가 코드를 어떻게 변경했는지 확인: diff 도구를 사용하고 잠재적인 문제를 해결합니다. 스크립트에 -f 옵션을 전달하지 않고 도입된 일반적인 문제가 있다고 생각되면 버그를 신고하세요.
  2. 프로젝트 빌드: ./gradlew clean build를 사용하거나 Android 스튜디오에서 File > Sync Project with Gradle Files, Build > Clean project, Build > Rebuild project를 차례로 선택합니다(Android 스튜디오의 'Build - Build Output' 탭에서 빌드 모니터링).

권장 후속 조치 단계:

  1. 불안정한 API 사용에 관한 오류 선택을 해결합니다.
  2. 지원 중단된 API 호출 대체: 추천 대체 API를 사용합니다. Android 스튜디오에서 경고 위로 포인터를 가져간 후 지원 중단된 기호의 JavaDoc을 참조하여 지정된 호출 대신 사용할 항목을 확인합니다.
  3. 가져오기 문 정렬: Android 스튜디오에서 프로젝트를 연 다음 프로젝트 뷰어에서 패키지 폴더 노드를 마우스 오른쪽 버튼으로 클릭하고 변경된 소스 파일이 포함된 패키지에서 가져오기 최적화를 선택합니다.

MediaSessionConnector를 androidx.media3.session.MediaSession로 바꿉니다.

기존 MediaSessionCompat 환경에서 MediaSessionConnector는 플레이어의 상태를 세션의 상태와 동기화하고 적절한 플레이어 메서드에 위임해야 하는 컨트롤러에서 명령어를 수신하는 역할을 담당했습니다. AndroidX Media3에서는 커넥터 없이 MediaSession에서 직접 이 작업을 실행합니다.

  1. MediaSessionConnector의 모든 참조 및 사용 삭제: 자동 스크립트를 사용하여 ExoPlayer 클래스와 패키지를 이전한 경우 스크립트가 해결할 수 없는 MediaSessionConnector와 관련하여 코드를 컴파일할 수 없는 상태로 남겨두었을 수 있습니다. 앱을 빌드하거나 시작하려고 하면 Android 스튜디오에 손상된 코드가 표시됩니다.

  2. 종속 항목을 유지하는 build.gradle 파일에서 AndroidX Media3 세션 모듈에 구현 종속 항목을 추가하고 기존 종속 항목을 삭제합니다.

    implementation "androidx.media3:media3-session:1.11.1"
    
  3. MediaSessionCompat를 androidx.media3.session.MediaSession로 바꿉니다.

  4. 기존 MediaSessionCompat를 만든 코드 사이트에서 androidx.media3.session.MediaSession.Builder를 사용하여 MediaSession를 빌드합니다. 플레이어를 전달하여 세션 빌더를 구성합니다.

    Kotlin

    val player = ExoPlayer.Builder(context).build()
    mediaSession = MediaSession.Builder(context, player).setCallback(MySessionCallback()).build()

    자바

    ExoPlayer player = new ExoPlayer.Builder(context).build();
    mediaSession =
        new MediaSession.Builder(context, player).setCallback(new MySessionCallback()).build();

  5. 앱에서 요구하는 대로 MySessionCallback를 구현합니다. 이는 선택사항입니다. 컨트롤러가 플레이어에 미디어 항목을 추가하도록 허용하려면 MediaSession.Callback.onAddMediaItems()를 구현합니다. 이전 버전과 호환되는 방식으로 재생을 위해 플레이어에 미디어 항목을 추가하는 다양한 현재 및 기존 API 메서드를 제공합니다. 여기에는 Media3 컨트롤러의 MediaController.set/addMediaItems() 메서드와 기존 API의 TransportControls.prepareFrom*/playFrom* 메서드가 포함됩니다. onAddMediaItems의 샘플 구현은 세션 데모 앱의 PlaybackService에서 확인할 수 있습니다.

  6. 마이그레이션 전에 세션을 폐기한 코드 사이트에서 미디어 세션을 해제합니다.

    Kotlin

    mediaSession?.run {
      player.release()
      release()
      mediaSession = null
    }

    자바

    if (mediaSession != null) {
      mediaSession.getPlayer().release();
      mediaSession.release();
      mediaSession = null;
    }

Media3의 MediaSessionConnector 기능

다음 표에는 이전에 MediaSessionConnector에서 구현된 기능을 처리하는 Media3 API가 나와 있습니다.

MediaSessionConnectorAndroidX Media3
CustomActionProvider MediaSession.Callback.onCustomCommand()/ MediaSession.setMediaButtonPreferences()
PlaybackPreparer MediaSession.Callback.onAddMediaItems() (prepare()는 내부적으로 호출됨)
QueueNavigator ForwardingSimpleBasePlayer
QueueEditor MediaSession.Callback.onAddMediaItems()
RatingCallback MediaSession.Callback.onSetRating()
PlayerNotificationManager DefaultMediaNotificationProvider/ MediaNotification.Provider

MediaBrowserService를 MediaLibraryService로 이전

AndroidX Media3에는 MediaBrowserServiceCompat를 대체하는 MediaLibraryService가 도입되었습니다. MediaLibraryService의 JavaDoc과 그 상위 클래스인 MediaSessionService는 API와 서비스의 비동기 프로그래밍 모델에 대한 좋은 소개를 제공합니다.

MediaLibraryService는 MediaBrowserService와 하위 호환됩니다. MediaBrowserCompat 또는 MediaControllerCompat를 사용하는 클라이언트 앱은 MediaLibraryService에 연결할 때 코드 변경 없이 계속 작동합니다. 클라이언트의 경우 앱이 MediaLibraryService를 사용하는지 아니면 기존 MediaBrowserServiceCompat를 사용하는지 여부가 투명합니다.

서비스, 활동, 외부 앱이 있는 앱 구성요소 다이어그램
그림 1: 미디어 앱 구성요소 개요
  1. 이전 버전과의 호환성이 작동하려면 AndroidManifest.xml의 서비스에 두 서비스 인터페이스를 모두 등록해야 합니다. 이렇게 하면 클라이언트가 필요한 서비스 인터페이스를 통해 서비스를 찾습니다.

    <service android:name=".MusicService" android:exported="true">
        <intent-filter>
            <action android:name="androidx.media3.session.MediaLibraryService"/>
            <action android:name="android.media.browse.MediaBrowserService" />
        </intent-filter>
    </service>
    
  2. 종속 항목을 유지하는 build.gradle 파일에서 AndroidX Media3 세션 모듈에 구현 종속 항목을 추가하고 기존 종속 항목을 삭제합니다.

    implementation "androidx.media3:media3-session:1.11.1"
    
  3. MediaBrowserService 대신 MediaLibraryService에서 상속하도록 서비스 변경 앞서 언급했듯이 MediaLibraryService는 기존 MediaBrowserService와 호환됩니다. 따라서 서비스가 클라이언트에 제공하는 더 광범위한 API는 여전히 동일합니다. 따라서 앱은 MediaBrowserService를 구현하는 데 필요한 대부분의 로직을 유지하고 이를 새로운 MediaLibraryService에 맞게 조정할 수 있습니다.

    기존 MediaBrowserServiceCompat와 비교한 주요 차이점은 다음과 같습니다.

    • 서비스 수명 주기 메서드 구현: 서비스 자체에서 재정의해야 하는 메서드는 onCreate 및 onDestroy입니다. 여기서 앱은 라이브러리 세션, 플레이어, 기타 리소스를 할당하거나 해제합니다. 표준 서비스 수명 주기 메서드 외에도 앱은 onGetSession(MediaSession.ControllerInfo)를 재정의하여 onCreate에서 빌드된 MediaLibrarySession를 반환해야 합니다.

    • MediaLibraryService.MediaLibrarySessionCallback 구현: 세션을 빌드하려면 실제 도메인 API 메서드를 구현하는 MediaLibraryService.MediaLibrarySessionCallback가 필요합니다. 따라서 기존 서비스의 API 메서드를 재정의하는 대신 MediaLibrarySession.Callback의 메서드를 재정의합니다.

      그런 다음 콜백을 사용하여 MediaLibrarySession를 빌드합니다.

      Kotlin

      mediaLibrarySession = MediaLibrarySession.Builder(context, player, MySessionCallback()).build()

      자바

      mediaLibrarySession =
          new MediaLibrarySession.Builder(context, player, new MySessionCallback()).build();

      API 문서에서 MediaLibrarySessionCallback의 전체 API를 찾습니다.

    • MediaSession.Callback.onAddMediaItems() 구현: 콜백 onAddMediaItems(MediaSession, ControllerInfo, List<MediaItem>)는 이전 버전과 호환되는 방식으로 재생을 위해 플레이어에 미디어 항목을 추가하는 다양한 현재 및 기존 API 메서드를 제공합니다. 여기에는 Media3 컨트롤러의 MediaController.set/addMediaItems() 메서드와 기존 API의 TransportControls.prepareFrom*/playFrom* 메서드가 포함됩니다. 콜백의 샘플 구현은 세션 데모 앱의 PlaybackService에서 찾을 수 있습니다.

    • AndroidX Media3는 MediaBrowserCompat.MediaItem 및 MediaMetadataCompat 대신 androidx.media3.common.MediaItem를 사용합니다. 기존 클래스에 연결된 코드 부분을 적절하게 변경하거나 Media3 MediaItem에 매핑해야 합니다.

    • MediaBrowserServiceCompat의 분리 가능한 Result 접근 방식과 달리 일반적인 비동기 프로그래밍 모델이 Futures로 변경되었습니다. 서비스 구현은 결과를 분리하는 대신 비동기 ListenableFuture를 반환하거나 즉시 Future를 반환하여 값을 직접 반환할 수 있습니다.

PlayerNotificationManager 삭제

MediaLibraryService는 미디어 알림을 자동으로 지원하며 MediaLibraryService 또는 MediaSessionService를 사용할 때 PlayerNotificationManager를 삭제할 수 있습니다.

앱은 DefaultMediaNotificationProvider를 대체하는 onCreate()에서 커스텀 MediaNotification.Provider를 설정하여 알림을 맞춤설정할 수 있습니다. 그러면 MediaLibraryService가 필요에 따라 포그라운드에서 서비스를 시작합니다.

MediaLibraryService.updateNotification()를 재정의하면 앱이 필요에 따라 알림을 게시하고 포그라운드에서 서비스를 시작하거나 중지하는 전체 소유권을 추가로 가질 수 있습니다.

MediaBrowser를 사용하여 클라이언트 코드 이전

AndroidX Media3를 사용하면 MediaBrowser가 MediaController/Player 인터페이스를 구현하며 미디어 라이브러리를 탐색하는 것 외에도 미디어 재생을 제어하는 데 사용할 수 있습니다. 기존 환경에서 MediaBrowserCompat와 MediaControllerCompat를 만들어야 했다면 Media3에서 MediaBrowser만 사용하여 동일한 작업을 할 수 있습니다.

MediaBrowser를 빌드하고 서비스 연결이 설정될 때까지 기다릴 수 있습니다.

Kotlin

scope.launch {
  val sessionToken = SessionToken(context, ComponentName(context, "MusicService"))
  browser =
    MediaBrowser.Builder(context, sessionToken)
      .setListener(BrowserListener())
      .buildAsync()
      .await()
}

자바

SessionToken sessionToken =
    new SessionToken(context, new ComponentName(context, "MusicService"));
ListenableFuture<MediaBrowser> browserFuture =
    new MediaBrowser.Builder(context, sessionToken)
        .setListener(new BrowserListener())
        .buildAsync();

미디어 세션에서 재생 제어를 참고하여 백그라운드에서 재생을 제어하는 MediaController를 만드는 방법을 알아보세요.

추가 단계 및 정리

다음 섹션에서는 초기 이전을 완료한 후 불안정한 API 린트 오류와 지원 중단된 API를 처리하는 방법을 설명합니다.

불안정한 API 오류

Media3로 이전한 후 불안정한 API 사용에 관한 린트 오류가 표시될 수 있습니다. 이러한 API는 안전하게 사용할 수 있으며 린트 오류는 새로운 바이너리 호환성 정책의 부작용입니다. 엄격한 바이너리 호환성이 필요하지 않은 경우 @OptIn 주석을 사용하여 이러한 오류를 안전하게 억제할 수 있습니다.

배경

ExoPlayer v1 또는 v2는 후속 버전 간에 라이브러리의 엄격한 바이너리 호환성을 제공하지 않았습니다. ExoPlayer API 노출 영역은 앱이 재생의 거의 모든 측면을 맞춤설정할 수 있도록 설계상 매우 큽니다. ExoPlayer의 후속 버전에서는 기호 이름 바꾸기 또는 기타 브레이킹 체인지 (예: 인터페이스에 필요한 새 메서드)가 도입되기도 했습니다. 대부분의 경우 이러한 중단은 개발자가 사용을 이전할 수 있도록 몇 가지 버전에 대해 이전 기호를 지원 중단하는 동시에 새 기호를 도입하여 완화되었지만 항상 가능한 것은 아니었습니다.

이러한 브레이킹 체인지로 인해 ExoPlayer v1 및 v2 라이브러리 사용자에게 두 가지 문제가 발생했습니다.

  1. ExoPlayer 버전으로 업그레이드하면 코드 컴파일이 중지될 수 있습니다.
  2. ExoPlayer에 직접적으로 그리고 중간 라이브러리를 사용하여 종속된 앱은 두 종속 항목이 동일한 버전인지 확인해야 했습니다. 그렇지 않으면 바이너리 비호환성으로 인해 런타임 비정상 종료가 발생할 수 있었습니다.

Media3의 개선사항

Media3는 API 노출 영역의 하위 집합에 바이너리 호환성을 제공합니다. 바이너리 호환성을 제공하지 않는 부분은 @UnstableApi로 표시됩니다. 이러한 구분을 명확히 하기 위해 불안정한 API 기호를 사용하면 @OptIn로 주석을 달지 않는 한 린트 오류가 발생합니다.

ExoPlayer v2에서 Media3로 이전한 후 불안정한 API 린트 오류가 많이 표시될 수 있습니다. 이로 인해 Media3가 ExoPlayer v2보다 '덜 안정적'인 것처럼 보일 수 있습니다. 호출은 건너뛸 수 없습니다. Media3 API의 '불안정한' 부분은 ExoPlayer v2 API 표시 경로의 전체와 동일한 수준의 안정성을 가지며 안정적인 Media3 API 표시 경로의 바이너리 호환성은 ExoPlayer v2에서 전혀 사용할 수 없습니다. 차이점은 이제 린트 오류가 다양한 수준의 안정성을 알린다는 것입니다.

불안정한 API 린트 오류 처리

@OptIn를 사용하여 불안정한 API의 Java 및 Kotlin 사용에 주석을 추가하는 방법에 관한 자세한 내용은 이러한 린트 오류에 관한 문제 해결 섹션을 참고하세요.

지원 중단된 API

Android 스튜디오에서 지원 중단된 API 호출에 취소선이 표시될 수 있습니다. 이러한 호출은 적절한 대안으로 대체하는 것이 좋습니다. 기호 위로 마우스를 가져가면 대신 사용할 API를 알려주는 JavaDoc이 표시됩니다.

스크린샷: 지원 중단된 메서드의 대체 메서드가 포함된 JavaDoc을 표시하는 방법
그림 3: Android 스튜디오의 JavaDoc 도움말에서 지원 중단된 기호의 대안을 제안합니다.

코드 샘플 및 데모 앱