갑자기 잘되던 어플에서 android.os.FileUriExposedException 와 같은 오류가 발생하였습니다. (구글 마켓에 등록된 어플에서 구글 개발자 콘솔로 확인하면 마켓에 등록된 어플에서 어떤 오류가 발생했는지 확인이 가능합니다.)
오류 내용
android.os.FileUriExposedException: file:///storage/emulated/0/Download/xxx_user_release-keys.csv exposed beyond app through ClipData.Item.getUri() at android.os.StrictMode.onFileUriExposed(StrictMode.java:1958) at android.net.Uri.checkFileUriExposed(Uri.java:2348) at android.content.ClipData.prepareToLeaveProcess(ClipData.java:944) at android.content.Intent.prepareToLeaveProcess(Intent.java:10480) at android.content.Intent.prepareToLeaveProcess(Intent.java:10486) at android.content.Intent.prepareToLeaveProcess(Intent.java:10465) at android.app.Instrumentation.execStartActivity(Instrumentation.java:1616) at android.app.Activity.startActivityForResult(Activity.java:4564) at android.support.v4.app.BaseFragmentActivityJB.startActivityForResult(BaseFragmentActivityJB.java:48) at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:75) at android.app.Activity.startActivityForResult(Activity.java:4522) at android.support.v4.app.FragmentActivity.startActivityForResult(FragmentActivity.java:856) at android.app.Activity.startActivity(Activity.java:4883) at android.app.Activity.startActivity(Activity.java:4851)
해당 오류는 7.0 API 24부터 발생하는 오류입니다.
https://developer.android.com/about/versions/nougat/android-7.0-changes.html?hl=ko
앱 사이의 파일 공유
Android 7.0을 대상으로 하는 앱의 경우, Android 프레임워크는 앱 외부에서
file://
URI의 노출을 금지하는 StrictMode
API 정책을 적용합니다. 파일 URI를 포함하는 인텐트가 앱을 떠나면 FileUriExposedException
예외와 함께 앱에 오류가 발생합니다.
애플리케이션 간에 파일을 공유하려면
content://
URI를 보내고 이 URI에 대해 임시 액세스 권한을 부여해야 합니다. 이 권한을 가장 쉽게 부여하는 방법은 FileProvider
클래스를 사용하는 방법입니다. 권한과 파일 공유에 대한 자세한 내용은 파일 공유를 참조하세요.문제의 소스에서는 다른 어플로 intent를 전달하여 startActivity시키는 함수입니다. 이 부분이 파일 URI가 있어서 오류가 발생하게 됩니다.
문제의 소스
private void fabUploadMenu() { if( getSelectedItemCount()!=1 ) { Toast.makeText(getApplicationContext(), "Please select only one item", Toast.LENGTH_SHORT).show(); return; } Intent it = new Intent(Intent.ACTION_SEND); String fname = getSelectedItem(); it.putExtra(Intent.EXTRA_SUBJECT, fname); it.putExtra(Intent.EXTRA_TEXT, fname); String path = LocalFileAccess.getDefaultPath(); path += "/"+fname; File file = new File(path); if (!file.exists() || !file.canRead()) { Toast.makeText(this, "Attachment Error", Toast.LENGTH_SHORT).show(); return; } Uri uri = Uri.parse("file://"+file); it.putExtra(Intent.EXTRA_STREAM, uri); it.setType("text/plain"); startActivity(Intent.createChooser(it, "Choose a application for sending")); }
해결하기 위해서는 3단계로 처리합니다.
1단계
AndroidManifest.xml에 provider를 넣습니다.AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> ... <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> </application> </manifest>
2단계
res/xml/provider_paths.xml 아래 내용으로 파일을 추가합니다.
provider_paths.xml
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external_files" path="."/> </paths>
3단계
FileProvider.getUriForFile api를 이용하여 문제의 코드를 변경합니다.문제의 소스
Uri uri = Uri.parse("file://"+file); => Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file);
원본 출처 : 아래 코드는 screenshot 공유에 대한 부분 소스를 참고하 였습니다.
https://inthecheesefactory.com/blog/how-to-share-access-to-file-with-fileprovider-on-android-nougat/en
댓글 없음:
댓글 쓰기