Android10 버전부터 앱에서 앱 외부 파일에 접근하는 방법에 큰 변화가 오게 되었습니다.

ScopedStorage때문인데요. 오늘은 이것에 대해서 정리해 보도록 하겠습니다.

 

1.  과거 Storage 구성

먼저 과거의 Storage의 구성에 대해서 알아보도록 하겠습니다.

이 부분은 과거의 History를 알고 Scoped Storage의 필요성을 이해하기 위한 부분으로,

Scoped Storage에 대해서 바로 알고 싶은 분들은 아래 2로 바로 넘어가셔도 됩니다.

과거에 Storage는 다음과 같이 크게 2가지로 구분해서 사용하고 있었는데요.

 

구분 내용
앱내 Private Directory
(app-specific directory)
외부 앱에서는 접근할 수 없고, 내부앱에서만 접근해서 사용하는 영역
공유할 필요가 없고 Private한 데이터들을 저장해서 앱에서 사용합니다.
Shared Storage(내부 및 외부) Private Directory를 제외한 부분으로 외부에서 접근이 가능하다.
Cache파일들을 저장하거나, 외부에 저장해두고 공유해서 사용할 수도 있는 파일들을 이곳에 저장합니다.

 

앱에서 사용하는 Private한 내부저장소 디렉토리영역과 그렇지 않는 Shared Storage의 내부 및 외부저장소로 구분한 것 입니다.

그럼 각각에 대해 알아보고 왜 ScopedStorage를 적용하였는지 알아보겠습니다.

 

1-1. App을 위한 Private Directory

모든 앱에는 그 앱을 위한 Private Directory가 내부저장소(internal Storage)에 존재합니다.

그것은 아래와 같은 위치에 존재하는데요.

 

android/data/패키지명

 

이 내부저장소에 접근하기 위해서는 다음의 두가지 API를 사용할 수 있습니다.

  • getFilesDir()
  • getCacheDir()

이 private Directory는 다른 앱에서는 접근할 수 없는, 해당 앱만의 private한 영역입니다.

앱이 삭제되면 이 영역의 데이터도 함께 사라지게 됩니다.

앱과 함께 관리가 되고 있던 부분으로, ScopedStorage의 필요성과도 무관해서, 기존 코드사용법과 달라질 필요가 없습니다.

 

1-2. Shared Storage

Shared Storage는 아래와 같이 크게 두가지로 볼 수 있는데요.

Media Collections와 외부 앱 디렉토리입니다.

  • Media Collections - 안드로이드에는 Audio, Image, Video Collections가 있어왔습니다.
  • External App Directory - 외부 SD카드의 디렉토리들

 

External App Directory로 접근하기 위해서는 Context클래스의 다음의 API들을 사용했었습니다.

 

API return 받는 위치
getExternalFilesDirs() /sdcard/Android/data/packagename/files
getExternalCacheDirs() /sdcard/Android/data/packagename/cache

 

광고

 

과거 이 부분들이 앱 삭제시 같이 삭제되지 않으며, 소유자등에 대해서 관리가 되지 않아왔습니다.

그럼 앱을 지울 때마다, 필요하지 않은 파일들이 관리되지 않은 채로 존재하게 되겟지요.

Media Collections에 들어가는 미디어 파일들은 유저소유로 볼 수 있지만, 다른 부분들은 특히 기기에 남아서 여러문제를 야기할 수 있겟지요. 개인정보문제도 포함해서 말 입니다.

Shared Storage도 이 부분의 개선에 무게를 두고 있습니다.

 

2. Scoped Storage

2-1.  ScopedStorage

위에서 알아본 것 처럼 Shared Storage역 영역의 개선을 위해 ScopedStorage가 필요하게 되었는데요.

개별앱들에서 생성하지만 삭제할 때는 같이 삭제가 않되어서, 관리되지 않고 낭비하는 Shared Storage의 영역이 필요이상으로 늘어나는고, 그로인해 발생하는 문제를 해결하고자 하는 것 입니다. 사실 구글에서 제시한 이유는 이런 것 이외에도 Privacy강화등 다른 이유들도 있었지만, 개인적으로는 위에서 말한 부분이 가장 크게 다가왔습니다.

 

Scoped Storage에서 Scoped는 '범위가 지정된' 이라는 정도로 해석할 수 있을 텐데요.

앱마다 액세스하는 영역이 지정되있다고 생각해 볼 수 있습니다.

 

구분 범위
앱내 Private Directory
(app-specific directory)
아무 권한 없이 Access 및 생성과 수정이 가능
외부저장소에 앱이 만든파일 아무 권한 없이 Access 및 생성과 수정이 가능
외부 앱에서 만든 파일 "READ_EXTERNAL_STORAGE" 권한을 얻어야 Access 가능

 

이제 앱내 Private Directory가 아닌 외부에 저장된 파일들은, Scoped Storage를 통해 관리되어서, 앱삭제시 함께 삭제됩니다.

하지만, Media컨텐츠나 문서등 유저소유로 남겨놓아서, 앱이 삭제되어도 유저가 사용할 수 있도록 해야하는 파일들은, MediaStore에 저장하여야 하는데요.

MediaStore에 저장할 수 있는 파일의 타입과 위치는 다음과 같습니다. 

특히 Downlaods에 저장되는 문서나 파일은 기존에는 downloads폴더에 저장되고 있었는데,

Scoped Storage가 시작된 API29(Android10)부터 사용이 가능하게 되었습니다.

 

Type 위치 RELATIVE_PATH
Audio MediaStore.Audio Environment.DIREC TORY_PICTURES
Environment.DIRECTORY_DCIM
Video MediaStore.Video Environment.DIRECTORY_MOVIES
Images MediaStore.Images Environment.DIRECTORY_MUSIC
Media타입이외의
문서나 기타파일
MediaStore.Downloads Environment.DIRECTORY_DOWNLOADS

 

광고

 

이렇게 Scoped Storage를 사용하면 다음과 같은 장점들이 있습니다.

A. 앱삭제시 앱이 만든 파일도 모두 삭제되도록 함

해당 앱이 어떤 파일을 만들었는지 추적 및 관리가 가능해서 앱 삭제시에 해당 파일을 모두 삭제하여,

디스크를 효율적으로 사용할 수 있습니다.

 

B. 외부 저장소의 App Directory도 Private해 집니다.

공유된 공간이 제한되므로, 외부 저장소의 App Directory도 Private해 집니다.

예전에는, Internal App Directory만 Private했었는데요.

이제는 정해진 MediaStore의 파일들만 공유될 수 있으므로,

나머지 공간도 Private하게 사용할 수 있습니다.

이 뜻은 Permission없이 사용할 수 있게 된다는 것 이지요.

다른 앱에서 만든파일에 대해서는 createWriteRequest나 createDeleteRequest같은 API를 이용할 수 있는데요.

이 경우는 유저의 허락이 필요합니다.

 

C. 유저데이터 보안 강화

파일의 metadata에 유저동의없이 접근하기 힘들도록 보안성을 강화하였습니다.

또한 파일앱같이 모든 공간에 접근해야 하는 앱들은 Google의 Whitelist에 들어가야만 가능해졌습니다.

whitelist에 들어가기 위해서는 Google Play에서 폼을 작성해서 신청을하고 Google의 허가를 얻어야만 합니다.

개인개발자다를이 파일앱같이 전체공간에 접근하는 앱을 만들어서 관리하기가 매우 힘들어졌습니다.

유저에겐 장점이고, 개발자에게는 단점이라고 할 수 있겠습니다.

 

 

2-2. 파편화 추가

ScopedStorage가 모든 것을 해결해 줄것 같지만, 사실 그렇지는 않구요. 문제도 생기게 됩니다.

이 API는 jetpack라이브러리도 아니고, Android10(Q)이상에서만 동작하는 API입니다.

추후에 어떤 라이브러리가 등장할지는 모르겠습니다.

하지만, 일단은 개발자 스스로 버전별로 다르게 코드를 적용해야 하므로, 안드로이드 특유의 파편화가 불가피해 보입니다.

Android가 버전업되면서 이런일이 줄어들 것이라고 생각했는데요. 보안등의 이유로 오히려 추가되고 말았네요.

 

게다가, Android10(API29)와 Android11(30)에서의 Scoped Storage의 Spec이 조금 변경되었습니다.

Android11에서는 다음과 같은 것들이 추가되었는데요.

OS버전이 올라가면서 파편화는 더욱 심해지고 있습니다.

  • File Path API추가 - MediaStore API 외외에 File Path API 사용한 접근 가능
  • Bulk media modifiaciton API - 한꺼번에 많은 미디어 파일
  • All Files Access
  • AppStorage

 

좋아지는 것은 맞는데, API21이상기기도 여전히 많은 상태에서, 현실적으로 위와같은 기능을 이용하기 위해서,

분기를 탄 코드를 추가로 넣는 것은 효율적으로 보이지는 않는데요.

한편으로는 이렇게까지 해서도 공용저장공간의 관리에 대한 강한 의지가 보여지기도 합니다.

 

이러한 권한들이 필요하다는 것을 알아놓고, TargetSDK가 변경되어질 때, 해당부분을 적용시키는데 초점을 맞추어야 하겟습니다.

PlayStore에서 요구하는 최소 TargetSDK버전은 계속 올라가고 있기 때문에, 관련내용에 대해서는 숙지해둘 필요가 있습니다.

 

3. Scoped Storage API

3-1.  MediaStore API

ScopedStorage를 이용해 공유할 컨텐츠 파일을 저장하고 읽는데 있어서 메인이 되는 API는 Media Collections API입니다.

모든 앱에서 Media Collections에 File을 저장할 때는 어떤 Permission도 필요하지 않습니다.

자신이 만든 파일에도 Permission없이 접근하도록 API가 설계되어 있는데요.

그래서, 자신이 만든파일을 MediaStore에 저장하고 수정하고 삭제하는 것이 자유롭습니다.

대신, 다른 앱에서 만든 파일에는 아래의 permission을 얻어야 사용할 수 있는데,

Runtime Permission이기 때문에 조금은 까다롭습니다.

 

 READ_EXTERNAL_STORAGE

 

Media Collections API의 MediaStore에는 다음과 같이 media file들만 넣을 수 있는데요.

기존의 Shared Directory처럼 앱을 지워도 여기에 저장한 파일들은 삭제되지 않습니다.

 

 

한가지 알아둘 것은, MediaStore에 접근해서 파일을 쓸 때,  Image, Audio, Video 각각에 맞는 위치에 저장하도록 해야합니다.

예를 들어, 오디오는 MediaStore.Audio에만 저장이 가능하도록 API가 설계되어 있어서 잘못된 디렉토리에 저장하지 않도록 해야 하는것이지요.

 

MediaStore에 저장한다는 것은, 이 파일이 앱이 아니라 유저에 포함되어진다고 명시하는 것과 같습니다.

그렇기에 앱을 삭제해도 해당 파일은 지워지지 않는 것 이지요.

 

3-2. Download Collection

위에서 언급한 Audio, Video, Images타입의 Media파일이 아닌 파일은 어떻게 공유할 수 있을까요?

이러한 파일들은 새롭게 추가된 Download Collections에 저장할 수 있습니다.

대표적인 것중하나가 PDF파일이 되겠네요.

 

정리하면 Media파일은 Media Collections에,

Media파일이 아닌 파일은 Download Collections에 저장된다음, 읽고 편집 혹은 삭제되야 합니다.

 

Media파일과 마찬가지로 자신이 만든 파일에는 권한없이 접근할 수 있는데요.

다른 앱이 만든 파일에 접근하기 위해서는 아래와 같은 Intent에 의해서 실행되는 system picker를 사용해주어야 합니다.

  • ACTION_OPEN_DOCUMENT
  • ACTION_CREATE_DOCUMENT

 

3-3. 다른앱이 만든 파일에 접근하는 방법

Scoped Storage개념이 생기면서 주의할 점은,

자신(앱)이 만든 파일인지 아닌지에 따라서 접근권한이 달라진다는 것 인데요.

위에서 언급한대로, 이제 해당앱에서 만든 파일에는 Media Collections든 Download Collections이던 권한이 필요하지 않습니다.

 

그러나 자신이 만들지 않은 파일에 접근할 때는 Permission을 요청해야만 하는데요.

심지어 앱을 삭제한 후 같은 앱을 다시 설치해서, 기존에 삭제한 앱이 만든 파일에 접근할 때도 Permission은 필요하게 됩니다.

 

그럼, 다른앱이 만든 파일에 접근하는 방법에 대해서 알아보도록 하겠습니다.

 

A. Media Files 접근

다른앱이 만든 파일에 접근하기 위해서는 아래 permission이 필요합니다.

 

READ_EXTERNAL_STORAGE

 

이를 통해서 유저의 Permission을 얻으면 접근이 가능하게 됩니다.

 

 

B. 다른앱이 만든 Media파일의 편집 및 삭제

다른앱이 만든 Media파일을 편집하거나 삭제하기 위해서는 MediaStore API를 이용해 주어야 하는데요.

이 때, READ_EXTERNAL_STORAGE권한을 가지고 있어야 하구요.

해당 API를 이용하면, 편집이나 삭제시 유저의 동의를 얻는 dialog가 보여지게 됩니다.

유저가 동의하면, callback을 받아서 그에 따라 코드를 실행하도록 하면 됩니다.

 

C. Non Media파일 접근

다른 앱에서 만든 Non Media파일에 접근하기 위해서는 READ_EXTERNAL_STORAGE Permission이 위의 Media 파일때와 같이 필요하구요. 파일은 아래의 장소 위치하고 있어야 합니다.

(Non Media 파일을 공유하기 위해 저장하고자 한다면 아래 위치에 저장해야만 하겠지요.)

 

MediaStore.Downloads

 

위의 장소에 위치하고 있는 Non Media파일에 접근하기 위해서는,

Storage Access Framework API를 이용해서,

System Picker를 론칭해주어야 합니다.

 

3-4.  ACCESS_MEDIA_LOCATION permission

이제는 다른앱에 의해서 생성된 Media File에 접근하면 위치정보와 같이 Private한metadata에 접근할 수 없게 됩니다.

이러한 metadata에 접근하기 위해서는 ACCESS_MEDIA_LOCATION Permission을 얻어야 합니다.

 

만약 metadata가 필요하다면,  위에서 언급된 READ_EXTERNAL_STORAGE와 함께 이 Permission이 필요한데요.

그리고 MediaStore의 LATITUDE와 LONGITUDE는 deprecated되었으므로, ExifInterface를 대신 이용해 주어야 합니다.

다행히도 ExifInterface는 jetpack라이브러리에 포함되어 있으므로, 호환성걱정을 할 필요는 없습니다.

 

val latLong = resolver.openInputStrea(item).use {
    ExifInterface(stream).run {
    	//만약없으면, (30.0, 30.0)을 좌표로 사용
        latLong ?: doubleArrayOf(30.0, 30.0)
    }

 

3-5. 파일의 byte크기를 알고 싶을 때

만약, metadata의 도움없이 파일의 byte크기를 알고싶다면,

MediaStore.setRequireOriginal API를 이용할 수 있습니다.

 

3-6 다중 파일에 대한 작업

많은 파일에 한꺼번에 작업을 할 때는 아래 API를 이용하면 유용하다고 합니다.

 

ContentProvider.applyBatch()

 

4. Scoped Storage 구현

4-1.  남은 공간 확인하기

남은 공간을 확인할 때는 아래 Intent를 사용해 줍니다.

startActivityForResult를 사용해서 launch해 주어야 하구요.

RESULT_OK가 나오면 공간이 있다는 뜻 입니다.

ACTION_MANAGE_STORAGE

 

이에대한 공식문서 설명도 잠깐 보고 가도록 하겠습니다.

 

 

4-2. Insert

다음과 같이, ContentValue를 생성해서, MediaStore에 Image로 저장할 수 있습니다.

ContetnResolver객체의 openFileDescriptor()함수를 이용해서,

query() 하거나, read, write를 할 수 있는데요.

 

DISPLAY_NAME 과 MIME_TYPE 은 새롭지 않은데,

IS_PENDING은 무엇일까요?

PENDING으로 저장하게 되면, 다른 앱에서는 보이지 않게 되구요.

PENDING이 끝났다고하면 그 때부터 다른 앱에서 보이게 되므로,

중간에 어떤 작업들이 필요하게 되면, 그때까지는 PENDING시켜놓을 수 있는 것 입니다.

예를 들어서, 이미지를 다운로드 한다면, 완료가 되기 전까지는 PENDING으로 해 놓고, 완료가 된다면,

"0"로 변경해서, 다른 앱에서도 보이도록 해줄 수 있겠지요.

 

val valuse = ContentValues().apply {
    put(MediaStore.Images.Media.DISPLAY_NAME, "test.JPG")
    put(MediaStore.Images.Media.MIME_TYPE, "image/jpg")
    put(MediaStore.Images.Media.IS_PENDING, 1)    
}

val collection = MediaStore.Images.Media
                 .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val itemUri = getContentResolver().insert(collection, values)

 

이제 PENDING상태인 image에 data를 작성해서 update 해 보도록 하겠습니다.

아래에서는 openFileDescriptor를 사용하고 있는데요.

 

resolver.openFileDescriptor(itemUri, "w", null).use { pfd ->

}

values.clear()
values.put(MediaStore.Images.Media.IS_PENDING, 0)
resolver.update(item, values, null, null)

 

 

파일저장 위치를 설정할 때는 MediaStore.MediaColumns.RELATIVE_PATH를 수정해 주면 됩니다.

이것을 수정해서 resolver.update()를 하면 해당파일의 위치도 수정할 수 있습니다.

그런데 사실 아래와 같은 작업은 하지 않아도 되는 것이, MIME_TYPE을 설정한 시점에 이미, OS에서 Picures 디렉토리로 저장위치를 잡기 때문입니다.

 

 

음악파일같이 폴더별 정리가 필요한 경우는 아래와 같이 활용해 볼 수 있습니다.

그럼, 최상위디렉토리인 Music디렉토리아래, test디렉토리에 아래 파일이 저장되게 되겠지요.

 

val valuse = ContentValues().apply {
    put(MediaStore.Audio.Media.RELATIVE_PATH, "Music/test/testSongs")
    put(MediaStore.Images.Media.DISPLAY_NAME, "testSong.mp3")
}

// return타입은 Set<String>
val volumeNames = MediaStore.Images.Media
                 .getExternalVolumeNames(context)
val collection = MediaStore.Audio.Media.getContentUri(volumeNames 중 하나의 이름)
val itemUri = getContentResolver().insert(collection, values)

 

참고로 getExternalVolumeNames() API를 이요하면 사용할 수 있는 VolumeName을 얻어올 수 있는데요.

SECONDARY인 SD CARD를 얻어올 수도 있겠지만,

보통은 아래와 같이, MediaStore.VOLUME_EXTERNAL_PRIMARY 로 통해서,

PRIMARY 디스크를 얻어오면 되겠지요.

 

val collection = MediaStore.Images.Media
                 .getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

 

다음과 같이 thumbNail 아이템을 얻어올 수도 있습니다.

resolver객체의 loadThumbnail 함수를 이용하기만 하면 되네요.

val thumb = resolver.loadThumbnail(item, Size(320, 240), null)

 

4-3. API19이하에서 사용

ScopedStorage를 사용할 때 주의할 점은, API19이하에서는 openFileDescriptor를 사용할 수 없다는 점 인데요.

그래서 아래와 같이, openOutputStream() 을 사용해주어야 합니다.

 

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                val mResolver = requireContext().contentResolver
                val mValues = ContentValues().apply {
                    put(MediaStore.MediaColumns.DISPLAY_NAME, mFileName)
                    put(MediaStore.MediaColumns.MIME_TYPE, "image/png")
                    put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_PICTURES)
                }
                val mUri = mResolver.insert(
                        MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mValues)
                if (mUri != null) {
                    val mOs = mResolver.openOutputStream(mUri)
                    mBitmap.compress(Bitmap.CompressFormat.PNG, 100, mOs)
                    if (mOs != null) {
                        mOs.flush()
                        mOs.close()
                    } 
                    mOutputUri = mUri
                } else { // throw Exception }
} else { //Q이하 버전에서는 예전방식으로 저장 }

 

4-4. 다른 앱이 만든 파일에 접근

해당하는 item을 지우려고 할 때 만약 다른 앱에서 만든파일이라면,

아래와 같이 RecoverableSecurityException을 catch해서 반응할 수 있습니다.

 

try {
  resolver.delete(item, null, null)
catch(e: RecoverableSecurityException) {
  AlertDialog.Builder(requireActivity())
    .setMessage(e.userMessage)
    .setPositiveButton(e.userAction.title) { dialog, which ->
        try { e.userAction.actionIntent.send() } 
        catch (ignored: PendingIntent.CanceledException) {
        
        }
    .setNegativeButton("취소", null)
    .show()
}    

 

5. 기존과 같은 내부저장소의 앱을 위한 공간

다행히도 내부저장소에 사용하는 API들은 크게 바뀌지 않고 사용할 수 있습니다.

 

5-1. 내부저장소 filesDir

Context클래스에는 getFilesDir()라는 함수를 가지고 있습니다.

이 함수는 앱에서 저장해서 쭉 private하게 사용할 파일의 저장위치를 return해 주는데요.

API를 보도록 하겠습니다.

 

 

Kotin에서는 아래와 같이 사용할 수 있겠지요.

 

 

openFileOutput() 함수를 이용해서 FileOutputStream을 받아서 내부의 private한 위치에 file을 생성할 수도 있습니다.

참고로 kotlin에서 use를 사용하면, close를 Exception이 나던 않나던 실행해 줍니다.

 

 

만약 이렇게 저장된 위치에 다른앱에서 Access해야한다면,

FLAG_GRANT_READ_URI_PERMISSION과 함께 FileProvider를 이용해 주어야 합니다.

 

 

이곳에 파일이 아닌 디렉토리를 생성하거나 열려고 하면 어떻게 하면 될까요?

Context클래스의 getDir()를 이용하면 디렉토리를 생성하거나 접근할 수 있습니다.

getDir()의 공식문서에는 아래와 같이 설명이 되어 있네요.

 

 

아래와 같이 사용해주면 되구요.

다만, 이렇게 디렉토리까지 만들어서 사용할 일은 그리 많지는 않을 것 같습니다.

 

 

filesDir에 저장된 파일들 보는 방법은 다음과 같습니다.

Context클래스에서는 fileList()라는 함수를 제공해서 저장된 파일들을 볼 수 있도록 해 줍니다.

 

 

5-2. Cache파일의 생성 cacheDir

A. 내부 캐쉬 저장소

안드로이드에는 내부저장소에 특정한 Cache파일의 저장장소가 있습니다.

위에서는 fileDir를 사용하였는데요. 여기서는 cacheDir를 이용해서 접근합니다.

 

 

이 캐쉬파일은 앱삭제시 같이 삭제되구요.

휴대폰내의 공간이 부족할 경우 삭제될수도 있습니다.

직접 살제할 경우, delete()함수를 사용할수도 있구요.

fileName을 이용해서 deleteFile()함수를 이용할수도 있습니다.

 

 

B. 외부저장소

외부에도 Cache파일의 저장소가 있습니다.

외부 Cache파일은 getExternalCacheDir()함수를 이용해서 접근할 수 있습니다.

삭제하는 방법은 내부저장소와 같이 delete()함수를 사용해주면 됩니다.

 

 

6. 하위호환 문제

6-1. WRITE_EXTERNAL_STORAGE

새로운 Scoped Storage는 Android10이상 에서는 잘 동작하겠지만,

Android9이하에서는 문제가 될 수 있습니다.

그래서 기존과 같이 WRITE_EXTERNAL_STORAGE permission 을 얻어주어야 합니다.

 

 

AndroidX와 같은 여러버전의 호환성문제를 해결하는 라이브러리 API를 사용하는 것이 아니므로,

개발하시는 분들은 Andoroid10이하의 하위시스템 버전에서 잘 동작하는지 꼭 테스트할 필요가 있습니다.

 

 

 

8. 팁 및 주의할 점

8-1.  Android10에서의 ScopedStorage 적용 유예

Android10을 타게팅하는 경우는 manifest에서 flag를 주어서 적용하지 않을 수 있었는데요.

manifest의 application태그에서 아래와 같이 선언해 주면 됩니다.

다만, 이제는 앱을 PlayStore에 올릴 때, 왜 업데이트하지 않는지 설명을 해야 한다고 합니다.

 

 

타겟SDK가 Andoid 11부터는 이 Flag는 유효하지 않고, 무조건 ScopedStorage가 적용되어야 합니다.

결국에는 강제 적용사항이므로 Scoped Storage를 잘 이해하고 적용시키는 것이 중요합니다.

 

8-2. Best Practice 공식문서

Scoped Storage가 나오면서 코드 파편화도 심해지고 복잡해 보이는데요.

구글에서 Case별로 어떻게 해야하는지 Best Practice를 공식문서에 작성해 주었습니다.

자신의 케이스와 비교해보고, 참조하는 것도 좋을 것 같습니다.

 

>> Android storage use cases and best practices

 

8-3. FileUriExposedException

이 글의 주제와는 관련성은 떨어지지만, 앱의 스크린을 캡처해서 앱내에 파일로 저장한 다음 이미지 파일을 공유할 경우가 있는데요.

이 때, FileProvider API를 사용하지 않으면 FileUriExposedException을 냅니다.

구체적인 내용은 아래 글을 참조해주세요.

 

>>이미지 등의 파일 공유시에 FileProvider를 사용해 주세요

 

9. 정리

다행히 저는 Scoped Storage의 영향을 받는 부분이 적기는 하였는데요.

외부앱에서 만든 파일을 읽거나 수정해야 하는 경우, 코드를 읽거나 수정해야해서 소규모 개인개발자들에게는 매우 피곤한 부분이 아닐 수 없습니다.

앱개발시에 데이터를 저장하거나 액세스하는 것은 피할수 없는 부분이므로, 잘 알아두고 미리미리 테스트를 진행해서 기존앱에 무리가 없도록 해야할 것 같습니다.

 

 

728x90

+ Recent posts