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

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

by Developer88 2019. 9. 7.
반응형

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

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

그 파일을 내부 저장소로 가져와 파일을 재생하는 방법을 정리하도록 하겠습니다.

 

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

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

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

 

1. 저장소(Storage)

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

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

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

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

 

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

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

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

 

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

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

따라서, 이 공간에 앱에서 삭제해도 남아있어야 할 데이터,

예를 들면, 사진앱에서의 사진같은 데이터는 남겨둬야 하므로,

이러한 사진을 내부 저장소에 저장하면 않되는 것이지요.

 

1-2. Internal Cache Directory

임시적으로 cache로서 사용할 내부 저장소에 대해서도

안드로이드 시스템은 공간을 부여해 주고 있는데요.

대신 이러한 cache파일이 너무 많이 쌓이지 않도록,

cache를 사용할 때는 관리가 필요하겠습니다.

 

 

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

이 공간에 들어있는 데이터는 유저가 직접 수정할 수도 있고,

다른 앱에서 접근할 수도 있는데요.

다만 이 공간자체가 있는지를 사용하기전에 체크해야 합니다.

기기에 따라서는 이 공간이 SD카드를 넣어야만 접근할 수도 있기 때문인데요.

 

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

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

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

안드로이드 시스템 또한 이러한 파일들을 저장하기 위해서 디렉토리를 제공하고 있습니다.

 

외부저장소에서 사용할 수 있는 파일에는 두가지 타입이 존재하는데요.

Private files와 Public files입니다

A. Private Files

이름에서 알 수 있듯이,해당 앱을 위한 데이터들을 저장할 수 있는 공간입니다.

앱이 지정한 특정 디렉토리에서 Context.getExternalFilesDir()를 이용해서 접근할 수 있는데요.

다만 내부저장소와는 다르게 유저가 SD카드를 빼는등의 행위가 가능하므로,

앱의 중요 데이터는 여기에서 관리하면 않되겠습니다.

 

B. Public Files

이름에서 알 수 있듯이 특별한 제한이나 구분없이 저장 및 삭제할 수 있는 공간입니다.

 

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

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

외부 저장소에서 파일을 읽어오는 방법에 대해서 정리해 보겠습니다.

 

2-1. Permission

외부 저장소에 접근하기 위해서는 동적인 허가를 받아야만 합니다.

읽기와 쓰기에 관한 request storage permissions를 받아야 접근이 가능한 것이지요.

Permission에 관한 글은 아래 링크를 참조해 주세요. 이 글에서는 다루지 않겠습니다.

>> Android 에서 동적 권한 얻기 #골치아픈 # Runtime Permission

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

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

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

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

 

 

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

외부 저장소 중에서도 Public디렉토리에서 파일을 읽어오도록 하겠습니다.

여기서 저희는 MP3파일을 읽어올 수 있는데요.

 

공식문서에서는 사진은 MediaStore.Images에, 영상은 MediaStore.Video에 오디오 파일은 MediaStore.Audio
타입으로 저장하도록 되어있으므로, 이를 참조하면 될 것 같습니다.

저희는 Audio파일이므로 MediaStore.Audio 파일 타입을 이용하면 되겠군요.

 

Audio파일의 칼럼에 다음과 같은 값이 있으므로 이를 이용하면 음악데이터의 정보를 더욱 쉽게 얻을 수 있겠습니다.

 

A. ContentResolver이용하기

위와 같은 데이터들을 쉽게 얻을 수 있도록 도와주는 것이 안드로이드 4대 구성요소 중 하나인 ContentResolver인데요.

ContentProvider로부터 데이터를 가져와주는 역할을 ContentResolver가 합니다.

따라서 이 클래스의 객체를 활용해 데이터에 접근하면 됩니다.

 

이 객체는 아래와 같이 하면 생성할 수 있는데요.

ContentResolver는 query메소드를 이용하면,

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

 

query메소드의 첫번째 인자가 가장 중요한데요. 외부 저장소의 URI를 아래와 같이 표현해 주면 됩니다.

두번째 인자는 데이터의 어느 컬럼을 가져올지 array로 만들어서 알려주면 되구요.

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

 

 

 

이제 이 객체를 어떻게 사용할 것인지가 핵심입니다.

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

이를 활용해서 해당하는 파일의 정보만 얻어오면 되겠습니다.

 

getInt()메소드에 컬럼 index를 넣어주면 값을 int값으로 받을 수 있다는 것과,

아래와 같이, 공식문서에서 IS_MUSIC은 0이 아니면 해당 음악파일이라고 하는 정보를 이용하면,

원하는 MP3파일만 가려서 받을 수 있겠습니다.

이를 활용해서, 아래와 같이 행의 데이터인 cursor의 첫번재 컬럼 IS_MUSIC이 0인가아닌가를 검사해서 맞으면,

해당데이터를 list에 넣어주면 되겠네요.

 

 

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

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

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

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

>> Consume Cursor as RxJava2 Iterable)

 

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

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

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

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

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

 

 

 

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

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

 

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

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

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

 

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

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

 

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

 

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

728x90

댓글