[안드로이드] 저장소 파일 처리 FileProvider를 사용한 Context.getDir() 사용시 IllegalArgumentException: Failed to find configured root that contains /data/data/ 오류 처리 방법
안드로이드 앱 내부 저장소에 디렉토리 자동 생성 및 파일 쓰기를 위해서 ContextWrapper 클래스를 사용하여 초기화 후 getDir(name, mode)메소드를 사용하여 파일쓰기를 하였다. getDir()메소드는 앱의 고유한 파일 시스템 디렉토리 내에 새 디렉토리를 생성한다. 존재한다면 기존 디렉토리를 열어서 파일을 생성할 수 있다. 디렉토리를 별도로 생성하는 과정을 줄일 수 있어서 사용해왔는데 문제가 발생되었다. 공유하려고 보니 해당 파일의 경로를 찾지 못하는 java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/ 오류가 발생하였다.
String filename = getFileName();
ContextWrapper cw = new ContextWrapper(PreviewActivity.this);
File directory = cw.getDir("g_pdf", Context.MODE_PRIVATE);
final File mypath = new File(directory,filename);
shareFile = mypath;
FileOutputStream fos = null;
try {
fos = new FileOutputStream(mypath);
createPDF(fos);
Log.d("TAG", "================== PDF : " + mypath.getPath());
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if(fos!=null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
다음 메소드를 사용하여 파일을 생성할 경우 앱 사용자에게 저장소 권한을 요청할 필요가 없다.
메소드 | 설명 |
getDir(name, mode) | 앱의 고유한 파일 시스템 디렉터리 내에 새 디렉터리를 생성 또는 기존 디렉터리에 파일을 쓴다. |
getCacheDir() | 앱과 고유하게 연결된 파일 시스템의 캐시 디렉터리에 파일을 쓴다. |
getFilesDir() | 앱과 고유하게 연결된 파일 시스템의 디렉터리에 파일을 쓴다. |
AndroidManifest.xml
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="ddolcat.app.tools.test.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/fileprovider" />
</provider>
@xml/fileprovider.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path
name="cache"
path="." /> <!--Context.getCacheDir() 내부 저장소-->
<files-path
name="files"
path="." /> <!--Context.getFilesDir() 내부 저장소-->
<external-path
name="external"
path="."/> <!-- Environment.getExternalStorageDirectory() 외부 저장소-->
<external-cache-path
name="external-cache"
path="."/> <!-- Context.getExternalCacheDir() 외부 저장소-->
<external-files-path
name="external-files"
path="."/> <!-- Context.getExternalFilesDir() 외부 저장소-->
<files-path name="app_g_pdf" path="." />
</paths>
생성한 PDF파일을 공유하기 위한 코드 스니펫은 다음과 같다. Intent를 생성할때 반드시 Intent.FLAG_GRANT_READ_URI_PERMISSION 플래그를 추가해주어야한다. 그렇지 않으며 공유 프로그램 호출하는 과정에 오류가 발생하기 때문이다.
if (shareFile!=null && shareFile.exists()) {
File cacheFile = shareFile;
Uri uri = FileProvider.getUriForFile(PreviewActivity.this, "ddolcat.app.tools.test.provider", cacheFile);
Intent intent = ShareCompat.IntentBuilder.from(PreviewActivity.this)
.setType("application/pdf")
.setStream(uri)
.createChooserIntent()
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo resolveInfo : resInfoList) {
String packageName = resolveInfo.activityInfo.packageName;
getApplicationContext().grantUriPermission(packageName, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
startActivity(intent);
}
} catch (Exception e) {
e.printStackTrace();
}
발생 오류 java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/…………..
2021-02-18 19:47:07.471 21386-21386/ddolcat.app.tools.test D/TAG: ==== PDF : /data/user/0/ddolcat.app.tools.test/app_g_pdf/pdf_code_194706.pdf
21386-21386/ddolcat.app.tools.test W/System.err: java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/ddolcat.app.tools.test/app_g_pdf/pdf_code_194706.pdf
21386-21386/ddolcat.app.tools.test W/System.err: at androidx.core.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:744)
21386-21386/ddolcat.app.tools.test W/System.err: at androidx.core.content.FileProvider.getUriForFile(FileProvider.java:418)
21386-21386/ddolcat.app.tools.test W/System.err: at ddolcat.app.tools.test.PreviewActivity.sharePDFContents(PreviewActivity.java:151)
21386-21386/ddolcat.app.tools.test W/System.err: at ddolcat.app.tools.test.PreviewActivity.printPDF(PreviewActivity.java:339)
21386-21386/ddolcat.app.tools.test W/System.err: at ddolcat.app.tools.test.PreviewActivity$1.onClick(PreviewActivity.java:117)
21386-21386/ddolcat.app.tools.test W/System.err: at android.view.View.performClick(View.java:7169)
21386-21386/ddolcat.app.tools.test W/System.err: at android.view.View.performClickInternal(View.java:7139)
21386-21386/ddolcat.app.tools.test W/System.err: at android.view.View.access$3900(View.java:808)
21386-21386/ddolcat.app.tools.test W/System.err: at android.view.View$PerformClick.run(View.java:27481)
21386-21386/ddolcat.app.tools.test W/System.err: at android.os.Handler.handleCallback(Handler.java:883)
21386-21386/ddolcat.app.tools.test W/System.err: at android.os.Handler.dispatchMessage(Handler.java:100)
21386-21386/ddolcat.app.tools.test W/System.err: at android.os.Looper.loop(Looper.java:214)
21386-21386/ddolcat.app.tools.test W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7615)
21386-21386/ddolcat.app.tools.test W/System.err: at java.lang.reflect.Method.invoke(Native Method)
21386-21386/ddolcat.app.tools.test W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
21386-21386/ddolcat.app.tools.test W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:964)
파일생성시 cw.getDir(“g_pdf”, Context.MODE_PRIVATE); 로 접근했기 때문에 폴더는 app_(고정) 그 뒤에 g_pdf(개발자 정의 디렉토리)이름이 붙은 후 생성된다. 그런까닥에 오류 해결을 위해 @xml/fileprovider.xml에 아래와 같은 file경로를 추가해주고 다시 시도해보았으나 여전히 동일한 오류가 발생하였다. 해결법이 아닌 것이다.
<files-path name="app_g_pdf" path="." />
오류 해결방법
파일 생성시 getDir()메소드 대신 getFilesDir()메소드를 사용하여 처리하면 해결할 수 있으며 파일프로바이더(FileProvider)가 접근할 수 있도록@xml/fileprovider.xml에 files-path는 이미 추가해 준 상태이다.
File directory = cw.getFilesDir();
<files-path
name="files"
path="." /> <!--Context.getFilesDir() 내부 저장소-->
또 다른 오류 해결방법
@xml/fileprovider.xml에 root-path를 추가해 주면 접근이 가능해진다. 그러나 위의 방법을 권장한다. 루트 경로를 추가했을 때 액세스 권한이 있다면 모든 파일에 접근이 가능해지기 때문이다.
<root-path name="root_files" path="." />
REFERENCE