android.os.FileUriExposedException: файл: ///storage/emulated/0/test.txt, доступный за пределами приложения через Intent.getData ()

Приложение падает, когда я пытаюсь открыть файл. Работает ниже Android Nougat, но на Android Nougat вылетает. Вылетает только при попытке открыть файл с SD-карты, а не из системного раздела. Некоторая проблема с разрешением?

Образец кода:

File file = new File("/storage/emulated/0/test.txt");
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.fromFile(file), "text/*");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent); // Crashes on this line

Журнал:

android.os.FileUriExposedException: файл: ///storage/emulated/0/test.txt, доступный за пределами приложения через Intent.getData ()

Редактировать:

Ориентируясь на Android Nougat,file:// URI больше не разрешены. Мы должны использоватьcontent:// URI вместо этого. Тем не менее, мое приложение должно открывать файлы в корневых каталогах. Есть идеи?

 Binesh Kumar08 окт. 2018 г., 06:38
попробуй это, маленький и идеальный кодstackoverflow.com/a/52695444/4997704
 jimbo1qaz17 сент. 2018 г., 13:36
Я чувствую, что это была ошибка, которая делает жизнь разработчиков ненужной. Необходимость связать «FileProvider» и «полномочия» с каждым приложением, похоже на шаблон Enterprisey. Необходимость добавлять флаг к каждому намерению файла кажется неудобным и, возможно, ненужным. Нарушение элегантного понятия «дорожки» неприятно. И в чем выгода? Выборочное предоставление доступа к хранилищу приложениям (хотя большинство приложений имеют полный доступ к SDCard, особенно те, которые работают с файлами)?

Ответы на вопрос(19)

https://stackoverflow.com/a/38858040/395097 этот ответ завершен.

Ответ на этот вопрос - у вас уже есть приложение с таргетингом ниже 24, и теперь вы переходите на targetSDKVersion> = 24.

В Android N изменяется только файл, доступный стороннему приложению. (Не так, как раньше). Поэтому меняйте только те места, где вы делите путь, с помощью стороннего приложения (Камера в моем случае)

В нашем приложении мы отправляли URI в приложение Camera, в этом месте мы ожидаем, что приложение Camera сохранит захваченное изображение.

Для Android N мы генерируем новый Content: // URL-адрес на основе URL, указывающий на файл.Мы генерируем обычный путь к файлу на основе API для того же (используя более старый метод).

Теперь у нас есть 2 разных URI для одного файла. # 1 используется совместно с приложением Camera. Если цель камеры - успех, мы можем получить доступ к изображению из # 2.

Надеюсь это поможет.

 IgniteCoders09 мая 2018 г., 20:12
Вы ссылаетесь на уже опубликованный здесь ответ, если вам необходимо его заполнить, прокомментируйте ответ, плз.
 Aram10 мая 2018 г., 08:46
@IgniteCoders Как я ясно упомянул в сообщении, мой ответ охватывает связанный вариант использования.

Xamarin.Android

Замечания: ПутьXML / provider_paths.xml (.axml) не удалось решить даже послеXML папка подРесурсы (может быть, его можно поместить в существующее место, напримерЦенности, не пытался), поэтому я прибегнул к этому, который работает на данный момент. Тестирование показало, что его нужно вызывать только один раз за запуск приложения (что имеет смысл, поскольку оно меняет рабочее состояние виртуальной машины хоста).

Замечания: XML должен быть написан с заглавной буквы, такРесурсы / Xml / provider_paths.xml

Java.Lang.ClassLoader cl = _this.Context.ClassLoader;
Java.Lang.Class strictMode = cl.LoadClass("android.os.StrictMode");                
System.IntPtr ptrStrictMode = JNIEnv.FindClass("android/os/StrictMode");
var method = JNIEnv.GetStaticMethodID(ptrStrictMode, "disableDeathOnFileUriExposure", "()V");                
JNIEnv.CallStaticVoidMethod(strictMode.Handle, method);

Если ваше приложение предназначено для API 24+, и вы все еще хотите / должны использовать file: // намерения, вы можете использовать хакерский способ отключить проверку во время выполнения:

if(Build.VERSION.SDK_INT>=24){
   try{
      Method m = StrictMode.class.getMethod("disableDeathOnFileUriExposure");
      m.invoke(null);
   }catch(Exception e){
      e.printStackTrace();
   }
}

методStrictMode.disableDeathOnFileUriExposure скрыто и задокументировано как:

/**
* Used by lame internal apps that haven't done the hard work to get
* themselves off file:// Uris yet.
*/

Проблема заключается в том, что мое приложение не хромое, а скорее не хочет наносить вред при использовании контента: // намерения, которые не понятны многим приложениям. Например, открытие mp3-файла со схемой content: // предлагает гораздо меньше приложений, чем при открытии того же поверх схемы file: //. Я не хочу платить за недостатки дизайна Google, ограничивая функциональность моего приложения.

Google хочет, чтобы разработчики использовали схему контента, но система не подготовлена ​​к этому, поскольку приложения создавались годами для использования файлов, а не «контента», файлы можно редактировать и сохранять обратно, тогда как файлы, обслуживаемые по схеме контента, не могут (могут Oни?).

 Gonzalo Ledezma Torres07 авг. 2017 г., 17:40
Работает и на Android 8, протестировано на Huawei Nexus 6P.
 mrid18 нояб. 2017 г., 10:59
куда добавить этот код?
 OferR30 окт. 2018 г., 21:38
@Eli, а как насчет Android 9? Спасибо...
 Anton Kizema29 мая 2017 г., 16:18
Работает на Android 7. Спасибо
 Jenix14 окт. 2018 г., 19:29
На самом деле мне не нужно обнаруживать какие-либо нарушения с StrictMode. Все, что мне нужно, это просто отключить проверку времени выполнения для file: // намерения. Так что, в этом случае, могу ли я оставитьStrictMode строки в релизных версиях? Просто чтобы быть уверенным, я использую File Provider почти всегда, но недавно я столкнулся с некоторыми исключениями.
 Maria18 февр. 2018 г., 14:35
Кто-нибудь может подтвердить, что он работает в производстве? (после развертывания в магазине приложений)
 Mohit Singh11 нояб. 2017 г., 14:36
Работает на Android 8 Oreo - протестировано на Pixel 2
 Jay Patel06 мар. 2018 г., 20:43
Работает на 7.0, но выдает ошибку в oreo.
 Jenix14 окт. 2018 г., 20:19
@ CommonsWare Ах, теперь все ясно! Большое спасибо.
 Chinthaka Devinda21 февр. 2018 г., 11:51
@PointerNull Спасибо за решение отлично работает (Nexus 5, Samsung J7, Meizu MX4)
 Biswatma09 июл. 2018 г., 14:34
работает нормально на 8.1
 Ali Obeid29 окт. 2017 г., 22:03
Я добавил это в основной метод onCreate, не работал на A5 2017 (Android 7.0) ..
 FindOutIslamNow05 янв. 2019 г., 07:21
На пироге больше не будет работатьdeveloper.android.com/about/versions/pie/...
 Pointer Null20 нояб. 2017 г., 13:40
Application.onCreate
 CommonsWare14 окт. 2018 г., 19:39
@Jenix: эта документация вводит в заблуждение.StrictMode являетсявсегда включен. Отличается то, за чем вы следите и какое наказание вы применяете. Лично я рекомендую отключить штрафы за сбои для версий выпуска, хотя и использовать их для отладочных сборок, чтобы вы могли отловить ошибки в своем тестировании.
 REJH20 авг. 2018 г., 17:46
Оооо ... это все еще работает на пирог? :)
 xroche09 апр. 2017 г., 15:46
Большое спасибо @ pointer-null! Ваш взлом может иметьрешил проблемы у моих пользователей была Android-версия HTTrack, после нескольких часов безуспешных копаний в документации API. Правда в том, что разрешение одного URI через FLAG_GRANT_READ_URI_PERMISSION просто бесполезно, когда у вас есть файловая иерархия для просмотра внизу (т.е. зеркальный веб-сайт)
 Jenix14 окт. 2018 г., 19:29
@CommonsWare После прочтения вашего комментария я самостоятельно настроил правила виртуальной машины, и все заработало как положено! Спасибо! Но официальная документация гласит: «... поэтому никогда не оставляйте StrictMode включенным в приложениях, распространяемых в Google Play» вdeveloper.android.com/reference/android/os/...
 hungtdo31 окт. 2017 г., 05:23
Android 7.0 (S8 +) и Эмулятор 8.0 работают. Спасибо
 Pointer Null06 янв. 2019 г., 11:23
Протестировано, работает на Android 9.
 Eli22 апр. 2018 г., 14:26
Я подтверждаю, что это работает на производстве (у меня более 500 000 пользователей), в настоящее время версия 8.1 является самой высокой версией, и она работает на нем.
 CommonsWare25 февр. 2017 г., 18:13
msgstr "в то время как файлы, обслуживаемые по схеме содержимого, не могут быть (могут?) - конечно, если у вас есть доступ для записи к контенту.ContentResolver имеет обаopenInputStream() а такжеopenOutputStream(), Менее хакерский способ сделать это простонастроить правила виртуальной машины самостоятельнои не включайтеfile Uri править.
 Matt W26 мая 2017 г., 01:11
Именно так. Это сложная работа, когда вы создали все свое приложение, а затем выясните, после того как нацелены на все 25 методов вашей камеры. Это работает для меня, пока у меня не будет времени, чтобы сделать это правильно.
 soynerdito24 июл. 2017 г., 22:50
Спасибо, это сработало. Правильно или нет, я не знаю. Но спасибо, это сработало. Будет искать более надежное решение в будущем, но вы сэкономите мне много времени.

Использование fileProvider - путь. Но вы можете использовать этот простой обходной путь:

ПРЕДУПРЕЖДЕНИЕ: Это будет исправлено в следующем выпуске Android -https://issuetracker.google.com/issues/37122890#comment4

заменить:

startActivity(intent);

от

startActivity(Intent.createChooser(intent, "Your title"));
 Diljeet25 апр. 2017 г., 09:28
Этот работает, но не будет работать в будущих версиях Android.
 Pointer Null24 февр. 2017 г., 12:31
Вскоре Google выберет тот, кто выберет тот же чек. Это не решение.
 ArMo 37209 апр. 2018 г., 08:25
это не работает на Android 8 и выше

Просто вставьте приведенный ниже код в активностьonCreate().

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); 
StrictMode.setVmPolicy(builder.build());

Он будет игнорировать воздействие URI.

Удачного кодирования :-)

 James F15 апр. 2019 г., 20:47
Каковы недостатки этого?

В моем случае я избавился от исключения, заменивSetDataAndType простоSetData.

Для загрузки pdf с сервера добавьте приведенный ниже код в свой класс обслуживания. Надеюсь, это полезно для вас.

File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName + ".pdf");
    intent = new Intent(Intent.ACTION_VIEW);
    //Log.e("pathOpen", file.getPath());

    Uri contentUri;
    contentUri = Uri.fromFile(file);
    intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);

    if (Build.VERSION.SDK_INT >= 24) {

        Uri apkURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".provider", file);
        intent.setDataAndType(apkURI, "application/pdf");
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

    } else {

        intent.setDataAndType(contentUri, "application/pdf");
    }

И да, не забудьте добавить разрешения и провайдера в манифест.

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

<application

<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>
 Bhoomika Chauhan16 апр. 2019 г., 13:04
@ Heisenberg, пожалуйста, отсылайте сообщение Рахула Упадхьяя с URL:stackoverflow.com/questions/38555301/...
 Heisenberg30 нояб. 2018 г., 13:35
что такое@xml/provider_paths ?

Просто вставьте приведенный ниже код в активность onCreate ()

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder(); StrictMode.setVmPolicy(builder.build());

Это будет игнорировать воздействие URI

 saksham18 апр. 2018 г., 12:16
это одно из решений, но не стандартное. Тем не менее, люди, которые отклонили ответы, ошибочны, так как это также рабочий код с рабочим решением.

Я использовал ответ Палаша, приведенный выше, но он был несколько неполным, я должен был дать разрешение, как это

Intent intent = new Intent(Intent.ACTION_VIEW);
    Uri uri;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        uri = FileProvider.getUriForFile(this, getPackageName() + ".provider", new File(path));

        List<ResolveInfo> resInfoList = getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resInfoList) {
            String packageName = resolveInfo.activityInfo.packageName;
            grantUriPermission(packageName, uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
    }else {
        uri = Uri.fromFile(new File(path));
    }

    intent.setDataAndType(uri, "application/vnd.android.package-archive");

    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

    startActivity(intent);

Помимо решения с использованиемFileProviderЕсть еще один способ обойти это. Проще говоря

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
StrictMode.setVmPolicy(builder.build());

вApplication.onCreate(), Таким образом, виртуальная машина игнорирует файлURI экспозиция.

метод

builder.detectFileUriExposure()

включает проверку экспозиции файла, что также является поведением по умолчанию, если мы не настроили VmPolicy.

Я столкнулся с проблемой, что если я используюcontent:// URI чтобы отправить что-то, некоторые приложения просто не могут этого понять. И понижениеtarget SDK версия не допускается. В этом случае мое решение полезно.

Обновить:

Как упомянуто в комментарии, StrictMode является диагностическим инструментом и не должен использоваться для этой проблемы. Когда я опубликовал этот ответ год назад, многие приложения могут получать только файлы Uris. Они просто вылетали, когда я пытался отправить им FileProvider uri. Сейчас это исправлено в большинстве приложений, поэтому мы должны использовать решение FileProvider.

 hqzxzwb19 июл. 2017 г., 20:31
@LaurynasG В API от 18 до 23 по умолчанию Android не проверяет уязвимость файла uri. Вызов этого метода включает эту проверку. Начиная с API 24, android делает эту проверку по умолчанию. Но мы можем отключить его, установив новыйVmPolicy.
 CKP7822 нояб. 2017 г., 18:38
Есть ли другой шаг, необходимый для этого, чтобы работать? Не работает, так как он стоит на моем Moto G под управлением Android 7.0.
 Srihari Karanth10 дек. 2018 г., 08:41
Это способ облегчить подход к выбранному ответу. Создание файла не является проблемой, он все еще может быть старым подходом к передаче файла на внешний проигрыватель, который создавал проблему.
 Shaode Lu18 дек. 2017 г., 09:57
такой замечательный ответ, который решает мою путаницу, это кажется таким бесцельным, но таким важным!
 Ali Obeid29 окт. 2017 г., 22:37
Нет, не работал с моим телефоном Android 7.0
 Adil H. Raza26 апр. 2018 г., 13:09
Если кто-то приходит со стороны xamarin android и имеет эту проблему с xlabs IMediaPicker, решение состоит в том, чтобы просто добавить эти две строки перед base.OnCreate (bundle);
 Dave Rincon24 июл. 2019 г., 18:54
у меня работает, просто измените каталог на context.getExternalCacheDir ()
 Imene Noomene04 янв. 2018 г., 16:02
Как это может решить эту проблему, однако, StrictMode это диагностические инструменты, которые должны быть включены в режиме разработчика, а не в режиме выпуска ???
 Jon05 янв. 2018 г., 20:08
@ImeneNoomene Я полностью с тобой в твоем негодовании. Вы правы, это диагностический инструмент, или, по крайней мере, это было давным-давно, когда я добавил его в свои проекты. Это супер расстраивает!StrictMode.enableDefaults();, который я запускаю только в своих сборках разработки, предотвращает этот сбой - так что теперь у меня есть производственное приложение, которое вылетает, но не вылетает в процессе разработки. Таким образом, включение диагностического инструмента здесь скрывает серьезную проблему. Спасибо @hqzxzwb за помощь в разъяснении этого.
 hqzxzwb04 янв. 2018 г., 16:54
@ImeneNoomene На самом деле мы отключаем StrictMode здесь. Представляется разумным, что StrictMode не следует включать в режиме выпуска, но на самом деле Android включает некоторые параметры StrictMode по умолчанию независимо от режима отладки или режима выпуска. Но так или иначе, этот ответ должен был стать обходным решением, когда некоторые целевые приложения не были подготовлены к получению контента. Теперь, когда в большинство приложений добавлена ​​поддержка контента Uris, мы должны использовать шаблон FileProvider.

ЕслиtargetSdkVersion выше чем24, затемFileProvider используется для предоставления доступа.

Создайте файл XML (путь: res \ 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>


Добавитьпоставщик вAndroidManifest.xml

    <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>

а такжезамещать

Uri uri = Uri.fromFile(fileImagePath);

в

Uri uri = FileProvider.getUriForFile(MainActivity.this, BuildConfig.APPLICATION_ID + ".provider",fileImagePath);

Редактировать: Пока вы включаете URI сIntent не забудьте добавить строку ниже:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

и ты в порядке. Надеюсь, поможет.

 Vikash Sharma23 мая 2018 г., 10:40
@FelipeCastilhos решил это. Только что добавил Uri Permission для намерения в addFlags ()
 Pankaj Lilan22 окт. 2018 г., 11:09
@box Это из-за того, что ваш файл будет создавать еще одну копию выбранного вами изображения. Так что оба разрешения необходимы.
 Jagdish Bhavsar08 мар. 2018 г., 14:07
Выдает исключение java.lang.IllegalArgumentException: не удалось найти настроенный корень, который содержит / Мой путь к файлу /storage/emulated/0/GSMManage_DCIM/Documents/Device7298file_74.pdf. не могли бы вы помочь?
 mehmoodnisar12523 янв. 2019 г., 10:36
вышеуказанный метод работает, когда я добавляю флаг разрешения intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
 Vikash Sharma21 мая 2018 г., 20:13
@FelipeCastilhos я получаю ту же ошибку, что и вы, пустой экран, вы можете мне помочь.
 user1234635222 июл. 2019 г., 14:14
Может кто-нибудь сказать мне, почему он вызывает исключениеjava.lang.IllegalArgumentException: Failed to find configured root
 Kedar Tendolkar22 июн. 2018 г., 12:28
Работаю на 8.1. Благодарю.
 Felipe Castilhos07 мар. 2018 г., 19:14
@PankajLilan, я сделал именно то, что ты сказал. Но каждый раз, когда я открываю свой PDF-файл в другом приложении, он отображается пустым (его сохранение правильно). Нужно ли мне редактировать XML? Я уже добавил FLAG_GRANT_READ_URI_PERMISSION;
 Felipe Castilhos22 мая 2018 г., 16:20
@VikashSharma Привет, как я уже говорил выше, я добавил разрешение к неправильному умыслу. Проверьте, правильно ли вы все настраиваете
 paakjis09 авг. 2019 г., 13:03
ЭТО ПРАВО ЗДЕСЬ ОТВЕТ! Все остальные не правы. Это свежо и работает с новым кодом!
 Pankaj Lilan23 янв. 2019 г., 12:31
@ mehmoodnisar125 Спасибо за ваше предложение. Я отредактирую свой ответ тем же.
 Maksim Kniazev17 нояб. 2017 г., 05:27
@Pankaj Лилан не бери в голову, все хорошо, я сделал ошибку на .provider. Работает хорошо сейчас. Спасибо за продолжение
 Pankaj Lilan16 нояб. 2017 г., 12:47
@MakimKniazev Можете ли вы кратко описать свою ошибку? Так что я могу вам помочь.
 box22 окт. 2018 г., 10:54
Не знаю почему, но мне пришлось добавить разрешения READ и WRITE: target.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION) target.addFlags (Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
 Felipe Castilhos07 мар. 2018 г., 20:10
Моя ошибка, я добавил разрешение к неправильному умыслу. Это самый лучший и самый простой правильный ответ. Спасибо!

добавить эти две строки в onCreate

StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
    StrictMode.setVmPolicy(builder.build());

Поделиться методом

File dir = new File(Environment.getExternalStorageDirectory(), "ColorStory");

                        File imgFile = new File(dir, "0.png");

                        Intent sendIntent = new Intent(Intent.ACTION_VIEW);
                        sendIntent.setType("image/*");
                        sendIntent.setAction(Intent.ACTION_SEND);
                        sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + imgFile));
                        sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        startActivity(Intent.createChooser(sendIntent, "Share images..."));

Если твойtargetSdkVersion 24 или выше,вы не можете использоватьfile: Uri значения вIntents на устройствах Android 7.0+.

Ваш выбор:

Оставь свойtargetSdkVersion до 23 или ниже, или

Поместите ваш контент во внутреннюю память, затемиспользованиеFileProvider сделать его доступным для других приложений

Например:

Intent i=new Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(this, AUTHORITY, f));

i.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
startActivity(i);

(отэтот пример проекта)

 Thomas Vos05 июл. 2016 г., 14:56
Спасибо за ответ. Что происходит, когда я использую это с файлами на/system раздел? Каждое приложение должно иметь доступ к этому разделу без рута.
 Daniel Benedykt28 апр. 2019 г., 21:14
Возвращение к более старой версии API - это не решение, а временное исправление.
 lostintranslation17 июн. 2017 г., 01:13
@CommonsWare спасибо за ваше время. И я согласен, это то, что меня беспокоило в первую очередь. Как определить, какой тип URI отправлять в намерение? Можно ли охватить все случаи?
 CommonsWare16 июн. 2017 г., 23:54
@lostintranslation: "Это означает, что на старых ОС работает приложение, ориентированное на sdk> =, вы все еще можете использовать файл Uris?" -- правильный. «в котором намерение видео не работает с содержимым URI» - это не имеет ничего общего с версией ОС Android и всем, что связано с установленными пользователем приложениями.
 CommonsWare29 июн. 2017 г., 16:34
@rommex: я не знаю, что можно назвать «самым важным». Например, пользователям, которые работают в режиме разделенного экрана или на многооконных устройствах произвольной формы (Chromebook, Samsung DeX), сообщат, что ваше приложение может не работать с многооконным режимом. Важно это или нет, решать только вам.
 lostintranslation17 июн. 2017 г., 00:17
@CommonsWare «все, что связано с приложениями, которые установил пользователь» - своего рода. Нативное приложение камеры Samsung S5 под управлением Marshmallow не работает с URI контента из моего приложения, созданного с targetSDK> 24, только с URI файла. Я должен проверить версию ОС и использовать URI файла, если <N, и URI содержимого, если> = N.
 CommonsWare05 июл. 2016 г., 18:17
@SuperThomasLab: простое «решение» - броситьtargetSdkVersion теперь. Я не коренной тип парня, но я немного удивлен, что вам не нужен был ваш собственныйContentProvider прежде, чтобы позволить произвольным приложениям читать файлы, которые они не могут обычно читать. Я предположил, что приложения корневого браузера имелиContentProvider что под одеялом использовалиsu/sudo/ что угодно, чтобы транслировать контент на то, с чем они запускалиACTION_VIEW.
 rommex29 июн. 2017 г., 16:06
Каковы наиболее важные недостатки снижения targetSdkVersion до 23? Thnx
 Jenix11 окт. 2018 г., 02:53
@CommonsWare Могу ли я позвонитьstartActivity() из рабочего потока? Я знаю, что это другой вопрос, и уже задавал и отвечал несколько раз на stackoverflow, и очень жаль спрашивать здесь, но я немного запутался. Я видел, как несколько человек сказали, что это вызвало исключение на некоторых устройствах, в то время как многие сказали, что можно вызывать из рабочего потока, потому чтоstartActivity() Сам внутренне выполняется в потоке пользовательского интерфейса. Я также проверил себя, и он работал нормально. Но из-за комментариев других людей я всегда используюrunOnUiThread(), Это очень избыточно и раздражает. Я хочу знать ваши мысли по этому поводу.
 Thomas Vos05 июл. 2016 г., 18:20
Да, другие приложения (и мое приложение) используют команды терминала для загрузки данных файла. (С правами su) Вот почему я все еще ищу правильный способ получить фактический путь из Uri контента, поэтому я могу использовать его в терминальной команде :)
 lostintranslation16 июн. 2017 г., 23:47
@CommonsWare, поэтому ключевым моментом здесь, как я понимаю, является цель sdk> = 24 И работающая ОС 7.0+. Это означает, что на старых ОС работает приложение, ориентированное на sdk> =, вы все еще можете использовать файл uris? Я спрашиваю, потому что у меня есть некоторые старые устройства под управлением 5.0 и 6.0, в которых видео не работает с URI контента.
 Thomas Vos05 июл. 2016 г., 18:11
Все еще думаю, как я собираюсь решить эту проблему .. Приложение, которое я обновляю с поддержкой Android N, является корневым браузером. Но теперь вы не можете больше открывать файлы в корневых каталогах. (/data, /system), из-за этого "хорошего изменения".
 CommonsWare05 июл. 2016 г., 18:33
@SuperThomasLab: «Вот почему я все еще ищу правильный способ получить реальный путь из контента Uri» - для вашего собственногоContentProviderвы определяетеUri состав. Итак, используйте свои собственные права доступа (так как это необходимо в любом случае), и имейтеgetPath() изUri верните ваш фактический путь к файловой системе. Или, если вы обеспокоены утечкой информации о файловой системе, сохраните простоеLRUCache сопоставление некоторого строкового токена (например, UUID) с путем к файловой системе и использование токена вUri.
 CommonsWare17 июн. 2017 г., 01:15
@lostintranslation: "Как можно определить, какой тип URI отправить в намерение?" - если вы используетеACTION_IMAGE_CAPTUREВообще говоря, ты облажался. В любое времяUri передается через дополнительный, а не фасет данныхIntent (например, способACTION_VIEW работает), нет никакой фильтрации на схеме или чем-либо еще, и поэтому ни у вас, ни у получателя нет никакого способа договориться или определить взаимоприемлемую схему. :-(
 Jenix11 окт. 2018 г., 12:59
Просто хотел задать вам простой вопрос «да» или «нет», потому что он уже был задан, но все же мне было любопытно, что бы подумал мастер андроида. Спасибо!
 CommonsWare05 июл. 2016 г., 15:11
@SuperThomasLab: я бы не рассчитывал на все/system быть читаемым во всем мире. При этом, я предполагаю, что вы все равно получите это исключение. Я подозреваю, что они просто проверяют схему и не пытаются определить, действительно ли файл доступен для чтения. Тем не мение,FileProvider не поможет вам, так как вы не можете научить его служить от/system, Вы могли бысоздать собственную стратегию для моегоStreamProviderили сверните свое собственноеContentProvider, чтобы обойти проблему.
 CommonsWare11 окт. 2018 г., 12:53
@Jenix: я не помню, чтобы пытался начать деятельность из фонового потока, и определенно не в контексте этого вопроса или ответа. Сожалею!
 CommonsWare17 июн. 2017 г., 00:22
@lostintranslation: «Приложение Samsung S5 для встроенной камеры, работающее с Marshmallow, не работает с URI контента из моего приложения, созданного с targetSDK> 24, только с URI файла» - да, но имейте в виду, что вполне возможно, что устройство пользователя на более новой версии В версиях ОС все еще есть приложение с глючной камерой. Или, что пользователь устанавливает стороннее приложение камеры, которое отстой. Ваш чек не плохой, но не думайте, что он охватит все случаи.

Мое решение было «Uri.parse» путь к файлу в виде строки, а не с помощью Uri.fromFile ().

String storage = Environment.getExternalStorageDirectory().toString() + "/test.txt";
File file = new File(storage);
Uri uri;
if (Build.VERSION.SDK_INT < 24) {
    uri = Uri.fromFile(file);
} else {
    uri = Uri.parse(file.getPath()); // My work-around for new SDKs, causes ActivityNotFoundException in API 10.
}
Intent viewFile = new Intent(Intent.ACTION_VIEW);
viewFile.setDataAndType(uri, "text/plain");
startActivity(viewFile);

Кажется, что fromFile () использует указатель файла, который, я полагаю, может быть небезопасным, когда адреса памяти открыты для всех приложений. Но путь к файлу String никогда никому не причиняет вреда, поэтому он работает без исключения FileUriExposedException.

Проверено на уровнях API от 9 до 26! Не требует ни FileProvider, ни библиотеки поддержки Android.

 Serdar Samancıoğlu14 мар. 2019 г., 15:11
Это не исключение, но оно также не может отправить файл в соответствующее приложение. Так что, не работал для меня.
 Xmister24 янв. 2019 г., 10:53
Примечание о том, почему это действительно работает: проблема заключается не в указателе файла, а в том, что исключение возникает только в том случае, если у вас есть путь с «file: //», которому автоматически предшествует fromFile, а не parse ,
 Dale20 нояб. 2018 г., 17:52
Хотел бы я увидеть это первым. Я не доказал, что это работает для меня, но это гораздо менее громоздко, чем FileProvider.

Сначала вам нужно добавить провайдера в ваш AndroidManifest

  <application
    ...>
    <activity>
    .... 
    </activity>
    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="com.your.package.fileProvider"
        android:grantUriPermissions="true"
        android:exported="false">
        <meta-data
            android:name="android.support.FILE_PROVIDER_PATHS"
            android:resource="@xml/file_paths" />
    </provider>
  </application>

Теперь создайте файл в папке ресурсов xml (если вы используете Android Studio, вы можете нажать Alt + Enter после выделения file_paths и выбрать вариант создания ресурса xml)

Далее в файле file_paths введите

<?xml version="1.0" encoding="utf-8"?>
<paths>
  <external-path path="Android/data/com.your.package/" name="files_root" />
  <external-path path="." name="external_storage_root" />
</paths>

Этот пример для внешнего пути вы можете ссылатьсяВот для большего количества вариантов. Это позволит вам обмениваться файлами, которые находятся в этой папке и ее подпапке.

Теперь осталось создать намерение следующим образом:

    MimeTypeMap mime = MimeTypeMap.getSingleton();
    String ext = newFile.getName().substring(newFile.getName().lastIndexOf(".") + 1);
    String type = mime.getMimeTypeFromExtension(ext);
    try {
        Intent intent = new Intent();
        intent.setAction(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(getContext(), "com.your.package.fileProvider", newFile);
            intent.setDataAndType(contentUri, type);
        } else {
            intent.setDataAndType(Uri.fromFile(newFile), type);
        }
        startActivityForResult(intent, ACTIVITY_VIEW_ATTACHMENT);
    } catch (ActivityNotFoundException anfe) {
        Toast.makeText(getContext(), "No activity found to open this attachment.", Toast.LENGTH_LONG).show();
    }

РЕДАКТИРОВАТЬ: Я добавил корневую папку SD-карты в file_paths. Я проверил этот код, и он работает.

 MScott30 июн. 2017 г., 22:07
Я получил следующее исключение:java.lang.IllegalArgumentException: Failed to find configured root ... и единственное, что сработало<files-path path="." name="files_root" /> в файле XML вместо<external-path ..., Мой файл был сохранен во внутреннем хранилище.
 praneetloke31 дек. 2016 г., 17:57
Спасибо тебе за это. Я также хочу, чтобы вы знали, что есть лучший способ получить расширение файла.String extension = android.webkit.MimeTypeMap.getFileExtensionFromUrl(Uri.fromFile(file).toString());   Кроме того, я рекомендую всем, кто ищет ответы, прочитатьFileProvider сначала и поймите, с чем вы имеете дело здесь с правами доступа к файлам в Android N и выше. Существуют варианты для внутреннего хранилища и внешнего хранилища, а также для обычного пути к файлу и пути кеша.

Ответ @Pkosta - один из способов сделать это.

Помимо использованияFileProviderВы также можете вставить файл вMediaStore (особенно для файлов изображений и видео), поскольку файлы в MediaStore доступны для каждого приложения:

MediaStore в первую очередь нацелен на MIME-типы видео, аудио и изображений, однако, начиная с Android 3.0 (уровень API 11), он также может хранить не мультимедийные типы (для получения дополнительной информации см. MediaStore.Files). Файлы могут быть вставлены в MediaStore с помощью scanFile (), после чего URI content: //, пригодный для обмена, передается в предоставленный обратный вызов onScanCompleted (). Обратите внимание, что после добавления в систему MediaStore содержимое доступно любому приложению на устройстве.

Например, вы можете вставить видеофайл в MediaStore следующим образом:

ContentValues values = new ContentValues();
values.put(MediaStore.Video.Media.DATA, videoFilePath);
Uri contentUri = context.getContentResolver().insert(
      MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);

contentUri какcontent://media/external/video/media/183473, который может быть передан непосредственноIntent.putExtra:

intent.setType("video/*");
intent.putExtra(Intent.EXTRA_STREAM, contentUri);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
activity.startActivity(intent);

Это работает для меня, и избавить от хлопот использованияFileProvider.

@palash k ответ правильный и работал для файлов внутреннего хранилища, но в моем случае я хочу открыть файлы и из внешнего хранилища, мое приложение зависло при открытии файла из внешнего хранилища, такого как sdcard и usb, но мне удается решить проблему, изменив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 path="Android/data/${applicationId}/" name="files_root" />

<root-path
    name="root"
    path="/" />

</paths>

и в классе Java (без изменений, как принятый ответ, только небольшое изменение)

Uri uri=FileProvider.getUriForFile(getActivity(), BuildConfig.APPLICATION_ID+".provider", File)

Это поможет мне исправить сбой файлов с внешних хранилищ. Надеюсь, это поможет кому-то, у кого возникла та же проблема, что и у меня :)

 t0m16 мар. 2017 г., 12:23
Где вы узнали о<root-path пожалуйста ? Работает.<external-path path="Android/data/${applicationId}/" name="files_root" /> не имел никакого эффекта для открытых файлов из внешнего хранилища.
 Ramz16 мар. 2017 г., 12:41
Вы также упоминаете внешнее хранилище SD-карты или встроенное хранилище?
 t0m20 мар. 2017 г., 09:36
Извините за неточность. я имел в видуAndroid/data/${applicationId}/ в SDcard.
 artman05 июл. 2018 г., 17:57
это прекрасно работает, спасибо! Единственное, что Android Studio выделяет root-путь как недопустимый тег XML, но его компиляцию и рабочий файл.
 Ramz16 мар. 2017 г., 12:40
я нахожу это из различных результатов поиска, позвольте мне проверить еще раз и вернуться к вам как можно скорее
 s-hunter17 февр. 2018 г., 16:25
Нужно добавить это к цели: intent.addFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);

Я не знаю почему, я сделал все точно так же, как Пкоста (https://stackoverflow.com/a/38858040 ) но получал ошибку:

java.lang.SecurityException: Permission Denial: opening provider redacted from ProcessRecord{redacted} (redacted) that is not ,exported from uid redacted

Я потратил часы на этот вопрос. Виновник? Котлин.

val playIntent = Intent(Intent.ACTION_VIEW, uri)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)

intent был на самом деле установкаgetIntent().addFlags вместо того, чтобы работать на моем недавно объявленном playIntent.

Решение Вопроса

Если твойtargetSdkVersion >= 24тогда мы должны использоватьFileProvider класс для предоставления доступа к определенному файлу или папке, чтобы сделать их доступными для других приложений. Мы создаем наш собственный класс наследованияFileProvider чтобы убедиться, что наш FileProvider не конфликтует с FileProvider, объявленными в импортированных зависимостях, как описаноВот.

Шаги по заменеfile:// URI сcontent:// URI:

Добавьте расширение классаFileProvider

public class GenericFileProvider extends FileProvider {}

Добавить FileProvider<provider> отметить вAndroidManifest.xml под<application> тег. Укажите уникальный авторитет дляandroid:authorities атрибут, чтобы избежать конфликтов, импортированные зависимости могут указывать${applicationId}.provider и другие широко используемые органы.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name=".GenericFileProvider"
            android:authorities="${applicationId}.my.package.name.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>
Затем создайтеprovider_paths.xml файл вres/xml папка. Папка может понадобиться для создания, если она не существует. Содержание файла показано ниже. Он описывает, что мы хотели бы поделиться доступом к внешнему хранилищу в корневой папке(path=".") с именемexternal_files.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

Последний шаг - изменить строку кода ниже в

Uri photoURI = Uri.fromFile(createImageFile());

в

Uri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile());

Редактировать: Если вы используете намерение, чтобы система открыла ваш файл, вам может потребоваться добавить следующую строку кода:

intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);

Пожалуйста, обратитесь, полный код и решение было объясненоВот.

 Saeed Neamati26 июн. 2017 г., 21:32
Вопрос. Я использую это в своем модуле Android, и вопрос в том, добавлю ли я эти строки и конфигурацию в этот модуль, будут ли они повторно использоваться в моих приложениях? Или я должен определить их в каждом приложении?
 user540020629 апр. 2019 г., 08:51
@Spritzig Это файловый объект, которым вы хотите поделиться.
 Denny Weinberg24 дек. 2018 г., 17:19
ТЫ ЧЕЛОВЕК
 Orientos12 мар. 2018 г., 14:05
Могу ли я предоставить папку для сохранения видео в нем с этим решением?
 Waheed Nazir17 июл. 2019 г., 20:01
Спасибо, это работает.
 Murwa29 апр. 2018 г., 05:24
Черт возьми, работает как шарм.
 personne300030 авг. 2016 г., 02:36
Смотрите также объяснения от команды Android здесь:code.google.com/p/android/issues/detail?id=203555#c3
 Thomas Vos10 авг. 2016 г., 17:14
Спасибо за ответы. Я уже читал эту статью некоторое время назад. (См. Комментарий к этой записи), но он не работает в корневых каталогах. Смотрите мой ответ для решения.
 B Porr08 июн. 2019 г., 15:28
Не должно ли это быть:final Uri u = FileProvider.getUriForFile(getBaseContext(),getApplicationContext().getPackageName()+".fileprovider",fp);
 Iman Marashi06 дек. 2016 г., 19:11
Caused by: java.lang.SecurityException: Permission Denial: opening provider android.support.v4.content.FileProvider from ProcessRecord
 Atif Pervaiz10 апр. 2019 г., 11:44
После реализации этого кода у меня возникла эта проблема:stackoverflow.com/questions/55391934/...
 educoutinho17 сент. 2018 г., 01:52
Как показано в связанном коде, в AndroidManifest.xml я использовал это:android:authorities="${applicationId}.provider" и в результате, это:Uri uri = FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", file); и это сработало! Спасибо!
 Nathan O'Kane17 янв. 2019 г., 04:17
Я не могу поверить так много других тем, которые я прочитал, и как долго я пытался именно то, что должно работать, прежде чем я нашел этот комментарий. Спасибо.
 Tito Leiva19 июн. 2018 г., 19:39
Если у вас есть несколько пакетов вBuildConfigс разными суффиксами applicationID вы можете получить права доступа следующим образом:String authority = getContext().getPackageName().replace(BuildConfig.BUILD_TYPE, "my.package.name.provider"); Есть комментарии по этому поводу?
 alorma22 нояб. 2016 г., 16:11
Мне просто нужно было добавить intent.setFlags (Intent.FLAG_GRANT_READ_URI_PERMISSION);
 Violet Giraffe18 июн. 2019 г., 20:28
Не работает, намерение не может быть обработано внешними приложениями, по крайней мереACTION_VIEW не может. Это сработало:stackoverflow.com/a/39619468/634821
 Bhanu Sharma20 дек. 2018 г., 08:45
 Sébastien13 дек. 2017 г., 18:20
@rockhammer Я только что проверил это с Android 5.0, 6.0, 7.1 и 8.1, это работает во всех случаях. Итак(Build.VERSION.SDK_INT > M) состояние бесполезно.
 AbdulMomen عبدالمؤمن18 мар. 2017 г., 17:41
эта статья помогла мнеmedium.com/@ali.muzaffar/...
 Spritzig27 апр. 2019 г., 16:10
Что такоеcreateImageFile() потому что это неизвестно или неопределенно для меня?
 android developer24 нояб. 2016 г., 18:43
Будет ли это работать для всех версий Android или только из API 24?
 Joubert Vasconcelos31 янв. 2019 г., 02:52
Я действительно хочу поцеловать тебя, LOL! Спас мою жизнь.
 Alex11 июл. 2019 г., 10:55
Не нужно создавать GenericFileProvider
 Alireza Noorali17 дек. 2017 г., 14:15
Об этой линииUri photoURI = FileProvider.getUriForFile(context, context.getApplicationContext().getPackageName() + ".my.package.name.provider", createImageFile()); Я хочу скачать файл apk и открыть его, какой должен быть файл uri для установки места назначения?
 Iman Marashi20 нояб. 2017 г., 16:50
 Innocent15 нояб. 2017 г., 14:34
Добавьте строки ниже, если вы используете внутренний кеш для получения URI. <paths xmlns: android = "schemas.android.com/apk/res/android"> <cache-path name =" cache "path =" / "/> </ paths>
 JP Ventura15 янв. 2018 г., 22:56
FileProvider следует расширять, только если вы хотите переопределить любое поведение по умолчанию, в противном случае используйтеandroid:name="android.support.v4.content.FileProvider", Увидетьdeveloper.android.com/reference/android/support/v4/content/...
 Matteo17 мар. 2018 г., 01:43
Обратите внимание, что если вы уже настроили другого провайдера с помощью класса FileProvider, вам все равно нужно расширить свой собственный FileProvider.
 jackz31429 мая 2019 г., 23:00
Поставщик по умолчанию для AndroidXandroid:name="androidx.core.content.FileProvider"
 gary13 июл. 2019 г., 06:24
Я боролся в течение нескольких часов, чтобы найтиandroid:authorities а такжеgetUriForFile«sauthority Имя параметра ДОЛЖНО быть точно таким же. Ответ здесь использует...fileprovider в одном месте и....provider в другом месте, что немного сбило с толку.
 Euridice0124 июл. 2019 г., 06:39
Как получить контекст для FileProvider.getUriForFile ()? Что я должен установить в качестве первого параметра здесь?
 mrid17 нояб. 2017 г., 17:59
это дает мне ошибкуjava.lang.SecurityException: Permission Denial: opening provider....that is not exported..
 IgniteCoders09 мая 2018 г., 21:10
Да, единственная ошибка, которая у меня была, былаintent.setFlags послеintent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 Daniel Reyhanian24 апр. 2019 г., 00:00
гдеres/xml в ксарине?
 Milad Bahmanabadi12 апр. 2019 г., 21:09
спасибо тебеуууууууууу

Ваш ответ на вопрос