Android MediaPlayer

미디어 플레이어 개요

Android 멀티미디어 프레임 워크에는 다양한 일반 미디어 유형의 재생을 지원하므로 오디오, 비디오 및 이미지를 애플리케이션에 쉽게 통합 할 수 있습니다.

응용 프로그램의 리소스 (원시 리소스)에 저장된 미디어 파일, 파일 시스템의 독립형 파일 또는 네트워크 연결을 통해 도착하는 데이터 스트림에서 모두 MediaPlayer API를 사용하여 오디오 또는 비디오를 재생할 수 있습니다.

참고 : 오디오 데이터는 표준 출력 장치로만 재생할 수 있습니다. 현재는 모바일 장치 스피커 또는 Bluetooth 헤드셋입니다. 통화 중에는 대화 오디오에서 사운드 파일을 재생할 수 없습니다.

기본

다음 클래스는 Android 프레임 워크에서 사운드 및 비디오를 재생하는 데 사용됩니다.

  • MediaPlayer
    이 클래스는 사운드 및 비디오를 재생하기위한 기본 API입니다.

  • AudioManager
    이 클래스는 장치의 오디오 소스 및 오디오 출력을 관리합니다.

Manifest 선언

MediaPlayer를 사용하여 응용 프로그램 개발을 시작하기 전에 매니페스트에 관련 기능을 사용할 수 있는 적절한 선언이 있는지 확인하십시오.

  • 인터넷 권한 : MediaPlayer를 사용하여 네트워크 기반 컨텐츠를 스트리밍하는 경우 응용 프로그램은 네트워크 액세스를 요청해야합니다.
1
<uses-permission android:name="android.permission.INTERNET" />
1
<uses-permission android:name="android.permission.WAKE_LOCK" />

MediaPlayer 사용

미디어 프레임 워크의 가장 중요한 구성 요소 중 하나는 MediaPlayer 클래스입니다. 이 클래스의 객체는 최소한의 설정으로 오디오와 비디오를 모두 가져 와서 디코딩하고 재생할 수 있습니다.

다음과 같은 여러 가지 미디어 소스를 지원합니다.

  • 지역 자원
  • Content Resolver에서 얻을 수있는 것과 같은 내부 URI
  • 외부 URL (스트리밍)

Android가 지원하는 미디어 형식 목록은 지원되는 미디어 형식 페이지를 참조하십시오.

다음은 응용 프로그램의 res/raw/ 디렉토리에 저장된 로컬 raw 리소스로 사용 가능한 오디오를 재생하는 방법의 예입니다.

1
2
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.sound_file_1);
mediaPlayer.start(); // no need to call prepare(); create() does that for you

이 경우 “raw”리소스는 시스템이 특정 방식으로 구문 분석하지 않는 파일입니다. 그러나이 리소스의 내용은 원본 오디오가 아니어야합니다.

지원되는 형식 중 하나로 올바르게 인코딩되고 형식화 된 미디어 파일이어야합니다.

다음은 시스템에서 로컬로 사용 가능한 URI(예 : Content Resolver를 통해 얻은)에서 재생하는 방법입니다.

1
2
3
4
5
6
Uri myUri = ....; // initialize Uri here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), myUri);
mediaPlayer.prepare();
mediaPlayer.start();

HTTP 스트리밍을 통해 원격 URL에서 재생하는 방법은 다음과 같습니다.

1
2
3
4
5
6
String url = "http://........"; // your URL here
MediaPlayer mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(url);
mediaPlayer.prepare(); // might take long! (for buffering, etc)
mediaPlayer.start();

참고 : 온라인 미디어 파일을 스트리밍하기 위해 URL을 전달하는 경우 파일을 점진적으로 다운로드 할 수 있어야합니다.

주의 : setDataSource()를 사용하는 경우 참조하는 파일이 없을 수 있으므로 IllegalArgumentException 및 IOException을 catch하거나 pass해야합니다.

비동기 준비

MediaPlayer를 사용하는 것은 원칙적으로 간단 할 수 있습니다.

그러나 일반적인 Android 애플리케이션과 올바르게 통합하려면 몇 가지 사항이 더 필요하다는 점을 명심해야합니다.

예를 들어, 미디어 데이터를 가져오고 디코딩하는 작업이 필요할 수 있으므로 prepare() 호출에 시간이 오래 걸릴 수 있습니다.

따라서 실행하는 데 시간이 오래 걸릴 수있는 메소드의 경우와 마찬가지로 애플리케이션의 UI 스레드에서 호출하지 않아야합니다.

이렇게하면 메서드가 반환 될 때까지 UI가 중단됩니다. 이는 매우 나쁜 사용자 경험이며 ANR (Application Not Responding) 오류를 일으킬 수 있습니다.

리소스가 빠르게로드 될 것으로 예상 되더라도 UI에서 응답하는 데 10 초 이상 걸리는 작업이 눈에 띄게 일시 중지되고 사용자에게 응용 프로그램 속도가 느리다는 인상을줍니다.

UI 스레드를 중단하지 않으려면 다른 스레드를 생성하여 MediaPlayer를 준비하고 완료되면 기본 스레드에 알리십시오.

그러나 스레딩 로직을 직접 작성할 수는 있지만 MediaPlayer를 사용할 때이 패턴이 일반적이므로 프레임 워크는 prepareAsync() 메소드를 사용하여이 작업을 수행하는 편리한 방법을 제공합니다.

이 방법은 백그라운드에서 미디어 준비를 시작하고 즉시 돌아옵니다.

미디어 준비가 끝나면 setOnPreparedListener()를 통해 구성된 MediaPlayer.OnPreparedListeneronPrepared() 메서드가 호출됩니다.

상태 관리

명심해야 할 MediaPlayer의 또 다른 측면은 상태 기반이라는 것입니다.

즉, MediaPlayer에는 코드를 작성할 때 항상 알아야하는 내부 상태가 있습니다.

특정 작업은 플레이어가 특정 상태에있을 때만 유효하기 때문입니다.

잘못된 상태에서 작업을 수행하면 시스템에서 예외가 발생하거나 다른 바람직하지 않은 동작이 발생할 수 있습니다.

the state diagram

MediaPlayer 클래스의 설명서는 어떤 상태에서 MediaPlayer를 다른 상태로 옮길지를 설명하는 완전한 상태 다이어그램을 보여줍니다.

예를 들어 새 MediaPlayer를 만들면 유휴 상태입니다. 이 시점에서 setDataSource()를 호출하여 초기화 상태로 가져 와서 초기화해야합니다.

그 후, 당신은 prepare() 또는 PrepareAsync() 메소드를 사용하여 그것을 준비해야합니다.

MediaPlayer가 준비를 마치면 준비 됨 상태가되며 start()를 호출하여 미디어를 재생할 수 있습니다.

이 시점에서 다이어그램이 보여주는 것처럼 start(), pause()seekTo()와 같은 메서드를 호출하여 Started, Paused 및 PlaybackCompleted 상태 사이를 이동할 수 있습니다.

그러나 stop()을 호출하면 MediaPlayer를 다시 준비 할 때까지 start()를 다시 호출 할 수 없습니다.

MediaPlayer객체와 상호 작용하는 코드를 작성할 때는 항상 잘못된 상태에서 메소드를 호출하는 것이 버그의 원인이므로 상태 다이어그램을 명심하십시오.

미디어 플레이어 릴리즈

MediaPlayer는 귀중한 시스템 리소스를 소비 할 수 있습니다. 따라서 필요 이상으로 MediaPlayer 인스턴스에 매달리지 않도록 항상 추가 예방 조치를 취해야합니다.

작업이 끝나면 항상 release()를 호출하여 할당 된 시스템 리소스가 올바르게 해제되도록해야합니다.

예를 들어, MediaPlayer를 사용 중이고 활동이 onStop()에 대한 호출을 수신하는 경우 활동이 사용자와 상호 작용하지 않는 동안 (만약 재생하지 않는 한) MediaPlayer를 보류하는 것이 의미가 없으므로 MediaPlayer를 해제해야합니다.

물론 활동이 재개되거나 다시 시작되면 재생을 다시 시작하기 전에 새 MediaPlayer를 생성하고 다시 준비해야합니다.

MediaPlayer를 해제 한 다음 무효화하는 방법은 다음과 같습니다.

1
2
mediaPlayer.release();
mediaPlayer = null;

예를 들어, 활동이 중지 될 때 MediaPlayer를 해제하는 것을 잊었을 때 발생할 수있는 문제를 고려하지만 활동이 다시 시작될 때 새로 작성하십시오.

아시다시피, 사용자가 화면 방향을 변경하거나 다른 방식으로 장치 구성을 변경하면 시스템은 활동을 다시 시작하여 (기본적으로)이를 처리하므로 사용자가 회전함에 따라 모든 시스템 리소스를 빠르게 소비 할 수 있습니다.

각 방향이 바뀔 때마다 절대 해제하지 않는 새 MediaPlayer를 생성하기 때문에 세로와 가로 사이의 장치를 앞뒤로 움직입니다.

런타임 재시작에 대한 자세한 정보는 런타임 변경 처리를 참조하십시오.

사용자가 활동을 떠날 때에도 내장 음악 응용 프로그램과 같은 방식으로 “백그라운드 미디어”를 계속 재생하려면 어떻게되는지 궁금 할 것입니다.

이 경우 다음 섹션에서 설명하는 것처럼 서비스가 제어하는 MediaPlayer가 필요합니다.

서비스에서 MediaPlayer 사용

응용 프로그램이 화면에없는 경우에도 백그라운드에서 미디어를 재생하려는 경우 (즉, 사용자가 다른 응용 프로그램과 상호 작용하는 동안 계속 재생하려는 경우) 서비스를 시작하고 거기서부터 MediaPlayer 인스턴스를 제어해야합니다.

MediaPlayerMediaBrowserServiceCompat 서비스에 포함시키고 다른 활동에서 MediaBrowserCompat와 상호 작용하도록해야합니다.

이 클라이언트 / 서버 설정에주의해야합니다. 백그라운드 서비스에서 실행중인 플레이어가 나머지 시스템과 상호 작용하는 방식에 대한 기대가 있습니다.

응용 프로그램이 이러한 기대치를 충족하지 못하면 사용자에게 좋지 않은 경험이있을 수 있습니다. 자세한 내용은 오디오 앱 빌드를 참조하십시오.

이 섹션에서는 서비스 내부에서 구현 될 때 MediaPlayer를 관리하기위한 특별 지침에 대해 설명합니다.

비동기 적으로 실행

우선 Activity와 마찬가지로 서비스의 모든 작업은 기본적으로 단일 스레드에서 수행됩니다.

사실, 동일한 애플리케이션에서 활동 및 서비스를 실행하는 경우 기본적으로 동일한 스레드 (“메인 스레드”)를 사용합니다.

따라서 서비스는 들어오는 의도를 신속하게 처리해야하며 응답 할 때 긴 계산을 수행하지 않아야합니다.

과도한 작업이나 호출 차단이 예상되는 경우 직접 구현하는 다른 스레드에서 또는 비동기 처리를 위해 프레임 워크의 많은 기능을 사용하여 이러한 작업을 비동기 적으로 수행해야합니다.

예를 들어, 메인 스레드에서 MediaPlayer를 사용하는 경우, 준비가 완료되고 재생을 시작할 수 있도록 알림을 받으려면 Prepare()가 아니라 PrepareAsync()를 호출하고 MediaPlayer.OnPreparedListener를 구현해야합니다.

예를 들면 다음과 같습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MyService extends Service implements MediaPlayer.OnPreparedListener {
private static final String ACTION_PLAY = "com.example.action.PLAY";
MediaPlayer mediaPlayer = null;

public int onStartCommand(Intent intent, int flags, int startId) {
...
if (intent.getAction().equals(ACTION_PLAY)) {
mediaPlayer = ... // initialize it here
mediaPlayer.setOnPreparedListener(this);
mediaPlayer.prepareAsync(); // prepare async to not block main thread
}
}

/** Called when MediaPlayer is ready */
public void onPrepared(MediaPlayer player) {
player.start();
}
}

비동기 오류 처리

동기 작업의 경우 일반적으로 예외 또는 오류 코드와 함께 오류가 발생하지만 비동기 리소스를 사용할 때마다 응용 프로그램에 오류에 대한 적절한 알림이 표시되어야합니다.

MediaPlayer의 경우 MediaPlayer.OnErrorListener를 구현하고 MediaPlayer 인스턴스에서 설정하여이를 수행 할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyService extends Service implements MediaPlayer.OnErrorListener {
MediaPlayer mediaPlayer;

public void initMediaPlayer() {
// ...initialize the MediaPlayer here...
mediaPlayer.setOnErrorListener(this);
}

@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// ... react appropriately ...
// The MediaPlayer has moved to the Error state, must be reset!
}
}

오류가 발생하면 MediaPlayer가 오류 상태로 이동하고 (전체 상태 다이어그램은 MediaPlayer 클래스의 설명서 참조) 다시 사용하기 전에 재설정해야합니다.

깨우기 잠금 사용

백그라운드에서 미디어를 재생하는 응용 프로그램을 디자인 할 때 서비스가 실행되는 동안 장치가 절전 모드로 전환 될 수 있습니다.

Android 시스템은 기기가 절전 모드 일 때 배터리 절약을 시도하기 때문에 시스템은 CPU 및 WiFi 하드웨어를 포함하여 필요하지 않은 전화기 기능을 종료하려고합니다.

그러나 서비스가 음악을 재생하거나 스트리밍하는 경우 시스템이 재생을 방해하지 못하게하려고합니다.

이러한 조건에서 서비스가 계속 실행되도록하려면 “깨우기 잠금”을 사용해야합니다.

깨우기 잠금은 전화가 유휴 상태 인 경우에도 응용 프로그램에서 사용 가능한 일부 기능을 사용하고 있음을 시스템에 알리는 방법입니다.

참고 : 항상 잠금 장치를 사용하지 말고 장치의 배터리 수명을 크게 단축 시키므로 꼭 필요한만큼만 잡아야합니다.

MediaPlayer가 재생되는 동안 CPU가 계속 실행되도록하려면 MediaPlayer를 초기화 할 때 setWakeMode() 메서드를 호출하십시오.

그렇게하면 MediaPlayer는 재생하는 동안 지정된 잠금을 유지하고 일시 중지 또는 중지되면 잠금을 해제합니다.

1
2
3
mediaPlayer = new MediaPlayer();
// ... other initialization here ...
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);

그러나이 위 예제에서 얻은 깨우기 잠금은 CPU가 깨어있는 상태 만 보장합니다.

네트워크를 통해 미디어를 스트리밍하고 Wi-Fi를 사용하는 경우 WifiLock도 잡고 싶을 것입니다.

WifiLock은 수동으로 획득하여 해제해야합니다. 따라서 원격 URL을 사용하여 MediaPlayer를 준비하기 시작하면 Wi-Fi 잠금을 생성하고 획득해야합니다.

예를 들면 다음과 같습니다.

1
2
3
4
WifiLock wifiLock = ((WifiManager) getSystemService(Context.WIFI_SERVICE))
.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock");

wifiLock.acquire();

미디어를 일시 중지 또는 중지하거나 더 이상 네트워크가 필요하지 않은 경우 잠금을 해제해야합니다.

1
wifiLock.release();

정리 수행

앞에서 언급했듯이 MediaPlayer 객체는 상당한 양의 시스템 리소스를 소비 할 수 있으므로 필요한만큼만 유지하고 작업이 끝나면 release()를 호출해야합니다.

가비지 수집기가 MediaPlayer를 회수하기까지 시간이 걸릴 수 있기 때문에 시스템 가비지 수집에 의존하지 않고이 정리 방법을 명시 적으로 호출하는 것이 중요합니다.

메모리 요구에만 민감하고 다른 미디어 관련 리소스가 부족하지 않기 때문입니다.

따라서 서비스를 사용하는 경우 항상 onDestroy() 메서드를 재정 의하여 MediaPlayer를 릴리스해야합니다.

1
2
3
4
5
6
7
8
9
10
public class MyService extends Service {
MediaPlayer mediaPlayer;
// ...

@Override
public void onDestroy() {
super.onDestroy()
if (mediaPlayer != null) mediaPlayer.release();
}
}

종료 할 때 미디어 플레이어를 해제하는 것 외에 항상 미디어 플레이어를 릴리스 할 수있는 다른 기회를 찾아야합니다.

예를 들어, 오디오 포커스를 잃은 후 오랫동안 미디어를 재생할 수 없을 것으로 예상되는 경우 기존 MediaPlayer를 확실히 해제했다가 나중에 다시 만들어야합니다.

반면에 아주 짧은 시간 동안 만 재생을 중단 할 것으로 예상되는 경우 MediaPlayer를 다시 작성하여 다시 준비해야하는 오버 헤드를 피하기 위해 MediaPlayer를 기다려야합니다.

디지털 권한 관리 (DRM)

Android 8.0 (API 레벨 26)부터 MediaPlayer에는 DRM으로 보호되는 자료의 재생을 지원하는 API가 포함되어 있습니다.

MediaDrm에서 제공하는 저수준 API와 비슷하지만 더 높은 수준에서 작동하며 기본 추출기, drm 및 암호화 개체를 노출하지 않습니다.

MediaPlayer DRM API는 MediaDrm의 모든 기능을 제공하지는 않지만 가장 일반적인 사용 사례를 지원합니다.

현재 구현은 다음 컨텐츠 유형을 처리 할 수 있습니다.

  • Widevine으로 보호되는 로컬 미디어 파일
  • 광범위하게 보호되는 원격 / 스트리밍 미디어 파일

다음 코드 스 니펫은 간단한 동기 구현에서 새 DRM MediaPlayer 메소드를 사용하는 방법을 보여줍니다.

DRM 제어 미디어를 관리하려면 다음과 같이 일반적인 MediaPlayer 호출 흐름과 함께 새로운 방법을 포함해야합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
setDataSource();
setOnDrmConfigHelper(); // optional, for custom configuration
prepare();
if (getDrmInfo() != null) {
prepareDrm();
getKeyRequest();
provideKeyResponse();
}

// MediaPlayer is now ready to use
start();
// ...play/pause/resume...
stop();
releaseDrm();

평소와 같이 MediaPlayer 객체를 초기화하고 setDataSource()를 사용하여 소스를 설정하여 시작합니다.

그런 다음 DRM을 사용하려면 다음 단계를 수행하십시오.

  1. 앱이 사용자 지정 구성을 수행하도록하려면 OnDrmConfigHelper 인터페이스를 정의하고 setOnDrmConfigHelper()를 사용하여 플레이어에 연결하십시오.
  2. prepare()를 호출하십시오.
  3. getDrmInfo()를 호출하십시오. 소스에 DRM 내용이 있으면이 메서드는 null이 아닌 MediaPlayer.DrmInfo 값을 반환합니다.

MediaPlayer.DrmInfo가 존재하는 경우 :

  1. 사용 가능한 UUID의 맵을 검사하고 하나를 선택하십시오.
  2. PrepareDrm()을 호출하여 현재 소스에 대한 DRM 구성을 준비하십시오.
    • OnDrmConfigHelper 콜백을 작성하고 등록한 경우, PrepareDrm()이 실행되는 동안 호출됩니다. 이를 통해 DRM 세션을 열기 전에 DRM 속성의 사용자 지정 구성을 수행 할 수 있습니다. 콜백은 PrepareDrm()을 호출 한 스레드에서 동 기적으로 호출됩니다. DRM 특성에 액세스하려면 getDrmPropertyString()setDrmPropertyString()을 호출하십시오. 긴 작업을 수행하지 마십시오.
    • 장치가 아직 준비되지 않은 경우, PrepareDrm()은 프로비저닝 서버에 액세스하여 장치를 공급합니다. 네트워크 연결에 따라 시간이 달라질 수 있습니다.
  3. 라이센스 키로 보낼 불투명 한 키 요청 바이트 배열을 얻으려면 getKeyRequest()를 호출하십시오.
  4. 라이센스 서버로부터받은 키 응답에 대해 DRM 엔진에 알리려면 provideKeyResponse()를 호출하십시오. 결과는 키 요청 유형에 따라 다릅니다.
    • 응답이 오프라인 키 요청에 대한 것이라면 결과는 키 세트 식별자입니다. 이 키 세트 ID를 restoreKeys()와 함께 사용하여 키를 새 세션으로 복원 할 수 있습니다.
    • 응답이 스트리밍 또는 릴리스 요청에 대한 경우 결과는 널입니다.

PrepareDrm()을 비동기 적으로 실행

기본적으로 PrepareDrm()은 동기적으로 실행되어 준비가 완료 될 때까지 차단됩니다.

그러나 새 장치에서 처음으로 DRM을 준비하는 경우 공급을 요구할 수도 있는데, 이는 PrepareDrm()에 의해 내부적으로 처리되며 관련된 네트워크 작업으로 인해 완료하는 데 시간이 걸릴 수 있습니다.

MediaPlayer.OnDrmPreparedListener를 정의하고 설정하여 PrepareDrm()에서 블로킹을 피할 수 있습니다.

OnDrmPreparedListener를 설정하면 PrepareDrm()은 백그라운드에서 프로비저닝 (필요한 경우) 및 준비를 수행합니다.

프로비저닝 및 준비가 완료되면 리스너가 호출됩니다.

리스너가 핸들러 스레드에 등록되지 않은 경우 호출 시퀀스 또는 리스너가 실행되는 스레드에 대해 가정해서는 안됩니다.

리스너는 prepareDrm()이 리턴되기 전 또는 후에 호출 될 수 있습니다.

비동기 적으로 DRM 설정

DRM 준비를 위해 MediaPlayer.OnDrmInfoListener를 생성하고 등록하여 플레이어를 시작하여 MediaPlayer.OnDrmPreparedListener를 비동기 적으로 초기화 할 수 있습니다.

아래 그림과 같이 prepareAsync()와 함께 작동합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
setOnPreparedListener();
setOnDrmInfoListener();
setDataSource();
prepareAsync();
// ...

// If the data source content is protected you receive a call to the onDrmInfo() callback.
onDrmInfo() {
prepareDrm();
getKeyRequest();
provideKeyResponse();
}

// When prepareAsync() finishes, you receive a call to the onPrepared() callback.
// If there is a DRM, onDrmInfo() sets it up before executing this callback,
// so you can start the player.
onPrepared() {

start();
}

암호화 된 미디어 처리

Android 8.0 (API 레벨 26)부터 MediaPlayer는 기본 스트림 유형 H.264 및 AAC에 대해 CENC (Common Encryption Scheme) 및 HLS 샘플 레벨 암호화 미디어 (METHOD = SAMPLE-AES)를 해독 할 수 있습니다.

전체 세그먼트 암호화 미디어 (METHOD = AES-128)는 이전에 지원되었습니다.

ContentResolver에서 미디어 검색

미디어 플레이어 응용 프로그램에 유용 할 수있는 또 다른 기능은 사용자가 장치에서 가지고있는 음악을 검색하는 기능입니다.

외부 미디어에 대해 ContentResolver를 쿼리하여이를 수행 할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ContentResolver contentResolver = getContentResolver();
Uri uri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
Cursor cursor = contentResolver.query(uri, null, null, null, null);
if (cursor == null) {
// query failed, handle error.
} else if (!cursor.moveToFirst()) {
// no media on the device
} else {
int titleColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE);
int idColumn = cursor.getColumnIndex(android.provider.MediaStore.Audio.Media._ID);
do {
long thisId = cursor.getLong(idColumn);
String thisTitle = cursor.getString(titleColumn);
// ...process entry...
} while (cursor.moveToNext());
}

이것을 MediaPlayer와 함께 사용하려면 다음을 수행하십시오.

1
2
3
4
5
6
7
8
9
long id = /* retrieve it from somewhere */;
Uri contentUri = ContentUris.withAppendedId(
android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id);

mediaPlayer = new MediaPlayer();
mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer.setDataSource(getApplicationContext(), contentUri);

// ...prepare and start...
공유하기