Я получаю исключение android.os.StrictMode.onFileUriExposed при попытке отправить файл с намерением. Насколько я понимаю, это связано с тем, что я нацелен на 24>, а file:// больше не поддерживается, следует использовать content://.
Сначала я хотел бы сказать, что я видел похожие вопросы, такие как это, это, и я также видел сообщение в блоге это.
Но проблема в том, что все сообщения относятся к URI при съемке изображения, в моем случае файл успешно сохраняется с использованием Uri, и теперь я хочу отправить изображение с помощью Intent, как показано ниже:
shareBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
Uri screenshotUri = Uri.parse("file://" + directoryToStore + "/" + filename);
//the Uri above - file:///storage/emulated/0/Android/data/myPackageName/files/SavedImages/test.jpeg
sharingIntent.setType("image/jpeg");
sharingIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
startActivity(Intent.createChooser(sharingIntent, "Share image using"));
}
});
Выполняя вышеизложенное, я получаю сбой только на некоторых устройствах, работающих под управлением 19>. При тестировании на моем Samsung J7Pro (Android 7.0 API 24) сбой не возникает.
Я видел, что в некоторых из этих ответов говорится, что я могу использовать:
StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());
Но это не лучший способ.
Итак, мой вопрос. Как мне обрабатывать отправку файлов при таргетинге на 24>. Должен ли я выполнять оператор if / else, проверяя версию, например if (Build.VERSION.SDK_INT >= 19) {, а затем использовать обычный Uri для устройств, работающих под управлением <24, и как мне изменить file:// на content://? Я также не понимаю, почему сбой происходит только на некоторых устройствах.
РЕДАКТИРОВАТЬ 1:
Я сделал то, что предложил ответ ниже, но файл не передается с намерением, вместо этого я получаю Toast, говорящий Unable to attach file, когда я пытаюсь отправить изображение по электронной почте.
@ArsenyLevin Я не получаю никаких ошибок, когда я нажимаю кнопку «Поделиться», я получаю только ViewPostImeInputStage processPointer 1, затем я получаю средство выбора, в котором я могу выбрать приложение для обмена изображением.
@ArsenyLevin Я зарегистрировал Uri contentUri = FileProvider.getUriForFile(getApplication(),getApplication().getPackageName() + ". MyFileProvider", newFile);, и он возвращает shared-files вместо SavedImages
измените xml на <files-path name = "SavedImages" path = "SavedImages/"/>
@ArsenyLevin Я по-прежнему получаю тот же тост, что и моя редакция выше. Uri теперь возвращает content://my.packahe.name.MyFileProvider/SavedImages/test.jpeg
проверьте мою правку ниже. на основе документов здесь: developer.android.com/reference/android/support/v4/content/…
@ArsenyLevin Спасибо, что нашли время ответить, но теперь я получаю эту ошибку -Failed to find configured root that contains /data/data/my.package.name/files/SavedImages/test.jpeg
извините за долгий пинг-понг. попробуйте изменить элемент xml на <external-path, а НЕ на <external-files-path, как я предлагал ранее.
Позвольте нам продолжить обсуждение в чате.




Правильный способ обмена файлами из вашего приложения - это content provider, в частности FileProvider.
Добавьте свой FileProvider в AnroidManifest.xml:
<provider
android:name = "android.support.v4.content.FileProvider"
android:authorities = "${applicationId}.MyFileProvider"
android:exported = "false"
android:grantUriPermissions = "true" >
<meta-data
android:name = "android.support.FILE_PROVIDER_PATHS"
android:resource = "@xml/shared_paths"/>
</provider>
Затем добавьте ресурс, на который имеется ссылка, путем добавления ресурса xml (Android Studio: щелкните правой кнопкой мыши «приложение» -> «Создать» -> «Папка» -> «Сборщик ресурсов XML»).
Затем внутри этой новой папки создайте XML-файл с именем shared_paths.xml (должен соответствовать значению в AndroidManifest.xml). Содержимое для shared_paths.xml:
<?xml version = "1.0" encoding = "utf-8"?>
<paths xmlns:android = "http://schemas.android.com/apk/res/android">
<external-path name = "SavedImages" path = "SavedImages/"/>
</paths>
Обновлено: изменен тип элемента с <files-path ../> на <external-path .. /> на документация.
ВАЖНО: Измените атрибут path в соответствии со своими потребностями.
Наконец, измените код следующим образом:
shareBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent sharingIntent = new Intent(Intent.ACTION_SEND);
Uri screenshotUri = FileProvider.getUriForFile(context, context.getPackageName() + ".MyFileProvider", new File(directoryToStore, filename))
//the Uri above - file:///storage/emulated/0/Android/data/myPackageName/files/SavedImages/test.jpeg
sharingIntent.setType("image/jpeg");
sharingIntent.putExtra(Intent.EXTRA_STREAM, screenshotUri);
sharingIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Intent chooserIntent = Intent.createChooser(sharingIntent, "Share image using");
chooserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(chooserIntent);
}
});
Если вы сохраняете свой файл в общедоступной папке мультимедиа во внутренней памяти (например, для изображений /storage/emulated/0/DCIM/my.package.name/SavedImages/myImage.jpg), вы также можете поделиться файлом через его content- media uri (для содержимого изображения: // media / external / images / media / 4711).
Таким образом, вам не понадобится собственный FileProvider, но файл будет доступен всем.
С помощью lcoal FileProvider в каталогах личных данных приложений файл доступен только через ваше приложение и будет удален, если вы удалите приложение.
цель Contentprovider состоит в том, чтобы вы могли читать контент из любого uri как inputStream (т.е. форма «uri =« https://stackoverflow.com/questions/ ... »,« file: /// storage / emulated / 0 / ... »,« ftp :. ... "," content: ... "), если для этого есть поставщик.
с api 25 Google требует, чтобы uri-файлы "file: ..." больше не допускались
Спасибо за ваш ответ, я хочу, чтобы файлы оставались конфиденциальными, и я хочу, чтобы они были удалены после удаления приложения. Я хочу, чтобы мое приложение могло получить доступ к файлам, если только я не хочу делиться файлами из приложения y.
для личных файлов вам понадобится поставщик контента или поставщик документов
Спасибо. Мой минимальный SDK - 16, должен ли я проверить, является ли SDK 19 <, а затем выполнить его старым способом, используя file://?
это зависит от ваших клиентских приложений, получающих изображение. некоторые могут обрабатывать file: // некоторые могут обрабатывать content: //, а некоторые могут обрабатывать и то, и другое. используйте github.com/k3b/intent-intercept и github.com/k3b/ContentProviderHelper для анализа намерений и контент-провайдера. мой imageapp может обрабатывать оба и скоро получит настройку конфигурации, чтобы открыть file: // или content: //, где по умолчанию будет content: //
и это только усложняется .. Спасибо, что нашли время ответить на мой вопрос.
Вчера вечером я начал думать о вашем последнем комментарии. К концу месяца все приложения должны быть ориентированы на 24+, так что не означает ли это, что с конца месяца большинство приложений смогут обрабатывать content://? В этом был смысл принуждения разработчиков приложений ориентироваться на 24+ в первую очередь из соображений безопасности?
если все отправители-получатели запрограммированы вами, то да. если вы используете приложения, которые последний раз изменялись 4 года назад, это означает, что нет. в случае сомнений следует использовать «content:». после переосмысления этой темы: мое приложение в настоящее время использует отправку / просмотр через «файл», и я внесу несовместимое изменение в «content:» без страницы конфигурации. это означает меньше программирования, меньше элементов графического интерфейса меньше пользовательской документации.
Можете ли вы проверить свой logcat и увидеть, какие ошибки / stacktrace вы получаете?