본문 바로가기
Android 개발/File IO, FilesDir, Storage,

Mp3파일 외부 저장소에서 가져와 재생하기 #안드로이드

by Developer88 2019. 9. 7.
반응형

유저가 가지고 있는 MP3파일을 앱 내부에 저장해 두고 필요할 때 재생할 수 있도록 할 경우가 있는데요.

 

오늘은 외부 저장소에 있는 MP3파일들 중 하나를 선택한 다음,

그 파일을 내부 저장소로 가져와 파일을 재생하는 방법을,

정리하도록 하겠습니다.

참고로 이글은 2024년도에 수정되어 API34(Android13)에 대응되도록 수정하였습니다.

 

이 방법에 대해서 정리하기 전에,

저장소에 대한 개념을 가볍게 정리해 보고 가려고 합니다.

이에 대한 개념이 필요하시지 않은 분들은 바로 3번으로 넘어가 주세요.

 

1. 저장소(Storage)

안드로이드의 Storage의 개념에서 크게 두가지가 있는데요.

바로 내부저장소(Internal Storage)와 외부 저장소(External Storage)입니다.

더 세부적으로 구분할 수도 있겠지만,

오늘 글에 대해서 접근하는데 있어서는 이 개념들이 중요할 것 같습니다.

 

1-1. 내부 저장소(Internal Storage)

안드로이드에서 말하는 내부 저장소는 App내의 private한 공간,

즉, 다른 앱이 마음대로 접근할 수 없는 공간을 말합니다.

 

따라서 이 공간에는 앱에서 private하게 데이터를 저장할 수 있습니다.

앱을 삭제하면 이 공간에 있는 데이터도 날아가 버립니다.

 

1-2. Internal Cache Directory

내부 저장소 내 cache같은 임시 파일을 위한 공간입니다.

getCacheDir()로 접근할 수 있는데요.

너무 많이 쌓이지 않도록 관리가 필요합니다.

 

 

1-3. 외부 저장소(External Storage)

외부 저장소는 유저가 직접 수정할 수도 있고,

다른 앱에서 접근할 수 있는 저장소입니다.

예를 들어, 다운로드된 파일이나, 음악파일등이 있습니다.

 

다운로드된 파일이나 앱을 삭제한 후에도 남아있어야할 데이터들 같은 경우,

이 저장소에 넣어놓습니다.

MP3파일 같은 경우도 마찬가지이지요.

 

안드로이드 공식문서에서는 MediaStore API를 통해서,

외부저장소에 사진, 영상, 오디오 파일을 각각 다르게  접근하도록 하고 있습니다.

  • 사진: MediaStore.Images
  • 영상: MediaStore.Video
  • 오디오: MediaStore.Audio

 

2. 외부 저장소에서 MP3 읽어오기

이제 저장소에 대해서 알게 되었으므로,

외부 저장소에서 파일을 읽어오는 방법에 대해서 정리해볼까요?

 

2-1. Permission (권한 설정)

API33이상에서는 MediaStore에 접근할 때,

오디오, 비디오, 이미지를 구분해서 Permmission을 얻어야 합니다.

 

오디오 파일에 접근하기 위해서 'READ_MEDIA_AUDIO' 권한을,

아래와 같이 요청해야 합니다.

 

private static final int REQUEST_CODE = 1;
private static final String[] PERMISSIONS = {
    Manifest.permission.READ_MEDIA_AUDIO
};

private void requestPermissionsIfNecessary() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        if (checkSelfPermission(Manifest.permission.READ_MEDIA_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(PERMISSIONS, REQUEST_CODE);
        }
    } else {
        if (checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE);
        }
    }
}

 

2-2. 외부 저장소를 읽을 수 있는지 확인

Permisison을 받았다면, 위에서 언급한 대로, 외부 저장소가 있는지 그리고 읽을 수 있는지 확인할 수 있습니다.

getExternalStorageState()을 해서 Environment.MEDIA_MOUNTED가 나온다면,

사용할 외부 저장소가 있는 것이구요.

 

 

 

2-3. 외부 저장소에서 읽어오기

위에서 언급한 것처럼,

오디오 파일은 MediaStore.Audio 타입을 이용하면 됩니다.

 

Audio파일의 칼럼에는 다음과 같은 값이 있습니다.

이를 이용하면 음악데이터의 정보를 쉽게 얻을 수 있겠습니다.

 

 

A. ContentResolver이용하기

MediaStore의 Audio파일에 접근하기 우해서는,

안드로이드 4대 구성요소 중 하나인 ContentResolver를 얻어와야 합니다.

ContentResolver는 ContentProvider로부터 데이터를 가져와주는 역할을 하지요.

 

 

위와 같이 contentResolver를 얻어와서,

query메소드를 이용하면,

SQL에서의 하나의 행과 같은 cursor들에 접근할 수 있는 객체를 반환해 줍니다.

 

아래와 같이 query메소드를 사용할 때는,

첫번째 인자가 중요합니다.

'MediaStore.Audio.Media.EXTERNAL_CONTENT_URI'로 URI를 지정하구요.

두번째 인자를 넣기 위해서,

위에서 본 음악데이터의 컬럼들을 참고해,

어떤 정보가 들어있는 것을 가져올지 정합니다.

이를 array로 만들어 알려주면 됩니다.

아래와 같이 해주면 해당 데이터를 가지는 cursor 객체를 얻을 수 있습니다.

 

 

 

이렇게 얻은 cursor 객체를 어떻게 사용해야 할까요?

 

Cursor객체의 멤버함수인 moveToNext()를 활용하면 모든 행의 데이터를 조회할 수 있는데요.

각 행에서, 

mCursor객체에, getInt()나 getString()으로 컬럼에 해당하는 정보들을 얻어올 수 있습니다.

저희가 얻어올 정보들을 다음과 같습니다.

  • 첫번째 컬럼: (index=0) IS_MUSIC
  • 두번째 컬럼: (index=1) IS_ALARM
  • 4번째 컬럼: (index=3)TITLE
  • 5번째 컬럼: (index=4)ARTIST
  • 6째 컬럼: (index=5)_ID

아래와 같이 공식문서에서 IS_MUSIC컬럼의 값이 0이 아니면 음악파일이라 하는데요.

 

반환되는 각행의 데이터를 가진, mCursor에서,

IS_MUSIC 또는 IS_ALARM 에 해당하는 값이 0이 아니라면,

음악이므로,

그렇게 나온 데이터들을 가지고 mp3데이터들을 만들어 냅니다.

 

 

(한가지 팁으로 cursor객체를 RxJava와 같이 사용해주면 매우 활용도가 높아지는데요.

아쉽게도 cursor는 Iterable을 구현하고 있지 않습니다.

그런데 아래 개발자분의 링크를 참조하면 Iterable을 구현한 RxCursorIterable클래스를 만들어서 사용할 수 있으므로

참조해보면 좋을 것 같습니다.

>> Consume Cursor as RxJava2 Iterable)

 

외부 저장소에서 읽어오는 일은 거의 다 되었는데요.

중요한 것이 남아 있습니다.

나중에 해당 파일을 가져와서 저희 앱의 내부저장소에 저장해서 쓰려면 URI를 알아야 하는데요.

ContentUris의 withAppendedId 메소드를 이용해서 아래와 같이 얻어올 수 있습니다.

그것은 아래와 같이 얻어올 수 있습니다.

 

 

 

3. 내부 저장소에 선택한 파일 저장하기

다행히도 내부 저장소에서는 해당 앱에 할당된 공간이므로 permission은 신경쓰지 않아도 됩니다.

 

다만 한가지 주의할 점은 현재 기기에 저장한 충분한 공간이 남아있는가 인데요.

공간이 부족하면 IOException이 발생하므로 해당 Exception을 catch해서,

유저에게 공간이 부족해서 가져오지 못했다고 알려주어야 합니다.

 

내부 저장소의 위치를 가져오는 메소는 getFilesDir()입니다.

(참고로 위에서 언급했던 내부 cache디렉토리는 getCacheDir()입니다)

 

이제 여기서 만든 파일을 가지고 아래와 같이 실행만 해주면 됩니다.

 

이제 음악이 잘 나오는 것을 확인할 수 있습니다.

728x90

댓글