안드로이드 앱을 개발하다보면 기기안에있는 리소스에 접근을 해야할때가 있습니다. 예를들어 사진이라든가 문서, 비디오등이 있다고 해보죠. 이경우 해당 위치에 대한주소를 가지고 오더라고 해당 Uri가 file:// 스킴을 사용하는데, Android 7.0 (API 레벨 24)부터는 파일 시스템 경로를 직접 노출할 수 없고, 사용하더라도 FileUriExposedException이 발생합니다. 이는 보안상의 이유로 앱 간 파일 공유를 제한하기 위한 것입니다. 이 경우 우린 FileProvider를 이용할수 있습니다.

 

FileProvider.getUriForFile()를 사용하면 FileProvider가 제공하는 content:// 스킴을 통해 파일에 대한 안전한 접근이 가능하며, 파일 공유 규칙을 준수할 수 있습니다. 이를 통해 안드로이드 시스템은 앱 간 파일 공유를 보다 안전하게 할 수 있습니다.

 

사용하는 방법 알아보겠습니다.

 

AndroidManifest.xml

<provider
    android:name="androidx.core.content.FileProvider"
    android:authorities="kr.xxx.insurance.fileprovider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/file_provider" />
</provider>

 

xml/file_provider.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">

    <files-path
        name="files"
        path="."/> <!--    내부저장소 / Context.getFilesDir()-->

    <cache-path
        name="internal_cache_path"
        path="." /> <!--    내부저장소 / getCacheDir()-->

    <external-path
        name="external_files"
        path="."/> <!--    외부저장소 / Environment.getExternalStorageDirectory()-->

    <external-files-path
        name="external_files_path"
        path="." /> <!--    외부저장소 / Context.getExternalFilesDir()-->

    <external-cache-path
        name="external_cache_path"
        path="." /> <!--    외부캐시영역 / Context.getExternalCacheDir()-->

    <external-path name="Insurance" path="/Insurance"/>
</paths>

 

파일 프로바이더를 이용하여 content:// 스킴을 가지는 Uri를 가져와 사용할수 있습니다.

 

String fileDir = String.valueOf(this.getFilesDir());
String cacheDir = String.valueOf(this.getCacheDir());
String externalStorageDir = String.valueOf(this.getExternalFilesDir(null));
String externalFilesDir = String.valueOf(this.getExternalFilesDir(null));
String externalCacheDir = String.valueOf(this.getExternalCacheDir());
String insuranceDir = String.valueOf(this.getDir("insurance", MODE_PRIVATE));
String dowloadFolder = String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
String absPath = this.getFilesDir().getAbsolutePath();

DLog.d(new Exception(), "fileDir : " + fileDir);
DLog.d(new Exception(), "cacheDir : " + cacheDir);
DLog.d(new Exception(), "externalStorageDir : " + externalStorageDir);
DLog.d(new Exception(), "externalFilesDir : " + externalFilesDir);
DLog.d(new Exception(), "externalCacheDir : " + externalCacheDir);
DLog.d(new Exception(), "insuranceDir : " + insuranceDir);
DLog.d(new Exception(), "dowloadFolder : " + dowloadFolder);
DLog.d(new Exception(), "absPath : " + absPath);


[결과]
fileDir            : /data/user/0/kr.xxxx.insurance/files
cacheDir           : /data/user/0/kr.xxxx.insurance/cache
externalStorageDir : /storage/emulated/0/Android/data/kr.xxxx.insurance/files
externalFilesDir   : /storage/emulated/0/Android/data/kr.xxxx.insurance/files
externalCacheDir   : /storage/emulated/0/Android/data/kr.xxxx.insurance/cache
insuranceDir       : /data/user/0/kr.xxxx.insurance/app_XXInsurance
dowloadFolder      : /storage/emulated/0/Download
absPath            : /data/user/0/kr.xxxx.insurance/files

 

외부 저장소 사용할 수 있는지 확인

String baseFolder;
// 외부 저장소를 사용할 수 있는지 확인
if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    baseFolder = String.valueOf(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
}
// 내부 저장소 사용으로 되돌리기(위와 동등한 것이 있는지 확실하지 않음)
else {
    baseFolder = this.getFilesDir().getAbsolutePath();
}

// 파일 저장
File file = new File(baseFolder + File.separator + "test.text");
file.setWritable(true);
file.setReadable(true);
file.getParentFile().mkdirs();
try {
    FileOutputStream fos = new FileOutputStream(file);
    fos.write("저장할 메세지 텍스트 String".getBytes());
    fos.flush();
    fos.close();
} catch (IOException e) {
    DLog.e(new Exception(), e.getMessage());
}

+ Recent posts