java.lang.IllegalArgumentException: Failed to find configured root that contains ...
이런 에러 보신 적 있으신가요? 이게 다 FileProvider path 설정 때문입니다.
에러에 대해 설명하기 앞서 일단, Android developers Documentation 설명을 제가 이해한 대로 적어보겠습니다.
FileProvider는 ContentProvider의 하위 클래스입니다. 보안을 위해서, file Uri 대신 content Uri를 사용합니다. contentUri는 파일을 읽고 쓰는데 임시 접근 권한을 주고, content Uri가 포함된 Intent를 만들 때, 권한을 주는 Flag를 설정할 수 있습니다. MainActivity에서 아래와 같이 쓸 수 있습니다. (여기서 camerImageUri가 contentUri에 해당합니다. 이에 대해선 밑에서 다시 써보도록 하겠습니다.)
Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri);
intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
일단 FileProvider를 이용하기 위해선 manifest파일에 정의를 해주어야 합니다. <provider> 요소의 authorities 속성은 content Uri를 생성할 때 경로에 포함됩니다. 자세한 설명은 밑에 나와있습니다.
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
FileProvider는 (xml파일에서) 미리 명시한 경로가 포함된 content Uri를 생성할 수 있습니다. xml파일에 <paths>요소를 정의하여 경로를 적어줍니다.
Failed to find configured root that contains ... 의 에러가 나는 경우는 xml파일의 경로를 잘못써주었기 때문에 발생하는 경우가 많습니다. 파일을 생성할 때, 저는 getFilesDir() 메소드를 통해 경로를 지정해주었습니다.
File path = getFilesDir();
File file = new File(path, "sample.png");
이때 xml에서 <paths> 하위요소에는 <files-path>를 적어주어야 합니다!
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">\
<files-path name="files" path="/" />
</paths>
아래 표와 같이 파일 경로에 따라 <paths>의 하위요소가 달라지니 참고바랍니다.
getFilesDir() | <files-path name='name' path='path'> |
getCacheDir() | <cache-path name='name' path='path'> |
getExternalStorageDirectory() | <external-path name='name' path='path'> |
getExternalFileDir(String), getExternalFilesDir(null) | <external-files-path name='name' path='path'> |
getExternalCacheDir() | <external-cache-path name='name' path='path'> |
getExternalMediaDirs() | <external-media-path name='name' path='path'> |
'<provider>에서 정의한 권한/name 속성명/파일명' 의 경로를 포함한 contentUri가 만들어집니다.
따라서 사용자가 sample.png 파일의 Content Uri를 요청하면 FileProvider는 다음 예시와 비슷한 형태로 반환합니다.
ex)content://com.example.myapp.provider/name/sample.png
카메라를 실행시키는 부분의 코드는 아래와 같습니다.
public static final int REQUEST_CODE_CAMERA = 100;
...
private Context mCtx;
private Uri cameraImageUri = null;
...
Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File path = getFilesDir();
File file = new File(path, "sample.png");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
String strpa = getApplicationContext().getPackageName();
cameraImageUri = FileProvider.getUriForFile(this, mCtx.getApplicationInfo().packageName + ".provider", file);
}
else // API 24 미만
cameraImageUri = Uri.fromFile(file);
intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, cameraImageUri);
intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivityForResult(intentCamera, Constants.REQUEST_CODE_CAMERA);
'공부기록 > 안드로이드' 카테고리의 다른 글
Android Emulator can't provide internet (0) | 2022.06.22 |
---|---|
[안드로이드] 키보드 show&hide 이벤트 발생시키기 (0) | 2021.04.12 |
[안드로이드] 뒤로가기(backpressed) 안되는 경우 (0) | 2021.01.28 |
[안드로이드] setOnKeyListener 엔터(두 번 실행되는 경우) (0) | 2021.01.28 |
[안드로이드] Fragment to Fragment 데이터 쉽게 전달하기(자료형이 List 일 때) (0) | 2021.01.27 |