티스토리 뷰

z




안드로이드 공식 문서이다. 요약을 해보자면 다음과 같다.


1. 앱 간 파일을 공유하는 방법은 파일 공유를 요청한 앱에게 요청을 수신한 앱이 파일을 공유해주는것이다.

2. 파일을 안전하게 공유해주는 방법은 그 파일에 대한 content URI (content://~~)를 요청 앱에 전달해주고 그 content uri에 대한 일시적인 접근 권한을 요청 앱에게 주는것이다.

3. 안드로이드의 FileProvider라는 Content Provider의 하위클래스의 getUriForFile()을 수행하면 위에서말한 파일에 대한 content uri를 얻은뒤에 그 content uri에 대한 임시 접근 권한을 부여한 다음 다른 앱에 content uri를 전달할 수 있다. 이 uri를 받은 앱에게만 그 임시 접근 권한이 부여되고 자동으로 expire되기 때문에 안전하다.

4. 서로 다른 앱끼리 파일이 아닌 데이터를 공유하기 위해서는 간단히 인텐트를 사용하면 된다. (인텐트의 putExtra getExtra 메소드사용)


이것도 요약을 해보자면 다음과 같다.

1. FileProvider는 ContentProvider의 하위 클래스이며, 앱의 파일을 안전하게 공유하게끔 도와준다. 이 FileProvider는 파일을 외부 앱에 공유하고자 할때 그 파일에 대한 file:/// uri를 만들어내는게 아니라 content:// uri를 만들어냄으로써 파일의 안전한 공유를 가능하게 한다.

2. 이 content URI를 포함한 인텐트를 생성시키고 다른 앱에 보낼수 있으며, 인텐트의 setFlags() 메소드를 사용하게 되면 그 Content URI에 대한 권한을 추가 할 수 있다. 그 인텐트로 다른 앱의 액티비티를 실행시키면, 그 액티비티가 액티비티 스택에서 활성화 되어 있는 동안 이 권한은 유지 된다. 만약 그 인텐트로 다른 서비스를 실행시켰다면 그 서비스가 실행되는 동안은 Content URI에 대한 권한이 유지된다.

3. 만약 file URI를 통해서 파일을 공유하고자 할때, 그 파일 자체에 권한을 수정해야 한다. 그렇게 되면 내가 공유를 원하고자 하는 특정 앱에서만 그 파일 uri에 접근할수 있는게 아니라 다른 모든 앱에서도 그 파일에 대해 접근할수 있게 되며 이렇게 되면 보안상 문제가 발생한다. 그렇기 때문에 Content uri를 내가 공유를 원하고자 하는 앱에게만 임시 권한을 부여하여 제공함으로써 이런 보안상 문제를 막을 수 있다. 그 공유를 원하고자 하는 특정 앱 이외에 다른 앱들에게는 이 Content URI를 통한 파일 접근이 불가능하다. 또한, 그 공유를 받은 특정 앱의 액티비티 스택에서 그 액티비티가 pop되었을때도 그 액티비티는 마찬가지로 Content URI에 접근이 불가능하다.



예전에(targetSdkVersion 24미만)는 Intent(MediaStore.ACTION_IMAGE_CAPTURE)라는 인텐트를 전송함으로써 카메라앱을 실행 시키고, 이 인텐트에 intent.putExtra(MediaStore.EXTRA_OUTPUT, file uri); 를 넣어서 startActivity()를 실행함으로써 촬영된 사진을 extra로 전달한 file uri에 저장하게끔 간단하게 구현 해 줄 수 있었다. 즉, 다시말해서 너의 카메라 앱을 실행시켜주고 그 결과물은 내 앱 홈폴더내에 있는 file:// ~~~ 이곳에 담아줘라고 요청하는것이다.

하지만 Sdk Version이 업되면서 보안이 강화되어서 file uri를 단순하게 위 방법처럼 공유 할 수 없게 되었다. 현재 내 앱 홈 폴더내의 파일의 uri(file uri)자체를 다른앱에 전송하는것 자체가 불가능해졌다.

(file uri가 적힌 인텐트가 발신 앱을 떠나는것 자체가 예외가 발생한다)


cf) file uri를 얻는 방법

Uri클래스의 fromFile메소드를 사용한다.

Uri.fromFile(File file)이런식으로 매개변수로 파일을 넘겨주게 되면 그 파일의 경로(/sdcard/~~~~.picutre.jpg등)를 file://extra/~~~와 같은 file uri로 변경시켜 준다. 이 file uri를 위의 빨간색으로 칠해진 부분에 넘기게 되면 카메라앱은 사진을 찍고 나서 저 uri에 사진을 저장하게 된다.


검색해본 결과 하나의 파일의 경로를 나타내는 방법이 안드로이드에서는 3가지가 있는것 같다. 첫번째는 그 파일의 절대경로이고, 두번째는 그 파일에 대한 파일 uri 세번째는 content uri이다. content uri는 컨텐트 프로바이더를 통한 파일 접근을 할때 필요한 uri이다.

(파일 구조에 대해서는 이 포스트를 먼저 읽고 와라.)


자 그럼 이제부터는 FileProvider 라는 Content Provider의 하위 클래스로부터 현재 내 앱 홈 폴더 내부에 있는 어떤 파일의 contenturi를 만들어서 카메라 앱에 인텐트로 담아서 전송해준다음, 카메라가 사진을 찍고 나온 그 이미지 파일을 현재 내 앱 홈폴더에 저장해달라고 요청해 보겠다. 물론 카메라앱은 내 앱에 있는 FileProvider를 통해서 데이터를 저장시키는 것이다.

(지금 나오는 얘기들이 무슨 얘기인지 잘 모르겠으면 위에 링크되어있는 포스트와 컨텐트 프로바이더에 대해서 학습하고 와라.)


가장 먼저 해야 할 일은 앱 홈폴더내에 어떤 디렉토리에 대해서 Content Uri를 만들어서 그 Uri에 대해 임시 접근 권한을 부여하고 다른 앱에 전달하는것을 허용 할지 XML파일로 지정해야 한다.


1. res/xml/provider.paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="name" path="path" /> <!-- Context.getFilesDir(). -->
<cache-path name="name" path="path" /> <!-- getCacheDir(). -->
<external-path name="name" path="path" /> <!-- Environment.getExternalStorageDirectory(). -->
<external-files-path name="name" path="path" /> <!-- Context#getExternalFilesDir(String) Context.getExternalFilesDir(null). -->
<external-cache-path name="name" path="path" /> <!-- Context.getExternalCacheDir(). -->
</paths>

앱의 홈폴더

data

data

패키지명(com.회사명.서비스명) -> 앱의 홈폴더

cache

files

Memo.obj

lib


위와 같은 구조로 구성 되어 있다.

이 앱의 홈 폴더 내부에 있는 디렉토리,파일의 path를 가져오기 위해서는 Context클래스의 함수를 사용하면 된다.


Context.getFileDir() -> 빨간색

Context.getFileStreamPath() -> 파란색

Context.getCacheDir() -> 캐시 파일들이 저장되는 경로

Context.getDatabasePath(파일명) -> 특정 데이터베이스 파일 경로를 얻어 온다.


앱의 홈 폴더는 다음과 같이 구성되어 있다고 이전포스트에서 얘기했었다. f

<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="images" path="./images" /> <!-- getCacheDir(). -->
</paths>

만약 위와 같이 선언하게 되면 cache/images 내부에 있는 모든 파일에 대해서 content uri를 생성하여 앱 외부에 공개하는것을 허용하겠다는 의미이다. 그럼 name은 뭐냐? 그 path에 대한 별명을 짓는것과 같다. 이렇게 한 이유는 보안을 강화하기 때문이라고 한다. 폴더 내부의 구조가 어떻게 되어 있는지 외부에 공개하지 않게끔 하기 위해서 cache/images 라는 경로를 단순히 images라는 별명으로 다시 만든것이다.


마찬가지로 만약 <files-path name="fileimages" path="./images"/> 라고 선언하면 files/images라는 폴더 밑에 있는 모든 파일에 대하여 content uri를 생성 & 임시 권한 & 외부 공개를 가능하게 하겠다는것이고, 그 files/images라는 경로에 대한 별칭을 fileimages로 하겠다는 의미이다.


이렇게 res폴더 내부에 xml폴더를 만들고 그 xml폴더 내부에 위와 같이 xml파일을 만들어 준다.


2.AndroidManifest.xml


1
2
3
4
5
6
7
8
9
<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="${applicationId}.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/provider_paths" />
</provider>
cs


위와 같이 Content Provider를 선언하는것과 똑같이 FileProvider를 선언해주면 된다. 클래스는 이미 서포트 라이브러리에 정의된 FileProvider를 name 속성에 지정해주면 되고 authorities에는 다른 앱에서 내 앱의 FileProvider를 찾을 수 있게끔 적어야 한다. 

exported 속성을 false로 설정함으로써 fileprovider를 앱 내부에서만 접근 가능하게 만들었다. fileprovider라는것은 그 앱에서만 접근 할 필요가 있지 다른 앱에서는 내 앱의 fileprovider에 접근할 이유가 단 하나도 없기 때문에 이렇게 설정해야 한다. 나는 지금 카메라 앱에 내 앱 홈폴더내부의 디렉토리에 대한 어떤 파일의 content uri를 만들어서 카메라 앱에 제공하기만 하면 된다. grantUriPermissions 속성을 true 로 설정함으로써 이 FileProvider가 특정 파일에 대한 임시 권한을 부여 할 수 있게끔 설정한다.

FileProvider 내부에 Content URI생성 기능이 기본적으로 내장되어 있다.


${applicationId}

android {
defaultConfig {
applicationId "com.sharewith.smartudy"
}
}
applicationId는 build.gradle에 나와 있으며 그냥 패키지명을 의미한다.

FileProvider는 앱 홈 폴더내부에 있는 모든 파일에 대해 Content URI를 생성할수 있는게 아니라,
내가 지정한 디렉토리에있는 파일에 대해서만 Content URI를 생성할 수 있다.
그것을 meta-data 엘리먼트의 resource속성에 해당하는 xml파일에 정의하면 되는것이다.

이 다음부터는 2편에서 다시 소개하겠다.


'컴퓨터 공학과 졸업 > 안드 개발 기록' 카테고리의 다른 글

외장메모리공간  (0) 2018.07.22
어플리케이션 간 파일 공유 2  (0) 2018.07.22
MediaStore  (0) 2018.07.21
File, File Path, SharedPreferences  (0) 2018.07.21
암시적인텐트  (0) 2018.07.21
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함