맞춤 문서 제공 자 만들 기 | Android 개발자 | Android Developers 
 파일 에 저 장소 서비스 ( 예 : 클라우드 저장 서비스 ) 를 제공 하 는 앱 을 개발 하 고 있 다면 맞춤 문서 제공 자 를 작성 하 여 저 장소 액세스 프레임워크 ( SFA ) 를 통해 파일 을 사용 가능 하 게 할 수 있 습니다 . 이 페이지 에서 는 맞춤 문서 제공 자 를 만드 는 방법 에 관해 설명 합니다 . 
 저 장소 액세스 프레임워크 의 작동 방식 에 관한 자세 한 내용 은 저 장소 액세스 프레임워크 개요 를 참조 하 세요 . 
 맞춤 문서 제공 자 를 구현 하 려면 애플리케이션 의 manifest 에 다음 을 추가 합니다 . 
 API 레벨 19 이상 인 타 겟 . 
 맞춤 저 장소 제공 업체 를 선언 하 는 < provider > 요소 . 
 다음 과 같이 패키지 이름 을 포함 한 클래스 이름 을 DocumentsProvider 서브 클래스 의 이름 으로 설정 한 android : name 속성 . 
 com . example . android . storageprovider . MyCloudProvider 
 패키지 이름 ( 이 예 에서 는 com . example . android . storageprovider ) 과 콘텐츠 제공 업체 의 유형 ( documents ) 을 합친 android : authority 속성 . 
 " true " 로 설정 한 android : exported 속성 . 다른 앱 에서 볼 수 있 도록 제공 업체 를 내보내 야 합니다 . 
 " true " 로 설정 한 android : grantUriPermissions 속성 . 이 설정 을 사용 하 면 시스템 에서 다른 앱 에 제공 업체 의 콘텐츠 에 액세스 하 도록 권한 을 부여 할 수 있 습니다 . 특정 문서 에 부여 한 권한 을 유지 하 는 방법 은 권한 유지 를 참조 하 세요 . 
 MANAGE _ DOCUMENTS 권한 . 기본 적 으로 제공 자 는 모든 사용 자 가 사용 할 수 있 습니다 . 이 권한 을 추가 하 면 제공 자 가 시스템 으로 제한 됩니다 . 이 제한 은 보안 에 중요 합니다 . 
 시스템 이 제공 자 를 검색 할 때 제공 자 가 선택 도구 에 보이 도록 android . content . action . DOCUMENTS _ PROVIDER 작업 을 포함 하 는 인 텐트 필터 . 
 다음 은 제공 자 가 포함 된 샘플 manifest 에서 발췌 한 것 입니다 . 
 Android 4 . 3 이하 를 실행 하 는 지원 기기 
 ACTION _ OPEN _ DOCUMENT 인 텐트 는 Android 4 . 4 이상 을 실행 하 는 기기 에서 만 사용 할 수 있 습니다 . 애플리케이션 에서 ACTION _ GET _ CONTENT 를 지원 하 여 Android 4 . 3 이하 를 실행 하 는 기기 를 수용 하 려면 Android 4 . 4 이상 을 실행 하 는 기기 의 manifest 에서 ACTION _ GET _ CONTENT 인 텐트 필터 를 중지 해야 합니다 . 문서 제공 자 및 ACTION _ GET _ CONTENT 는 상호 배타 적 이 어야 합니다 . 두 가지 를 동시 에 지원 하 면 앱 이 시스템 선택 도구 UI 에 두 번 표시 되 어 저장 된 데이터 에 액세스 하 는 방법 이 두 가지 로 다르 게 제공 됩니다 . 이 는 사용 자 에게 혼동 을 줍니다 . 
 다음 은 Android 버전 4 . 4 이상 을 실행 하 는 기기 에서 ACTION _ GET _ CONTENT 인 텐트 필터 를 중지 하 는 데 추천 하 는 방법 입니다 . 
 res / values / 아래 bool . xml 리소스 파일 에 다음 라인 을 추가 합니다 . 
 res / values - v 19 / 아래 bool . xml 리소스 파일 에 다음 라인 을 추가 합니다 . 
 작업 별칭 을 추가 하 여 버전 4 . 4 ( API 레벨 19 ) 이상 에서 ACTION _ GET _ CONTENT 인 텐트 필터 를 중지 합니다 . 예 : 
 일반 적 으로 맞춤 콘텐츠 제공 업체 를 작성 할 때 해야 하 는 작업 중 하나 는 콘텐츠 제공 업체 개발자 가이드 에 설명 된 대로 계약 클래스 를 구현 하 는 것 입니다 . 계약 클래스 는 URI , 열 이름 , MIME 유형 및 제공 업체 에 적용 되 는 기타 메타데이터 에 관한 상수 정의 를 포함 하 는 public final 클래스 입니다 . SAF 는 다음 과 같 은 계약 클래스 를 제공 하 므로 직접 작성 할 필요 가 없 습니다 . 
 예 를 들 어 , 다음 은 문서 나 루트 에 관해 문서 제공 자 를 쿼리 할 때 커서 에 반환 할 수 있 는 열 입니다 . 
 루트 의 커서 에 는 특정 열 이 필수 로 포함 되 어야 합니다 . 필수 열 은 다음 과 같 습니다 . 
 문서 의 커서 에 다음 열 이 필수 로 포함 되 어야 합니다 . 
 DocumentsProvider 의 서브 클래스 만들 기 
 맞춤 문서 제공 자 를 작성 하 는 다음 단계 는 DocumentsProvider 추상 클래스 의 서브 클래스 를 만드 는 것 입니다 . 최소한 다음 메 서 드 를 구현 해야 합니다 . 
 반드시 구현 해야 하 는 메 서 드 는 이것 뿐 이 지만 추가 로 구현 할 수 있 는 메 서드 는 더 많이 있 습니다 . 자세 한 내용 은 DocumentsProvider 를 참조 하 세요 . 
 루트 정의 
 queryRoots ( ) 의 구현 은 DocumentsContract . Root 에 정의 된 열 을 사용 하 여 문서 제공 자 의 모든 루트 디렉터리 를 가리키 는 Cursor 를 반환 해야 합니다 . 
 다음 스니 펫 에서 projection 매개변수 는 호출자 가 가져오 려는 특정 필드 를 나타냅니다 . 스니 펫 은 새 커서 를 만들 고 하나 의 행 ( 다운로드 또는 이미지 와 같 은 최상위 디렉터리 , 즉 하나 의 루트 ) 을 커서 에 추가 합니다 . 대부분 의 제공 자 에 는 하나 의 루트 만 있 습니다 . 예 를 들 어 , 사용 자 계정 이 여러 개 인 경우 하나 이상 의 루트 가 있 을 수 있 습니다 . 이 경우 커서 에 두 번 째 행 을 추가 하 기 만 하 면 됩니다 . 
 문서 제공 자 가 동적 루트 집합 ( 예 : 연결 이 끊어진 USB 기기 또는 사용 자 가 로그아웃 할 수 있 는 계정 ) 에 연결 한다면 ContentResolver . notifyChange ( ) 메서 드 를 사용 하 여 이러 한 변경 사항 을 동기 화 된 상태 로 유지 할 수 있 도록 문서 UI 를 업데이트 할 수 있 습니다 . 
 제 공자 에 문서 나열 
 queryChildDocuments ( ) 구현 은 DocumentsContract . Document 에 정의 된 열 을 사용 하 여 특정 디렉터리 의 모든 파일 을 가리키 는 Cursor 를 반환 해야 합니다 . 
 사용자 가 선택 도구 UI 에서 루트 를 선택 하 면 이 메 서드 가 호출 됩니다 . 이 메 서 드 는 COLUMN _ DOCUMENT _ ID 에서 지정 한 문서 ID 의 하위 요소 를 가져옵니다 . 그런 다음 사용 자 가 문서 제공 자 내 하위 디렉터리 를 선택 할 때 마다 시스템 에서 이 메서 드 를 호출 합니다 . 
 이 스니 펫 은 요청 된 열 을 포함 한 새 커서 를 만든 다음 상위 디렉터리 의 바로 아래 에 있 는 모든 하위 요소 에 관한 정보 를 커서 에 추가 합니다 . 하위 요소 는 이미지 , 다른 디렉터리 즉 , 다음 과 같이 모든 파일 이 될 수 있 습니다 . 
 문서 정보 가져오 기 
 queryDocument ( ) 의 구현 은 DocumentsContract . Document 에 정의 된 열 을 사용 하 여 특정 파일 을 가리키 는 Cursor 를 반환 해야 합니다 . 
 queryDocument ( ) 메 서 드 는 queryChildDocuments ( ) 에 전달 된 것 과 동일 한 정보 를 반환 하 지만 , 특정 파일 의 경우 다음 과 같 습니다 . 
 문서 제공 자 는 DocumentsProvider . openDocumentThumbnail ( ) 메서 드 를 재정 의 하 고 지원 된 파일 에 FLAG _ SUPPORTS _ THUMBNAIL 플래그 를 추가 하 여 문서 의 미리 보 기 이미지 도 제공 할 수 있 습니다 . 다음 코드 스니 펫 은 DocumentsProvider . openDocumentThumbnail ( ) 을 구현 하 는 방법 의 예 를 제공 합니다 . 
 주의 : 문서 제공 자 는 sizeHint 매개변수 에서 지정 한 크기 의 두 배 가 넘 는 미리 보 기 이미지 를 반환 해서 는 안 됩니다 . 
 openDocument ( ) 를 구현 하 여 지정 된 파일 을 나타내 는 ParcelFileDescriptor 를 반환 해야 합니다 . 다른 앱 은 반환 된 ParcelFileDescriptor 를 사용 하 여 데이터 를 스트림 할 수 있 습니다 . 사용자 가 파일 을 선택 하 면 시스템 에서 이 메서 드 를 호출 하 고 클라이언트 앱 은 openFileDescriptor ( ) 를 호출 하 여 파일 에 액세스 할 수 있 도록 요청 합니다 . 예 : 
 문서 제공 자 가 파일 을 스트림 하 거나 복잡 한 데이터 구조 를 처리 한다면 createReliablePipe ( ) 또는 createReliableSocketPair ( ) 메 서드 구현 을 고려 해 보 세요 . 이러 한 메서 드 를 사용 하 면 ParcelFileDescriptor 객체 의 쌍 을 만들 수 있 으며 ParcelFileDescriptor . AutoCloseOutputStream 또는 ParcelFileDescriptor . AutoCloseInputStream 을 통하 여 한 객체 를 반환 하 고 나머지 객체 를 보낼 수 있 습니다 . 
 최근 문서 지원 및 검색 
 queryRecentDocuments ( ) 메서 드 를 재정 의 하 고 FLAG _ SUPPORTS _ RECENTS 를 반환 하 여 문서 제공 자 의 루트 아래 에서 최근 수정 된 문서 목록 을 제공 할 수 있 습니다 . 다음 코드 스니 펫 은 메 서 드 를 구현 하 는 방법 의 예 를 보여 줍니다 . 
 위 스니 펫 의 전체 코드 는 StorageProvider 코드 샘플 을 다운로드 하 여 받 을 수 있 습니다 . 
 문서 생성 지원 
 클라이언트 앱 을 사용 하 여 문서 제공 자 내 에 파일 을 만들 수 있 습니다 . 클라이언트 앱 이 ACTION _ CREATE _ DOCUMENT 인 텐트 를 보내 면 문서 제공 자 는 클라이언트 앱 이 문서 제공 자 내 에 새 문서 를 만들 도록 허용 할 수 있 습니다 . 
 문서 생성 을 지원 하 려면 루트 에 FLAG _ SUPPORTS _ CREATE 플래그 가 있 어야 합니다 . 디렉터리 내 에 새 파일 을 만들 도록 허용 하 려면 디렉터리 에 FLAG _ DIR _ SUPPORTS _ CREATE 플래그 가 있 어야 합니다 . 
 문서 제공 자 는 또한 createDocument ( ) 메서 드 를 구현 해야 합니다 . 사용자 가 문서 제공 자 내 의 디렉터리 를 선택 하 여 새 파일 을 저장 하 면 문서 제공 자 는 createDocument ( ) 의 호출 을 수신 합니다 . createDocument ( ) 메 서드 의 구현 내 에서 파일 의 새 COLUMN _ DOCUMENT _ ID 를 반환 합니다 . 그런 다음 클라이언트 앱 은 ID 를 사용 하 여 파일 의 핸들 을 가져오 고 최종 적 으로 openDocument ( ) 를 호출 하 여 새 파일 에 쓸 수 있 습니다 . 
 다음 코드 스니 펫 은 문서 제공 자 내 에 새 파일 을 만드 는 방법 을 보여 줍니다 . 
 문서 관리 기능 지원 
 문서 제공 자 를 사용 하 면 클라이언트 앱 에서 파일 열기 , 만들 기 및 보 기 외 에 도 파일 의 이름 바꾸 기 , 복사 , 이동 및 삭제 기능 을 사용 할 수 있 습니다 . 문서 제공 자 에 문서 관리 기능 을 추가 하 려면 문서 의 COLUMN _ FLAGS 열 에 지원 되 는 기능 을 나타내 는 플래그 를 추가 합니다 . 또한 , DocumentsProvider 클래스 에서 상응 하 는 메서 드 를 구현 해야 합니다 . 
 다음 표 에 는 COLUMN _ FLAGS 플래그 와 문서 제공 자 가 특정 기능 을 노출 하 기 위해 구현 해야 하 는 DocumentsProvider 메 서드 가 나와 있 습니다 . 
 파일 삭제 FLAG _ SUPPORTS _ DELETE deleteDocument ( ) 
 파일 이름 바꾸 기 FLAG _ SUPPORTS _ RENAME renameDocument ( ) 
 파일 을 문서 제공 자 내 의 새 상위 디렉터리 로 복사 FLAG _ SUPPORTS _ COPY copyDocument ( ) 
 문서 공급자 내 의 한 디렉터리 에서 다른 디렉터리 로 파일 이동 FLAG _ SUPPORTS _ MOVE moveDocument ( ) 
 상위 디렉터리 에서 파일 삭제 FLAG _ SUPPORTS _ REMOVE removeDocument ( ) 
 가상 파일 및 대체 파일 형식 지원 
 Android 7 . 0 ( API 레벨 24 ) 부터 도입 된 기능 인 가상 파일 을 사용 하 면 문서 제공 자 가 직접 적 인 바이트 코드 표현 이 없 는 파일 에 보 기 권한 을 제공 할 수 있 습니다 . 다른 앱 에서 가상 파일 을 볼 수 있 게 사용 설정 하 려면 문서 제공 자 는 가상 파일 의 열 수 있 는 대체 파일 표현 을 제작 해야 합니다 . 
 예 를 들 어 , 문서 제공 자 가 다른 앱 에서 직접 열 수 없 는 파일 형식 을 포함 한다고 상상 해 보 면 이 는 근본 적 으로 가상 파일 입니다 . 클라이언트 앱 이 CATEGORY _ OPENABLE 카테고리 없이 ACTION _ VIEW 인 텐트 를 보낸다면 사용 자 는 보 기 작업 을 위해 문서 제공 자 내 의 이러 한 가상 파일 을 선택 할 수 있 습니다 . 그런 다음 문서 제공 자 는 이미지 와 같이 형식 은 다르 지만 열 수 있 는 파일 형식 의 가상 파일 을 반환 합니다 . 그러면 클라이언트 앱 은 가상 파일 을 열 어 사용 자 가 보 게 할 수 있 습니다 . 
 제 공자 에서 문서 가 가상 이 라고 선언 하 려면 queryDocument ( ) 메 서드 에서 반환 하 는 파일 에 FLAG _ VIRTUAL _ DOCUMENT 플래그 를 추가 해야 합니다 . 이 플래그 는 파일 이 직접 적 인 바이트코드 표현 을 갖 고 있 지 않 고 직접 열 수 없 는 파일 이 라는 것 을 클라이언트 앱 에 알려 줍니다 . 
 문서 제공 자 의 파일 이 가상 이 라고 선언 하 면 이미지 나 PDF 와 같 은 다른 MIME 유형 에서 파일 을 사용 할 수 있 게 하 는 것 이 좋 습니다 . 문서 제공 자 는 getDocumentStreamTypes ( ) 메서 드 를 재정 의 하 여 가상 파일 보 기 를 지원 하 는 대체 MIME 유형 을 선언 합니다 . 클라이언트 앱 이 getStreamTypes ( android . net . Uri , java . lang . String ) 메서 드 를 호출 하 면 시스템 은 문서 제공 자 의 getDocumentStreamTypes ( ) 메서 드 를 호출 합니다 . 그러 면 getDocumentStreamTypes ( ) 메 서 드 는 문서 제공 자 가 가상 파일 을 위해 지원 하 는 대체 MIME 유형 의 배열 을 반환 합니다 . 
 문서 제공 자 가 볼 수 있 는 파일 형식 으로 문서 를 생성 할 수 있 다고 클라이언트 에서 판단 하 면 클라이언트 앱 은 o p e nTypedAssetFileDescriptor ( ) 메서 드 를 호출 하 며 이 메 서 드 는 내부 적 으로 문서 제공 자 의 openTypedDocument ( ) 메서 드 를 호출 합니다 . 문서 제공 자 는 요청 된 파일 형식 으로 클라이언트 앱 에 파일 을 반환 합니다 . 
 다음 코드 스니 펫 은 getDocumentStreamTypes ( ) 와 openTypedDocument ( ) 메 서드 의 간단 한 구현 을 보여 줍니다 . 
 문서 제공 자 가 비밀 번호 로 보호 되 는 클라우드 스토리지 서비스 이 고 사용 자 의 파일 을 공유 하 기 전 에 사용 자 가 로그인 되 어 있 는지 확인 한다고 가정 합니다 . 사용자 가 로그인 하 지 않 았 다면 앱 에서 어떻게 해야 할까요 ? 해결 방법 은 queryRoots ( ) 의 구현 에서 0 루트 를 반환 하 는 것 입니다 . 즉 , 다음 과 같이 비 어 있 는 루트 커서 입니다 . 
 다른 방법 은 getContentResolver ( ) . notifyChange ( ) 를 호출 하 는 것 입니다 . DocumentsContract 를 기억 하 시 나요 ? 이 클래스 를 사용 하 여 URI 를 만들 고 있 습니다 . 다음 스니 펫 은 사용 자 의 로그인 상태 가 변경 될 때 마다 문서 제공 자 의 루트 를 쿼리 하 도록 시스템 에 알려 줍니다 . 사용자 가 로그인 하 지 않 았 다면 위 에서 보 는 바 와 같이 queryRoots ( ) 의 호출 이 빈 커서 를 반환 합니다 . 이렇게 하 면 사용 자 가 제공 자 에 로그인 했 을 때 만 제공 자 의 문서 를 사용 할 수 있 습니다 . 
 이 페이지 와 관련 된 샘플 코드 는 다음 을 참조 하 세요 . 
 이 페이지 와 관련 된 동영상 은 다음 을 참조 하 세요 . 
 DevBytes : Android 4 . 4 저 장소 액세스 프레임워크 : 제공 자 
 저 장소 액세스 프레임워크 : DocumentsProvider 빌드 
 저 장소 액세스 프레임워크 의 가상 파일 
 추가 정보 는 다음 을 참조 하 세요 . 
 DocumentsProvider 빌드 
 arrow _ back 저 장소 액세스 프레임워크 를 사용 하 여 파일 열기