범위가 지정된 디렉터리 액세스 사용

일반적으로 사진 앱과 같은 앱은 외부 저장소의 특정 디렉터리(예: Pictures 디렉터리)에만 액세스해야 합니다. 기존 외부 저장소 액세스 방식은 이런 유형의 앱에 대상화된 디렉터리 액세스를 쉽게 제공하지 못합니다. 예를 들면 다음과 같습니다.

  • 매니페스트에서 READ_EXTERNAL_STORAGE 또는 WRITE_EXTERNAL_STORAGE를 요청하면 외부 저장소의 모든 공개 디렉터리에 액세스할 수 있습니다. 이는 앱에 필요한 것보다 과도한 액세스일 수 있습니다.
  • 일반적으로, 저장소 액세스 프레임워크를 사용하면 시스템 UI를 통해 사용자가 디렉터리를 선택할 수 있습니다. 이것은 앱이 항상 동일한 외부 디렉터리에 액세스한다면 불필요한 동작입니다.

Android 7.0에서는 일반 외부 저장소 디렉터리에 액세스하기 위한 단순화된 API를 제공합니다.

외부 저장소 디렉터리 액세스

StorageManager 클래스를 사용하여 적절한 StorageVolume 인스턴스를 가져옵니다. 그 후, 해당 인스턴스의 StorageVolume.createAccessIntent() 메서드를 호출하여 인텐트를 생성합니다. 이 인텐트를 사용하여 외부 저장소 디렉터리에 액세스합니다. 이동식 미디어 볼륨을 비롯한 모든 사용 가능한 볼륨의 목록을 가져오려면 StorageManager.getStorageVolumes()를 사용합니다.

특정 파일에 대한 정보가 있으면 StorageManager.getStorageVolume(File)을 사용하여 해당 파일이 포함된 StorageVolume을 가져옵니다. 이 StorageVolume에서 createAccessIntent()를 호출하여 파일의 외부 저장소 디렉터리에 액세스합니다.

외부 SD 카드와 같은 보조 볼륨에서는 특정 디렉터리 대신 전체 볼륨에 대한 액세스를 요청하려면 createAccessIntent()를 호출할 때 null을 전달합니다. 기본 볼륨에 null을 전달하거나 잘못된 디렉터리 이름을 전달하는 경우 createAccessIntent()는 null을 반환합니다.

다음 코드 조각은 기본 공유 저장소에서 Pictures 디렉터리를 여는 방법에 대한 예시입니다.

StorageManager sm = (StorageManager)getSystemService(Context.STORAGE_SERVICE);
StorageVolume volume = sm.getPrimaryStorageVolume();
Intent intent = volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);

시스템이 외부 디렉터리에 액세스 권한을 부여하려고 시도하고, 필요한 경우 단순화된 UI를 사용하여 사용자의 액세스를 확인합니다.

그림 1. Pictures 디렉터리에 액세스를 요청하는 애플리케이션.

사용자가 액세스 권한을 부여하면, 시스템이 RESULT_OK의 결과 코드가 포함된 onActivityResult() 재정의와 URI가 포함된 인텐트 데이터를 호출합니다. 제공된 URI를 사용하여 디렉터리 정보에 액세스합니다. 이는 저장소 액세스 프레임워크가 반환한 URI를 사용하는 것과 유사합니다.

사용자가 액세스 권한을 부여하지 않으면 시스템이 RESULT_CANCELED의 결과 코드가 포함된 onActivityResult() 재정의와 null 인텐트 데이터를 호출합니다.

특정 외부 디렉터리에 대한 액세스 권한을 얻으면 해당 디렉터리의 하위 디렉터리에 대한 액세스 권한도 얻게 됩니다.

이동식 미디어의 디렉터리 액세스

범위가 지정된 디렉터리 액세스를 사용하여 이동식 미디어의 디렉터리에 액세스하려면, 먼저 다음과 같은 MEDIA_MOUNTED 알림을 수신하는 BroadcastReceiver를 추가합니다.

<receiver
    android:name=".MediaMountedReceiver"
    android:enabled="true"
    android:exported="true" >
    <intent-filter>
        <action android:name="android.intent.action.MEDIA_MOUNTED" />
        <data android:scheme="file" />
    </intent-filter>
</receiver>

사용자가 SD 카드 등의 이동식 미디어를 장착하면 시스템이 MEDIA_MOUNTED 알림을 보냅니다. 이 알림은 이동식 미디어 디렉터리에 액세스하는 데 사용할 수 있는 인텐트 데이터의 StorageVolume 객체를 제공합니다. 다음은 이동식 미디어의 Pictures 디렉터리에 액세스하는 예시입니다.

// BroadcastReceiver has already cached the MEDIA_MOUNTED
// notification Intent in mediaMountedIntent
StorageVolume volume = (StorageVolume)
    mediaMountedIntent.getParcelableExtra(StorageVolume.EXTRA_STORAGE_VOLUME);
volume.createAccessIntent(Environment.DIRECTORY_PICTURES);
startActivityForResult(intent, request_code);

모범 사례

가능하면, 사용자에게 액세스 권한을 반복적으로 요청하지 않도록 외부 디렉터리 액세스 URI를 변경하지 마십시오. 사용자에게 액세스 권한을 부여하면, getContentResolver()를 호출하고, 반환된 ContentResolver가 디렉터리 액세스 URI로 takePersistableUriPermission() 을 호출합니다. 시스템이 URI를 유지하고 이후 액세스 요청에서는 RESULT_OK를 반환하고 사용자에게 확인 UI를 표시하지 않습니다.

사용자가 외부 디렉터리 액세스를 거부하면 다시 즉시 액세스를 요청하지 마십시오. 액세스를 반복적으로 요청하면 사용자 환경을 저해하는 결과를 낳습니다. 사용자가 요청을 거부하는데 앱이 다시 액세스를 요청하면, UI에 Don't ask again 체크박스가 표시됩니다.

그림 1. 이동식 미디어에 대해 다시 액세스 요청을 하는 애플리케이션.

사용자가 Don't ask again을 선택하여 요청을 거부하면 앱에서 해당 디렉터리에 대한 이후의 모든 요청이 자동으로 거부되고, 사용자에게는 어떤 요청 UI도 표시되지 않습니다.