Android 11+를 위한 Android 저장소 권한 적응 가이드

매우 간단한 요구 사항을 구현하려고 했을 때 이미지를 다운로드하여 로컬 저장소에 저장합니다. — 처음에는 모든 것이 괜찮아 보였습니다.

  • Honor(Android 10) – 작동함
  • Redmi(Android 11) – 작동함
  • 샤오미(안드로이드 13) - 작동함
  • 삼성(안드로이드 13) – 완전히 실패했습니다. 저장소 권한 대화 상자가 전혀 표시되지 않았습니다.

동일한 코드, 동일한 기능이지만 Android 13의 한 기기는 권한 요청 메시지를 표시하지 않았습니다. 이렇게 해서 이 작은 "이미지 다운로드" 작업이 심층 분석으로 바뀌었습니다. 범위가 지정된 저장소 그리고 외부 저장소 관리.

이 게시물은 내가 저장 권한을 어떻게 조정했는지 요약합니다. 안드로이드 11 이상, 그리고 버전별로 다른 동작을 어떻게 처리하는지 알려드리겠습니다.


1. 버그: 저장 권한 대화 상자가 표시되지 않음(Samsung Android 13)

요구 사항은 간단합니다.

이미지를 다운로드해서 기기에 저장하면 갤러리에 표시됩니다.

세 개의 테스트 장치에서:

  • Honor – 안드로이드 10 → OK
  • 레드미 – 안드로이드 11 → 확인
  • 샤오미 – 안드로이드 13 → 확인

하지만 안드로이드 13을 실행하는 삼성 기기, 시스템 저장 권한 대화 상자가 표시되지 않았습니다., 내가 어떻게 요청하든 상관없어요.

처음에는 이것이 단지 OEM의 또 다른 특성이라고 생각했지만, Android 버전 전체에서 저장소 권한 변경 사항을 확인한 후, SDK 33을 타겟으로 할 때 Android 13에서 사실상 더 이상 사용되지 않는 동작에 의존하고 있다는 것을 깨달았습니다.


2. 근본 원인: WRITE/READ_EXTERNAL_STORAGE는 Android 13(SDK 33)에서 더 이상 사용되지 않습니다.

이전 Android 버전에서는 매니페스트에서 다음 두 가지 권한을 간단히 선언할 수 있었습니다.

  • 외부 저장소 읽기
  • 외부 저장소 쓰기

그리고 필요할 때 런타임에 요청합니다.

Android 13(SDK 33)에서 targetSdkVersion = 33, 그 접근 방식은 깨지기 시작합니다.

  • 외부 저장소 쓰기 ~이다 더 이상 사용되지 않고 사실상 쓸모가 없음 최신 안드로이드 버전에서
  • 추가하면 최대 SDK 버전=32 이러한 권한은 Android 11/12에서도 여전히 작동합니다.
    하지만 그들은 무시하다 Android 13에서 33을 타겟팅할 때
  • 동시에 Play Store에서는 새 앱이 최소 SDK 33을 타겟으로 해야 합니다.

따라서 Android 11 이상에서는 다음 사항에 적응해야 합니다.

  • 범위가 지정된 저장소
  • 그리고 어떤 경우에는 특별 허가가 필요합니다. 외부 저장소 관리

외부 저장소 관리 앱에 모든 공유 저장소 콘텐츠(비미디어 파일 포함)에 대한 광범위한 액세스 권한을 부여합니다. ~ 아니다 다른 앱의 비공개 디렉토리에 대한 접근을 허용하지만, Google Play에서는 여전히 매우 민감한 권한으로 간주됩니다.

다양한 Android 버전을 지원하기 위해 권한 처리를 다음과 같이 분할했습니다.

  • Android 11 이전(API < 30) – 이전 스타일의 외부 저장소 권한
  • 안드로이드 11 이상 – 범위 지정 저장소 + 특수 처리 외부 저장소 관리 엄격히 필요한 경우

3. 단계별 적응

3.1 매니페스트에 MANAGE_EXTERNAL_STORAGE 선언

에서 AndroidManifest.xml:

<uses-permission
    android:name="android.permission.MANAGE_EXTERNAL_STORAGE"
    tools:ignore="ScopedStorage" />

⚠️ 참고: 왜냐하면 외부 저장소 관리 민감한 권한이라 Google Play에서는 제한되어 있습니다. 이미지만 저장하려는 경우의 대안은 나중에 설명하겠습니다.


3.2 권한이 부여되었는지 확인

나는 사용했다 이지퍼미션 권한 확인을 간소화합니다.

개인 fun checkPer(활동: PreViewActivity): Boolean { Build.VERSION.SDK_INT >= 30인 경우를 반환합니다. EasyPermissions.hasPermissions(활동, android.Manifest.permission.MANAGE_EXTERNAL_STORAGE) } else { EasyPermissions.hasPermissions(활동, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) } }
  • ~에 안드로이드 11 이상(API >= 30): 확인해요 외부 저장소 관리
  • ~에 안드로이드 10 이하: 아직도 확인 중이에요 외부 저장소 쓰기

이 분할은 중요합니다. 외부 저장소 쓰기 더 이상 최신 버전에서처럼 동작하지 않습니다.


3.3 권한이 없을 때 권한 요청

권한이 부여되지 않으면 시스템 버전에 따라 다르게 요청합니다.

private fun requestStoragePermission(activity: PreViewActivity, curImg: Int) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { // Android 11+ – 시스템 "모든 파일 액세스" 설정 페이지로 리디렉션 val intent = Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION) intent.data = Uri.parse("package:" + activity.packageName) activity.startActivityForResult(intent, 200) } else { // Android 10 이하 – 일반 런타임 권한 val perm = android.Manifest.permission.WRITE_EXTERNAL_STORAGE PaperThreeVariable.isToRequestPer = true EasyPermissions.requestPermissions( PermissionRequest.Builder( activity, 200, perm ).build() ) } }
  • ~에 안드로이드 11 이상: 일반 런타임 대화 상자를 그냥 "팝업"할 수 없습니다. 외부 저장소 관리
    사용자를 시스템 설정 페이지로 안내하여 수동으로 "모든 파일 액세스" 권한을 부여해야 합니다.
  • ~에 안드로이드 10 이하: 클래식 런타임 권한 대화 상자가 계속 작동합니다.

3.4 권한 콜백 처리

EasyPermissions는 Activity의 콜백과 자체 로직을 연결하는 데 도움이 됩니다.

onRequestPermissionsResult( requestCode: Int, permissions: Array)를 재정의합니다. , grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this) } override fun onPermissionsGranted(requestCode: Int, perms: MutableList ) { AppInitUtils().saveFreshAppImageToGallery(this, curImg) PaperThreeVariable.isToRequestPer = false } onPermissionsDenied(requestCode: Int, perms: MutableList)를 재정의합니다. ) { PaperThreeVariable.isToRequestPer = false if (EasyPermissions.somePermissionPermanentlyDenied(this, perms)) { AppSettingsDialog.Builder(this) .setRationale("이 함수를 활성화하려면 저장 권한이 필요합니다.") .setNegativeButton("아니요") .setPositiveButton("예") .build() .show() } }

내가 EasyPermissions를 사용하는 이유:

  • 사용자는 할 수 있습니다 영구적으로 거부하다 반복되는 요청이 자동으로 실패하게 만드는 권한
  • EasyPermissions를 사용하면 다음 작업을 더 쉽게 수행할 수 있습니다.
    • "영구 거부" 상태 감지
    • 사용자를 안내하는 대화 상자를 표시합니다. 시스템 설정 → 앱 권한 저장소 액세스를 수동으로 활성화하려면

허가가 내려지면 전화합니다.

AppInitUtils().saveFreshAppImageToGallery(이것, curImg)

실제로 이미지를 저장하고 갤러리를 새로 고칩니다.

이러한 적응 이후, 삼성 안드로이드 13 기기는 마침내 다른 기기와 동일하게 동작했습니다.

참고: 제 Xiaomi 기기는 Android 13으로 보고되었지만 Android Studio의 "연결된 이전 기기"에서는 Android 12로 인식했습니다. 이것이 어떤 경우에는 여전히 작동하는 이유를 설명할 수 있지만, 버전 기반 권한 처리가 중요한 이유는 바로 이 때문입니다.


4. MANAGE_EXTERNAL_STORAGE 및 Google Play 제한 사항에 관하여

외부 저장소 관리 강력하다:

읽기/쓰기 액세스 권한을 부여합니다. 모든 공유 스토리지 장치에서.

이로 인해 Google Play에서는 이를 다음과 같이 처리합니다. 매우 민감한 권한:

  • 주로 다음을 위해 의도되었습니다. 파일 관리자 / 백업 / 바이러스 백신 앱 입력
  • 사용하려면 정당한 사유를 제출해야 합니다.
  • 앱이 일반적인 소비자용 앱(예: 이미지 저장, 간단한 다운로드)인 경우 요청은 다음과 같을 가능성이 높습니다. 거부됨

따라서 귀하의 유일한 요구 사항이 다음과 같다면:

“"갤러리에 이미지를 저장하고 사용자에게 표시합니다."”

그럼 당신은해야합니다 강력히 피하는 것을 고려하세요 외부 저장소 관리 그리고 대신:

  • 사용 미디어스토어 시스템 미디어 라이브러리에 이미지를 삽입하려면
  • 또는 전체 파일 액세스가 필요하지 않고 이미지를 저장할 수 있는 API를 사용하세요.

다음과 같은 여러 가지 패턴이 있습니다.

  • 사진을 Pictures/DCIM 디렉토리에 저장합니다.
  • 미디어 스캐너에 알리거나 MediaStore를 사용하여 갤러리에서 이를 선택할 수 있도록 합니다.
  • 이 모든 것을 하세요 MANAGE_EXTERNAL_STORAGE를 요청하지 않고

내부 또는 Play 스토어 외 배포(예: 기업 내부 앱 스토어)의 경우 기술적으로 계속 사용할 수 있습니다. 환경.getExternalStorageDirectory(), 하지만 2025년에 이런 방식으로 새로운 앱을 디자인하는 것은 권장하지 않습니다.


5. 버전별 요약 (Android 9 → 13)

모든 내용을 한곳에 정리하기 위해, 외부 저장소와 권한이 여러 버전에 걸쳐 어떻게 동작하는지에 대한 간략한 요약을 소개합니다.

Android 9 이하(API 28 이하)

  • 권한:
    • 외부 저장소 읽기
    • 외부 저장소 쓰기
  • 행동:
    • 앱은 자유롭게 접근 가능 /SD카드 및 하위 디렉토리
    • 앱이 제거된 후에도 앱에서 생성된 파일은 기기에 남아 있습니다.
  • 일반적인 접근 방식:
    • 외부 저장소 경로에서 직접 읽기/쓰기

Android 10(API 29) - 범위 지정 저장소 도입

  • 권한:
    • 외부 저장소 읽기 아직도 작동하다
    • 외부 저장소 쓰기 여전히 존재하지만 효과적인 범위가 줄었습니다.
  • 행동:
    • 범위가 지정된 저장소 소개됩니다:
      • 앱은 해당 앱별 디렉토리로 제한됩니다.
        Android/데이터/귀하의 패키지 이름/
      • 다른 앱의 파일에 대한 직접 액세스가 제한됩니다.
    • 미디어 파일(이미지, 비디오, 오디오)은 다음을 통해 액세스해야 합니다. 미디어스토어
    • requestLegacyExternalStorage=true 일시적으로 오래된 행동을 유지할 수 있습니다
      (하지만 이 플래그는 Android 11부터 무시됩니다)
  • 권장되는 접근 방식:
    • 이미지/비디오/오디오의 경우: 사용 미디어스토어
    • 개인 파일의 경우: 사용 getExternalFilesDir() 또는 getDataDir()

Android 11(API 30) – 범위 지정 저장소 적용

  • 권한:
    • 외부 저장소 읽기 작동하지만 MediaStore에서 관리하는 미디어에만 해당
    • 외부 저장소 쓰기 효과적으로 쓸모없는 일반 외부 저장소용
    • 외부 저장소 관리 특별한 "모든 파일 액세스" 사용 사례에 도입됨
  • 행동:
    • requestLegacyExternalStorage=true 더 이상 작동하지 않습니다. 범위 지정 저장소는 항상 켜져 있음
    • 에 대한 접근 /SD카드/ 루트가 막혔습니다
    • 앱은 다음만 가능합니다.
      • 자신의 개인 디렉토리에 접근하세요
      • 공유 미디어에 액세스하려면 다음을 사용하세요. 미디어스토어
  • 권장되는 접근 방식:
    • 일반적인 앱의 경우:
      • MediaStore를 사용하거나 SAF (문서 열기 작업, 문서 생성 작업) 사용자가 선택한 파일의 경우
    • 고려만 하세요 외부 저장소 관리 귀하의 앱이 실제로 파일 관리자, 백업 도구, 보안 앱 등이라면.

Android 13(API 33) – 미디어 권한 분할

  • 권한:
    • 미디어 이미지 읽기 – 이미지 접근
    • 미디어_비디오_읽기 – 비디오에 접속하세요
    • 미디어 오디오 읽기 – 오디오에 액세스
  • 행동:
    • 미디어 권한은 다음과 같습니다. 세밀한:
      • 사용자는 이미지 접근만 허용하거나, 비디오 접근만 허용할 수 있습니다.
    • Android 11의 범위 지정 저장소 규칙은 그대로 유지됩니다.
  • 권장되는 접근 방식:
    • 필요한 특정 미디어 권한을 요청하세요.
      • 예를 들어, 이미지로만 작업하는 경우 다음만 요청하세요. 미디어 이미지 읽기
    • 하다 ~ 아니다 요구 외부 저장소 읽기 Android 13 이상에서는 새로운 미디어 권한으로 대체되므로

빠른 매트릭스(개념적)

  • 안드로이드 9 이하
    • 외부 저장소에 대한 액세스는 READ/WRITE_EXTERNAL_STORAGE로 제어되는 광범위한 액세스입니다.
  • 안드로이드 10
    • 스코프 스토리지가 도입되었지만 탈출구가 있습니다(요청레거시외부저장소)
  • 안드로이드 11
    • 범위 지정 저장소가 적용되고 레거시 스위치가 제거되었습니다.
    • 외부 저장소 관리 나타나지만 매우 제한적입니다
  • 안드로이드 13
    • 미디어 접근은 다음과 같이 분할됩니다. 미디어 읽기* 권한
    • 동일한 범위 지정 저장소 규칙이지만 더 세분화된 사용자 제어가 가능합니다.

6. 테이크어웨이

  • "한 Android 13 기기에서만 작동한다"고 해서 다른 모든 기기에서 작동한다는 뜻은 아닙니다. OEM과 시스템 보고서는 일관되지 않을 수 있습니다.
  • 을 위한 안드로이드 11 이상, 다음과 같은 관점에서 생각해 보세요.
    • 앱 개인 디렉토리 + MediaStore + SAF, "원시"가 아닙니다 /SD카드 입장"”
  • 대하다 외부 저장소 관리 로서 최후의 수단 매우 구체적인 앱 유형, 특히 Google Play에 게시할 계획이라면 더욱 그렇습니다.
  • 항상 테스트하세요 다양한 기기와 안드로이드 버전, 특히 권한과 저장과 관련된 경우에 그렇습니다.

이 글은 제가 실제 안드로이드 프로젝트(안드로이드 13을 탑재한 삼성 기기 포함)에서 직접 디버깅하고 수정한 내용을 바탕으로 작성되었습니다. GPT는 단지 번역과 표현 다듬기에 도움을 주었을 뿐이며, 모든 기술적 내용과 결정은 저에게 있습니다.

저자 소개

게시물 공유:

AI로 요약하기

목차

연결 상태 유지

더 많은 업데이트

WAN 2.6 빠른 시작 가이드

WAN 2.6의 공동 출시 파트너로서, 저희 Ima Studio는 지난 2주 동안 핵심 기능을 철저하게 테스트해 왔습니다. 오늘 드디어 그 결실을 맺게 되었습니다.