사용자에게 파일의 더 많은 권한을 제공하고 파일이 복잡해지는 것을 제한하기 위해, Android 10(API 레벨 29) 이상을 타겟팅하는 앱은 외부 저장소로 범위가 지정된 액세스 또는 범위 지정 저장소가 기본적으로 부여됩니다. 이러한 앱은 저장소의 앱별 디렉터리(Context.getExternalFilesDir()
을 사용해 액세스함) 및 특정 유형의 미디어를 볼 수 있습니다. 앱에서 앱별 디렉터리나 MediaStore
에 있지 않은 파일에 액세스해야 하는 경우가 아니면 범위 지정 저장소를 사용하는 것이 좋습니다.
다음 표에는 범위 지정 저장소가 파일 액세스에 미치는 영향이 요약되어 있습니다.
파일 위치 | 권한 필요 | 액세스 방법(*) | 앱 제거 시 파일 삭제 여부 |
---|---|---|---|
앱별 디렉터리 | 없음 |
getExternalFilesDir() |
예 |
미디어 컬렉션 (사진, 동영상, 오디오) |
READ_EXTERNAL_STORAGE 다른 앱의 파일에 액세스하는 경우에만 |
MediaStore |
아니요 |
다운로드 (문서 및 eBook) |
없음 | 저장소 액세스 프레임워크 (시스템의 파일 선택기 로드) |
아니요 |
*저장소 액세스 프레임워크를 사용하여 권한을 요청하지 않고 앞의 표에 명시된 각 위치에 액세스할 수 있습니다.
이 페이지에서는 범위 지정 저장소를 사용할 때 앱이 액세스할 수 있는 파일을 설명하고 앱에서 외부 저장소 기기에 저장된 파일을 계속 공유, 액세스, 수정할 수 있도록 앱을 업데이트하는 방법을 설명합니다.
파일 액세스에 필요한 권한
범위 지정 저장소가 있는 앱은 앱별 디렉터리 내부와 외부 모두에서 앱이 생성하는 파일을 대상으로 한 읽기/쓰기 액세스 권한을 항상 가집니다. 이에 따라, 자체에서 생성한 파일만 저장하고 액세스하는 앱의 경우 READ_EXTERNAL_STORAGE
권한이나 WRITE_EXTERNAL_STORAGE
권한을 요청할 필요가 없습니다.
그러나 다른 앱에서 생성된 파일에 액세스하려면 다음 조건이 모두 충족되어야 합니다.
- 앱에
READ_EXTERNAL_STORAGE
권한이 부여되었습니다. 파일이 다음의 잘 정의된 미디어 컬렉션 중 하나에 있습니다.
- 사진:
MediaStore.Images
에 저장 - 동영상:
MediaStore.Video
에 저장 - 오디오 파일:
MediaStore.Audio
에 저장
- 사진:
'downloads' 디렉터리에 있는 파일을 비롯하여 다른 앱에서 만든 다른 파일에 액세스하려면 앱에서 사용자가 특정 파일을 선택하도록 허용하는 저장소 액세스 프레임워크를 사용해야 합니다.
READ_EXTERNAL_STORAGE
권한이 있어도 외부 저장소 기기의 원본 파일 시스템 뷰에 액세스하는 앱은 앱별 디렉터리에만 액세스할 수 있습니다. 앱이 원본 파일 시스템 뷰를 사용하여 이 디렉터리 외부의 파일을 열려고 하면 다음 오류가 발생합니다.
- 관리형 코드에서는
FileNotFoundException
이 발생합니다. - 네이티브 코드에서는
EPERM
커널 오류가 발생합니다.
미디어 데이터 제한사항
범위 지정 저장소에는 다음과 같은 미디어 관련 데이터 제한이 적용됩니다.
- 앱에
ACCESS_MEDIA_LOCATION
권한이 부여되지 않으면 이미지 파일 내 Exif 메타데이터가 수정됩니다. 사진의 위치 정보에 액세스하는 방법에 관한 섹션에서 자세히 알아보세요. MediaStore.Files
테이블은 자체가 필터링되어 사진, 동영상, 오디오 파일만 표시됩니다. 예를 들어 PDF 파일은 표시되지 않습니다.- 미디어 파일 액세스는 자바 기반 코드나 Kotlin 기반 코드에서
MediaStore
를 사용하여 시작해야 합니다. 네이티브 코드에서 미디어 파일에 액세스하는 방법에 관한 안내를 참고하세요.
미디어 파일 작업을 하는 방법을 설명하는 가이드에서는 MediaStore
내에서 개별 문서 및 문서 트리에 액세스하기 위한 권장사항을 제공합니다. 앱이 범위 지정 저장소를 사용하는 경우 미디어에 액세스하는 이 방법이 필요합니다.
사진의 위치 정보
일부 사진의 경우 사용자가 사진이 촬영된 장소를 볼 수 있는 위치 정보가 Exif 메타데이터에 들어 있습니다. 그러나 이 위치 정보는 민감한 정보이므로, 앱이 범위 지정 저장소를 사용하는 경우 Android 10은 기본적으로 앱에서 발생한 정보를 숨깁니다.
앱이 사진 위치 정보에 액세스해야 하는 경우 다음 단계를 완료하세요.
- 앱의 manifest에서
ACCESS_MEDIA_LOCATION
권한을 요청합니다. 다음 코드 스니펫과 같이
MediaStore
개체에서setRequireOriginal()
을 호출하여 사진의 URI를 전달합니다.Kotlin
// Get location data from the ExifInterface class. val photoUri = MediaStore.setRequireOriginal(photoUri) contentResolver.openInputStream(photoUri).use { stream -> ExifInterface(stream).run { // If lat/long is null, fall back to the coordinates (0, 0). val latLong = ?: doubleArrayOf(0.0, 0.0) } }
자바
Uri photoUri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI, cursor.getString(idColumnIndex)); final double[] latLong; // Get location data from the ExifInterface class. photoUri = MediaStore.setRequireOriginal(photoUri); InputStream stream = getContentResolver().openInputStream(photoUri); if (stream != null) { ExifInterface exifInterface = new ExifInterface(stream); double[] returnedLatLong = exifInterface.getLatLong(); // If lat/long is null, fall back to the coordinates (0, 0). latLong = returnedLatLong != null ? returnedLatLong : new double[2]; // Don't reuse the stream associated with the instance of "ExifInterface". stream.close(); } else { // Failed to load the stream, so return the coordinates (0, 0). latLong = new double[2]; }
범위 지정 저장소 선택 해제하기
앱이 범위 지정 저장소와 완전히 호환되기 전에는 앱의 타겟 SDK 레벨이나 requestLegacyExternalStorage
manifest 속성에 따라 범위 지정 저장소를 일시적으로 선택 해제할 수 있습니다.
- Android 9(API 레벨 28) 이하를 타겟팅합니다.
Android 10 이상을 타겟팅하는 경우 앱의 manifest 파일에서
requestLegacyExternalStorage
의 값을true
로 설정합니다.<manifest ... > <!-- This attribute is "false" by default on apps targeting Android 10 or higher. --> <application android:requestLegacyExternalStorage="true" ... > ... </application> </manifest>
Android 9 이하를 타겟팅하는 앱이 범위 지정 저장소를 사용할 때 어떻게 동작하는지 테스트하려면 requestLegacyExternalStorage
의 값을 false
로 설정하여 동작을 선택할 수 있습니다.