Обновление для Android N (оставив исходный ответ ниже и подтвердив, что этот новый подход работает в производстве):
Как вы отметили в своем обновлении, многие модели устройств Huawei (например, KIW-L24, ALE-L21, ALE-L02, PLK-L01 и многие другие) нарушают контракт Android на звонки в ContextCompat#getExternalFilesDirs(String)
, Вместо того, чтобы возвращаться Context#getExternalFilesDir(String)
(то есть запись по умолчанию) в качестве первого объекта в массиве, вместо этого они возвращают первый объект в качестве пути к внешней SD-карте, если таковой имеется.
Нарушение этого контракта на заказ приводит к отказу устройств Huawei с внешними SD-картами IllegalArgumentException
на звонки в FileProvider#getUriForFile(Context, String, File)
за external-files-path
корнеплоды. Хотя существует множество решений, которые вы можете использовать для решения этой проблемы (например, написание FileProvider
реализации), я нашел самый простой подход, чтобы поймать эту проблему и:
- Pre-N: возврат
Uri#fromFile(File)
, который не будет работать с Android N и выше из-заFileUriExposedException
- N: скопируйте файл на свой
cache-path
(примечание: это может ввести ANR, если это сделано в потоке пользовательского интерфейса), а затем вернутьFileProvider#getUriForFile(Context, String, File)
для скопированного файла (то есть, чтобы избежать ошибки в целом)
Код для этого можно найти ниже:
public class ContentUriProvider {
private static final String HUAWEI_MANUFACTURER = "Huawei";
public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) {
if (HUAWEI_MANUFACTURER.equalsIgnoreCase(Build.MANUFACTURER)) {
Log.w(ContentUriProvider.class.getSimpleName(), "Using a Huawei device Increased likelihood of failure...");
try {
return FileProvider.getUriForFile(context, authority, file);
} catch (IllegalArgumentException e) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Log.w(ContentUriProvider.class.getSimpleName(), "Returning Uri.fromFile to avoid Huawei 'external-files-path' bug for pre-N devices", e);
return Uri.fromFile(file);
} else {
Log.w(ContentUriProvider.class.getSimpleName(), "ANR Risk -- Copying the file the location cache to avoid Huawei 'external-files-path' bug for N+ devices", e);
// Note: Periodically clear this cache
final File cacheFolder = new File(context.getCacheDir(), HUAWEI_MANUFACTURER);
final File cacheLocation = new File(cacheFolder, file.getName());
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(file);
out = new FileOutputStream(cacheLocation); // appending output stream
IOUtils.copy(in, out);
Log.i(ContentUriProvider.class.getSimpleName(), "Completed Android N+ Huawei file copy. Attempting to return the cached file");
return FileProvider.getUriForFile(context, authority, cacheLocation);
} catch (IOException e1) {
Log.e(ContentUriProvider.class.getSimpleName(), "Failed to copy the Huawei file. Re-throwing exception", e1);
throw new IllegalArgumentException("Huawei devices are unsupported for Android N", e1);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
}
}
} else {
return FileProvider.getUriForFile(context, authority, file);
}
}
}
Вместе с file_provider_paths.xml
:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="public-files-path" path="." />
<cache-path name="private-cache-path" path="." />
</paths>
Как только вы создали такой класс, замените ваши звонки на:
FileProvider.getUriForFile(Context, String, File)
ContentUriProvider.getUriForFile(Context, String, File)
Честно говоря, я не думаю, что это особенно изящное решение, но оно позволяет нам использовать официально задокументированное поведение Android, не делая ничего слишком радикального (например, написание FileProvider
реализация). Я проверил это в производстве, поэтому я могу подтвердить, что он устраняет эти сбои Huawei. Для меня это был лучший подход, так как я не хотел тратить слишком много времени на устранение недостатков производителя.
Обновление ранее выпущенных устройств Huawei с этой ошибкой, обновленной до Android N:
Это не будет работать с Android N и выше из-за FileUriExposedException
, но мне еще не приходилось сталкиваться с устройством Huawei с этой неправильной конфигурацией на Android N.
public class ContentUriProvider {
private static final String HUAWEI_MANUFACTURER = "Huawei";
public static Uri getUriForFile(@NonNull Context context, @NonNull String authority, @NonNull File file) {
if (HUAWEI_MANUFACTURER.equalsIgnoreCase(Build.MANUFACTURER) && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
Log.w(ContentUriProvider.class.getSimpleName(), "Using a Huawei device on pre-N. Increased likelihood of failure...");
try {
return FileProvider.getUriForFile(context, authority, file);
} catch (IllegalArgumentException e) {
Log.w(ContentUriProvider.class.getSimpleName(), "Returning Uri.fromFile to avoid Huawei 'external-files-path' bug", e);
return Uri.fromFile(file);
}
} else {
return FileProvider.getUriForFile(context, authority, file);
}
}
}
Мне нужно открыть документы или изображения, используя приложение по умолчанию в телефоне Android. поэтому я реализовал следующие коды, и это работает хорошо, но не работает только на Android 7 или выше. Пожалуйста, дайте мне знать, что не так и как исправить.
var documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
var filePath = Path.Combine(documentsPath, fileName);
var bytes = File.ReadAllBytes(filePath);
//Copy the private file's data to the EXTERNAL PUBLIC location
string externalStorageState = global::Android.OS.Environment.ExternalStorageState;
var externalPath = global::Android.OS.Environment.ExternalStorageDirectory.Path + "/" + global::Android.OS.Environment.DirectoryDownloads + "/" + fileName;
File.WriteAllBytes(externalPath, bytes);
Java.IO.File file = new Java.IO.File(externalPath);
file.SetReadable(true);
string application = "";
string extension = Path.GetExtension(filePath);
// get mimeTye
switch (extension.ToLower())
{
case ".txt":
application = "text/plain";
break;
case ".doc":
case ".docx":
application = "application/msword";
break;
case ".pdf":
application = "application/pdf";
break;
case ".xls":
case ".xlsx":
application = "application/vnd.ms-excel";
break;
case ".jpg":
case ".jpeg":
case ".png":
application = "image/jpeg";
break;
default:
application = "*/*";
break;
}
Intent intent = new Intent(Intent.ActionView);
Android.Net.Uri uri = Android.Net.Uri.FromFile(file);
Context context = MainActivity.instance;
if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.N)
{
uri = FileProvider.GetUriForFile(context, context.PackageName + ".fileprovider", file);
intent.SetDataAndType(uri, application);
intent.SetFlags(ActivityFlags.GrantReadUriPermission);
intent.AddFlags(ActivityFlags.NoHistory);
}
else
{
intent.SetDataAndType(uri, application);
intent.SetFlags(ActivityFlags.ClearWhenTaskReset | ActivityFlags.NewTask);
}
context.StartActivity(intent);
Первоначально я не соответствовал версии Android для этой функции, но после того, как я прочитал некоторые вопросы, подобные этому, я добавил его.
Вот мой манифест
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1" package="com.ibase.mtwpublicapp">
<uses-sdk android:minSdkVersion="15" android:targetSdkVersion="26" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<application android:label="OKS" android:largeHeap="true">
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.oks.mobileapp.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
</provider>
<meta-data android:name="com.facebook.sdk.ApplicationId" android:value="@string/facebook_app_id" />
</application>
</manifest>
Когда я тестирую приложение, оно показывает исключение в этой строке.
uri = FileProvider.GetUriForFile(context, context.PackageName + ".fileprovider", file);
вот сообщение об исключении.
{Java.Lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Download/237309880.doc
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw () [0x0000c] in <657aa8fea4454dc898a9e5f379c58734>:0
at Java.Interop.JniEnvironment+StaticMethods.CallStaticObjectMethod (Java.Interop.JniObjectReference type, Java.Interop.JniMethodInfo method, Java.Interop.JniArgumentValue* args) [0x00069] in <54816278eed9488eb28d3597fecd78f8>:0
at Android.Runtime.JNIEnv.CallStaticObjectMethod (System.IntPtr jclass, System.IntPtr jmethod, Android.Runtime.JValue* parms) [0x00000] in /Users/builder/data/lanes/5749/d8c6e504/source/xamarin-android/src/Mono.Android/Android.Runtime/JNIEnv.g.cs:562
at Android.Support.V4.Content.FileProvider.GetUriForFile (Android.Content.Context context, System.String authority, Java.IO.File file) [0x00077] in <e43264129f744fc09346a273ec4f6c48>:0
at FileManagement.Helpers.FileHelper.OpenFileByName (System.String fileName) [0x0022a] in FileManagement/Helpers/FileHelper.cs:141
--- End of managed Java.Lang.IllegalArgumentException stack trace ---
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Download/237309880.doc
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:712)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:401)
at android.support.v7.app.AlertDialog_IDialogInterfaceOnClickListenerImplementor.n_onClick(Native Method)
at android.support.v7.app.AlertDialog_IDialogInterfaceOnClickListenerImplementor.onClick(AlertDialog_IDialogInterfaceOnClickListenerImplementor.java:30)
at android.support.v7.app.AlertController$ButtonHandler.handleMessage(AlertController.java:162)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
}
Вот файл file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="my_images" path="Pictures" />
<external-files-path name="my_movies" path="Movies" />
</paths>
Я думаю, что это может быть связано с FileProvider, который я объявляю в манифесте.
Спасибо за вашу помощь!
При вызове системной камеры для съемки, процесс выглядит следующим образом
() {
Intent takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE);
(takePictureIntent.resolveActivity(getPackageManager()) != ) {
// Создать путь для сохранения картинки
photoFile = ImageUtil.createImageFile();
(photoFile != ) {
photoURI = Uri.fromFile(photoFile);
// Передаем Uri
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, TAKE_PHOTO);
}
}
}
Затем обработайте результаты фотографии в методе onActivityResult.
( requestCode, resultCode, Intent data) {
.onActivityResult(requestCode, resultCode, data);
(requestCode) {
TAKE_PHOTO:
(resultCode == RESULT_OK) {
// Обработка результата фотографирования
processTakePhoto(photoFile.getPath());
}
;
:
;
}
}
Но выяснилось, что он падал непосредственно в системе 7.0, ошибка заключается в следующем.
///storage/emulated//Android/data//files/Picture
s/_140305187933259 exposed beyond app through
ClipData()
Затем я поискал в Интернете и обнаружил, что это photoURI = Uri.fromFile (photoFile); что-то не так с этим способом создания Uri, который недостаточно безопасен. Вам нужно использовать FileProvider для создания Uri.
- Использование FileProvider Tetralogy
- нота
- Третий шаг — создание URI контента для файла.
- Шаг 4 Поделиться URI контента
- Introduction to FileProvider
- FileProvider usage
- Ответ
- Data/user/0/com. psh. mTest/app_imageDir/20181202101432629. png
- Ещё вопросы
- Using FileProvider Tetralogy
- note
- The third step is to generate a Content URI for a file
- Step 4 Share a Content URI
- Ответ 1
- Ответ 2
- Ответ 3
- Ответ 4
- Ответ 5
- Ответ 6
- Ответ 7
- Ответ 8
- Ответ 9
- Ответ 10
- Ответ 11
- Ответ 12
- Ответ 13
- Ответ 14
- Ответ 15
- Ответ 16
- data/user/0/com.psh.mTest/app_imageDir/20181202101432629.png
- Ответ 17
- Ответ 18
- Ответ 19
- Ответ 20
- Ответ 21
- Ответ 22
- Ответ 23
- Ответ 24
- Ответ 25
- Ответ 26
- Ответ 27
- Ответ 28
- Ответ 29
- Ответ 30
- Issue
- Solution
Использование FileProvider Tetralogy
Первый шаг — указать FileProvider. Объявите запись в AndroidManifest.xml
<manifest xmlns:android=
package=>
<application
>
<provider
android:name=
android:authorities=
android:grantUriPermissions=
android:exported=>
<meta-data
android:name=
android:resource= />
</provider>
</application>
</manifest>
com.example.myapp — это имя вашего пакета
Второй шаг — указать каталог, которым вы хотите поделиться. Создайте новый каталог xml в каталоге res и новый файл xml в каталоге xml. Мое новое имя файла filepaths.xml
<?xml version="1.0" encoding="utf-8"?>
<! - Представляет файл в корневом каталоге внешней области хранения Environment.getExternalStorageDirectory () / DCIM / camerademo directory ->
< = = />
<! - Представляет файл в корневом каталоге внешней области хранения Environment.getExternalStorageDirectory () / Pictures / camerademo directory ->
< = = />
< = = />
< = = />
<! - Представляет файлы в корневом каталоге внешней области хранения приложения. Каталог Pictures в каталоге Context.getExternalFilesDir (Environment.DIRECTORY_PICTURES) ->
< = = />
<! - представляет файлы в корневом каталоге области внешнего хранилища приложения. Каталог images в каталоге Context.getExternalCacheDir ->
< = = />
name = ”name” Сегмент пути URI, значение будет скрывать имя каталога, которым вы делитесь. Например, следующее
< = = />
Заменит /storage/emulated/0/Android/data/com.hm.camerademo/cache/images на hm_file
путь = «путь» имя каталога, которым вы делитесь
нота
java.lang.IllegalArgumentException: не удалось найти настроенный корень, который содержит /storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg Причина ошибки.
< = = />
< = = />
Я могу указать несколько каталогов в каталоге external-path, которым я хочу поделиться. Имена двух общих каталогов не должны совпадать. Я вызываю поле имени двух вышеупомянутых hm_file, а затем вижу, что не так. Результатом является сообщение об ошибке в заголовке эксперимента
< = = />
< = = />
Затем я сгенерировал Content URI.
File imageFile = ;
storagePath;
File storageDir;
timeStamp = SimpleDateFormat().format( ());
{
// Путь к файлу - это каталог / camerademo в общедоступном каталоге DCIM
storagePath = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
.getAbsolutePath() + File.separator + ;
storageDir = File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, , storageDir);
Log.e(TAG, imageFile.getAbsolutePath());
} (IOException e) {
e.printStackTrace();
}
imageFile;
/storage/emulated//DCIM/camerademo/_110056248725175.jpg
// Генерируем Uri
photoFile = ImageUtil.createImageFile();
photoURI = FileProvider.getUriForFile(, , photoFile);
Но это было неправильно. Ошибка заключается в следующем
java: Failed to find configured root
that contains
/storage/emulated//DCIM/camerademo/_110056248725175
Я изменил путь к сгенерированному выше файлу
// Путь - это каталог cameraademo в каталоге Pictures общего пути хранения
storagePath = Environment
(Environment_PICTURES)
() + File +
// Сгенерированный путь к файлу
/storage/emulated//Pictures/camerademo/_1104551680202685.jpg
// Путь Uri может быть сгенерирован нормально
/hm_file/_1104551680202685.jpg
Приведенная выше проблема показывает, что в файле filePath.xml, если вы хотите указать два общих каталога в одном и том же пути хранения, как показано ниже, тогда значение поля имени двух общих путей не должно быть одинаковым. Тогда путь, указанный в следующей строке (/ storage / emulated / 0 / Pictures / camerademo), переопределит путь, указанный в приведенной выше строке (/ storage / emulated / 0 / DCIM / camerademo)
// Корневым каталогом общего каталога является / storage / emulated / 0 /
external name path
external name path
Третий шаг — создание URI контента для файла.
File imageFile = ;
storagePath;
File storageDir;
timeStamp = SimpleDateFormat().format( ());
{
storagePath = App.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();
storageDir = File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, , storageDir);
} (IOException e) {
e.printStackTrace();
}
// Создать Uri
Uri photoURI = FileProvider.getUriForFile(, , imageFile );
Он должен совпадать с указанным в AndroidManifest.xml. В противном случае вы получите ошибку.
Шаг 4 Поделиться URI контента
В этом примере мы передаем Uri на камеру системы
photoURI = FileProvider(this, , photoFile)
Log(TAG, photoURI())
takePictureIntent(MediaStore_OUTPUT, photoURI)
startActivityForResult(takePictureIntent, TAKE_PHOTO)
Наконец, прикрепите две картинки, картинка идет из справочного документа 2
Рисунок 1: Использование Uri.fromFile () для генерации Uri
Рисунок 1: Используйте FileProvider.getUriForFile (this, «com.hm.camerademo.fileprovider», photoFile); для создания Uri
Recently, when testing the FileProvider related function, when the image was selected from the custom album through the FileProvider to get the content uri, the program suddenly crashed and reported.
Failed to find configured root that contains xxxx
The mistake was that I thought it was a mistake in my configuration, but I didn’t have any effect by referring to the official documentation. After trying to find out the cause of the error, I found the correct solution and learned the final solution. Before we solve the problem, let’s make a simple understanding and review of FileProvider.
Introduction to FileProvider
I have known FileProvider a long time ago, but I have rarely used it in the actual projects I have done before. On the one hand, my project does not involve related scenes. On the one hand, I don’t care too much about file security, even when I look at the official documentation. I have read it many times, but I never thought of using it.
However, with the advent of Android 7.0, in order to further improve the security of private files, Android no longer relaxes the access rights of private files by developers. Before we used the «file:///» absolute path to pass the file address, It is easy to trigger a SecurityException exception when the receiver accesses it.
Therefore, in order to better adapt Android 7.0, such as camera photography, the file address is used in the place where the file address is used, FileProvider is also better to enter the field of vision.
In fact, FileProvider is a special subclass of ContentProvider. It is essentially based on the implementation of ContentProvider. FileProvider will convert the path of «file:///» into a specific content uri of «content://» form. The receiver passes this uri. Then use ContentResolver to go to the media library query parsing.
FileProvider usage
- Declare the Provider in AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myapp">
<application
...>
<provider
Android:name="android.support.v4.content.FileProvider" //point to the FileProvider class in the v4 package
Android:authorities="com.example.myapp.fileprovider" // corresponding to the base domain of your content uri, the generated uri will start with content://com.example.myapp.fileprovider
Android: grantUriPermissions="true" / / set the permission to access the temporary access to the uri
Android:exported="false"//Settings are not allowed to export, our FileProvider should be private
>
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
Android:resource="@xml/filepaths" //Used to set the file access path of FileProvider
/>
</provider>
...
</application>
</manifest>
Next, create an xml directory in our res directory, and create a new filepaths.xml in the xml directory. The name of this xml can be taken according to the specific conditions of the project, corresponding to the configuration of the FileProvider path in the mainisest configuration in the first step. Specified file
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="my_images" path="images/"/>
...
</paths>
In the <paths> tag we must configure at least one or more path sub-elements.
The path sub-element is used to define the path directory corresponding to the content uri.
Here is <files-path> as an example:
- Name attribute: indicates the part of the fileProvider that needs to be added in the content uri, where the name is my_images, so the corresponding content uri is
content://com.example.myapp.fileprovider/my_images
- Path attribute: The path address corresponding to the <files-path> tag is the path address returned by Context.getFilesDir()](), and the value of the path attribute is the subpath of the path. The path value here is «images/». The combined path is as follows:
Content.getFilesDir() + "/images/"
- The name attribute corresponds to the path attribute. According to the above configuration, when accessing the file
content://com.example.myapp.fileprovider/my_images/xxx.jpg
Will find the path path configured here
Content.getFilesDir() + "/images/"
And find the xxx.jpg file
<files-path name="*name*" path="*path*" />
CorrespondingContext.getFilesDir() Path address
<cache-path name="*name*" path="*path*" />
CorrespondinggetCacheDir() Obtained path
<external-path name="*name*" path="*path*" />
<external-files-path name="*name*" path="*path*" />
correspondContext#getExternalFilesDir(String) Context.getExternalFilesDir(null) Returned path address
<external-cache-path name="*name*" path="*path*" />
File imagePath = new File(Context.getFilesDir(), "images");
File newFile = new File(imagePath, "default_image.jpg");
Uri contentUri = getUriForFile(getContext(), "com.mydomain.fileprovider", newFile);
4. Grant a temporary permission to the uri and pass the value to the recipient app
We assume that the receiving app uses startActivityForResult to request the image resource of the app.
then the requester gets the request and gets it according to the above code.
Intent intent = new Intent()
intent.setDataAndType(fileUri,getContentResolver().getType(fileUri));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
setResult(intent);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
The temporary permission used to grant the recipient access to the file can be set toFLAG_GRANT_READ_URI_PERMISSION
FLAG_GRANT_WRITE_URI_PERMISSION
or both
5. The receiver queries the data in the onActivityResult according to the obtained content uri.
Uri returnUri = returnIntent.getData();
Cursor returnCursor = getContentResolver().query(returnUri, null, null, null, null);
int nameIndex = returnCursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
int sizeIndex = returnCursor.getColumnIndex(OpenableColumns.SIZE);
returnCursor.moveToFirst();
TextView nameView = (TextView) findViewById(R.id.filename_text);
TextView sizeView = (TextView) findViewById(R.id.filesize_text);
nameView.setText(returnCursor.getString(nameIndex));
sizeView.setText(Long.toString(returnCursor.getLong(sizeIndex)));
The entire use process of our FileProvider is complete.
failed to find configured root that contains
After further understanding and consolidation of FileProvider, it was finally time to uncover the cause of the error.
Originally, the album of this red rice mobile phone selected the storage location on the external SD card, so the storage address of some photo album photos is actually on the external sd card.
Here is my path configuration
<paths xmlns:android="http://schemas.android.com/apk/res/android>
<external-path name="external_files" path="."/>
</paths>
At first glance, it seems that there is no problem, the path is set to the root path of external, correspondingEnvironment.getExternalStorageDirectory(),
However, this method only gets the path of the built-in SD card, so when the picture in the selected album is an external SD card, the image address cannot be found, so it is thrown. Failed to find configured root that contains error.
How to solve it, the official documentation given configuration items are not able to correspond to external addresses, then we only have to start from the FileProvider source to see if there is any way.
Because FileProvider restricts the path range through the configuration of path, we can analyze it by using path parsing as an entry point.
So I found it.parsePathStrategy()this way,
It is an xml file specifically designed to parse our path.
private static final String TAG_ROOT_PATH = "root-path";
private static final String TAG_FILES_PATH = "files-path";
private static final String TAG_CACHE_PATH = "cache-path";
private static final String TAG_EXTERNAL = "external-path";
private static final File DEVICE_ROOT = new File("/");
private static PathStrategy parsePathStrategy(Context context, String authority)
throws IOException, XmlPullParserException {
...
while ((type = in.next()) != END_DOCUMENT) {
if (type == START_TAG) {
//<external-path> and other root tags
final String tag = in.getName();
/ / Name attribute in the tag
final String name = in.getAttributeValue(null, ATTR_NAME);
/ / path attribute in the tag
String path = in.getAttributeValue(null, ATTR_PATH);
File target = null;
//label comparison
if (TAG_ROOT_PATH.equals(tag)) {
target = buildPath(DEVICE_ROOT, path);
} else if (TAG_FILES_PATH.equals(tag)) {
target = buildPath(context.getFilesDir(), path);
} else if (TAG_CACHE_PATH.equals(tag)) {
target = buildPath(context.getCacheDir(), path);
} else if (TAG_EXTERNAL.equals(tag)) {
target = buildPath(Environment.getExternalStorageDirectory(), path);
}
if (target != null) {
strat.addRoot(name, target);
}
}
}
return strat;
}
private static File buildPath(File base, String... segments) {
File cur = base; //root file
for (String segment : segments) {
if (segment != null) {
/ / Create a file with cur as the root file, segment as a subdirectory
cur = new File(cur, segment);
}
}
return cur;
}
Here I have extracted some key code, we can see through these key codes,
will be executed after the xml is parsed into the corresponding tag.buildPath() Method to use the path corresponding to the root tag (<files-path><external-path>, etc.) as the file root path.
At the same time, the path specified by the path attribute in the tag is used as the sub-path to create the corresponding File object, and finally a cur file object is generated as the target file, thereby limiting the file access path of the FileProvider. Cur’s path path.
One piece of code made me find a strange
if (TAG_ROOT_PATH.equals(tag)) {
target = buildPath(DEVICE_ROOT, path);
}
TAG_ROOT_PATH corresponds to the constant «root-path»,
and DEVICE_ROOT corresponds to new File(«/»);
Yes, although the official documentation is not written, the root tag of <root-path> is left in the code, and its path corresponds to the root of the entire storage pointed to by DEVICE_ROOT. path.
So according to the logic of the code, we have adjusted the configuration file of our path.
changed to
<paths xmlns:android="http://schemas.android.com/apk/res/android>
<root-path name="name" path="" />
</paths>
At this time running the program, you will find that FileProvider has not reported an error, because the file search path has become the entire root path of our client, and as such, our problem has finally been solved smoothly!
Through the analysis of the source code of FileProvider, as mentioned at the beginning, FileProvider inherits from ContentProvider, so there is another way to inherit ContentProvider and then customize Provider to handle, but the code of FileProvider itself has been written very well, so generally there is no need for this. Here, I gave a simple code for reference only:
Also configure our provider in AndroidManifest
<provider
Android:name="org.test.img.MyProvider"//point to a custom Provider
android:authorities="com.test.img"
/>
Then customize a MyProvider
public class MyProvider extends ContentProvider {
private String mAuthority;
@Override
public void attachInfo(Context context, ProviderInfo info) { super.attachInfo(context, info); // Sanity check our security
if (info.exported) {
throw new SecurityException("Provider must not be exported");
}
if (!info.grantUriPermissions) {
throw new SecurityException("Provider must grant uri permissions");
}
mAuthority = info.authority;
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
File file = new File(uri.getPath());
ParcelFileDescriptor parcel = ParcelFileDescriptor.open(file,ParcelFileDescriptor.MODE_READ_ONLY);
return parcel;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public int delete(Uri uri, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public String getType(Uri uri) {
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public Uri insert(Uri uri, ContentValues contentvalues) {
throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public Cursor query(Uri uri, String[] as, String s, String[] as1, String s1) { throw new UnsupportedOperationException("Not supported by this provider");
}
@Override
public int update(Uri uri, ContentValues contentvalues, String s, String[] as) {
throw new UnsupportedOperationException("Not supported by this provider");
}
public static Uri getUriForFile(File file) {
return new Uri.Builder().scheme("content").authority(mAuthority).encodedPath(file.getPath).build();
}
}
This way our code can get the content uri through the custom getUriForFile
Ответ
Ваш файл хранится в getExternalFilesDir()
. Это сопоставляется с <external-files-path>
, а не <files-path>
. Кроме того, ваш путь к файлу не содержит images/
в нем, поэтому атрибут path
в вашем XML недействителен.
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="my_images" path="my_images" />
</paths>
Аналогичная проблема возникла после активированных ароматов (dev, stage).
До того, как ароматы моего ресурса путей выглядели так:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="my_images"
path="Android/data/pl.myapp/files/Pictures" />
</paths>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="my_images"
path="." />
</paths>
Если вы используете внутренний кеш, используйте его.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="/" />
</paths>
Это может решить любую проблему: все теги добавляются, поэтому вам не нужно беспокоиться о пути к папкам. Замените res/xml/file_paths.xml следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Это тоже смущает меня.
Проблема заключается в атрибуте «path» в вашем XML файле.
Из этого документа FileProvider
‘path’ — это подкаталог,
но в другом документе (камера/фотобаза)
«путь» — полный путь.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>
Я просто изменяю этот «путь» на полный путь, и он просто работает.
Проверьте, сколько хранилищ вашего устройства предлагает — совместное использование файлов из вторичного хранилища не поддерживается. Посмотрите на источник FileProvider.java
(из поддержки-core-utils 25.3.1):
} else if (TAG_EXTERNAL_FILES.equals(tag)) {
File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
if (externalFilesDirs.length > 0) {
target = externalFilesDirs[0];
}
} else if (TAG_EXTERNAL_CACHE.equals(tag)) {
File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context);
if (externalCacheDirs.length > 0) {
target = externalCacheDirs[0];
}
}
Таким образом, они берут только первое хранилище.
Кроме того, вы можете видеть, что getExternalCacheDirs()
используется для получения списка хранилищ через интерфейс ContextCompat
. См. документация в своих пределах (он сказал, чтобы не распознавать USB-вспышки, например). Лучше всего сделать отладочный вывод списка хранилищ из этого API самостоятельно, чтобы вы могли проверить, что путь к хранилищу соответствует пути, переданному в getUriForFile()
.
Там уже отправлен билет (в соответствии с 06-2017) в трекер Google, запрашивая поддержку нескольких хранилищ. В конце концов, я нашел SO вопрос по этому вопросу.
Я бы опоздал, но нашел решение для него. Работая отлично, я просто изменил файл XML пути:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path name="root" path="." />
</paths>
Имейте в виду, что внешний путь не указывает на ваше вторичное хранилище, а также «съемное хранилище» (несмотря на название «внешний»). Если вы получаете «Не удалось найти настроенный root», вы можете добавить эту строку в свой XML файл.
<root-path name="root" path="." />
Подробнее см. Здесь FileProvider и дополнительное внешнее хранилище
Ничто из этого не сработало для меня. Единственный подход, который работает, заключается не в том, чтобы объявить явный путь в xml. Так сделайте это и будьте счастливы:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
</paths>
Здесь также есть отличный учебник по этому вопросу: https://www.youtube.com/watch?v=9ZxRTKvtfnY&t=613s
Я уверен, что я опоздал на вечеринку, но ниже работал для меня.
<paths>
<root-path name="root" path="." />
</paths>
Моя проблема заключалась в том, что у меня были перекрывающиеся имена в пути к файлам для разных типов, например:
<cache-path
name="cached_files"
path="." />
<external-cache-path
name="cached_files"
path="." />
После того, как я изменил имена («cached_files»), чтобы быть уникальными, я избавился от этой ошибки. Я предполагаю, что эти пути хранятся в некоторых HashMap или что-то, что не позволяет дублировать.
Это работало и для меня. Вместо того, чтобы давать полный путь, я дал путь = «Картинки», и он отлично работал.
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="images"
path="Pictures">
</external-files-path>
</paths>
Следующая работа решает мою проблему
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="media" path="." />
</paths>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="[PACKAGE_NAME]"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
void shareImage() {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this,"com.slappstudio.pencilsketchphotomaker", selectedFilePath));
startActivity(Intent.createChooser(intent,getString(R.string.string_share_with)));
}
Я потратил 5 часов на это..
Я перепробовал все методы, описанные выше, но это зависит от того, какое хранилище использует ваше приложение.
Проверьте документацию, прежде чем пытаться коды.
В моем случае, так как files-path
будет Context.getFilesDir()
. Самое Context.getFilesDir()
что Context.getFilesDir()
друг на друга.
что я ищу это
Data/user/0/com. psh. mTest/app_imageDir/20181202101432629. png
Context.getFilesDir()
поэтому тег должен быть
Тогда это работает!
ни один из вышеперечисленных не работал для меня, после нескольких часов отладки я узнал, что проблема в createImageFile()
, в частности absolute path
и relative path
Я предполагаю, что вы, ребята, используете официальное руководство по Android для фотосъемки. https://developer.android.com/training/camera/photobasics
private static File createImageFile(Context context) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
Обратите внимание на storageDir
, это место, где будет создан файл. Поэтому, чтобы получить абсолютный путь к этому файлу, я просто использую image.getAbsolutePath()
, этот путь будет использоваться в onActivityResult
если вам нужно изображение растрового изображения после съемки
ниже file_path.xml
, просто используйте .
так что он использует абсолютный путь
<paths>
<external-path
name="my_images"
path="." />
</paths>
и если вам нужно растровое изображение после съемки
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Bitmap bmp = null;
try {
bmp = BitmapFactory.decodeFile(mCurrentPhotoPath);
} catch (Exception e) {
e.printStackTrace();
}
}
Что я сделал для этого —
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"/>
</provider>
filepaths.xml(разрешить FileProvider обмениваться всеми файлами, находящимися в каталоге внешних файлов приложения)
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="files"
path="/" />
</paths>
и в классе java —
Uri fileProvider = FileProvider.getUriForFile(getContext(),"com.mydomain.fileprovider",newFile);
Я наконец нашел свою проблему. Я описал это в этой ссылке.
моя проблема заключалась в различии между идентификатором приложения в режиме отладки и выпуска
Следующее изменение файла xml file_paths сработало для меня.
<external-files-path name="my_images" path="Pictures"/>
<external-files-path name="my_movies" path="Movies"/>
Официальный документ Android говорит, что file_paths.xml должен иметь:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images"
path="Android/data/com.example.package.name/files/Pictures" />
</paths>
Но чтобы заставить его работать в последнем андроиде, в конце пути должно быть «/», например:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images"
path="Android/data/com.example.package.name/files/Pictures/" />
</paths>
1 — Убедитесь, что ваш поставщик тегов находится внутри приложения тегов
<application>
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.companyname.Pocidadao.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
</provider>
</application>
2 — Если вы безуспешно пробуете много путей, попробуйте следующее:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="." />
<external-path name="external" path="." />
<root-path name="root" path="." />
<files-path name="my_images" path="/" />
<files-path name="my_images" path="myfile/"/>
<files-path name="files" path="." />
<external-path name="external_files" path="." />
<external-path name="images" path="Pictures" />
<external-path name="my_images" path="." />
<external-path name="my_images" path="Android/data/com.companyname.yourproject/files/Pictures" />
<external-path name="my_images" path="Android/data/com.companyname.yourproject/files/Pictures/" />
<external-files-path name="images" path="Pictures"/>
<external-files-path name="camera_image" path="Pictures/"/>
<external-files-path name="external_files" path="." />
<external-files-path name="my_images" path="my_images" />
<external-cache-path name="external_cache" path="." />
</paths>
3 — Не забудьте проверить, активирована ли камера в вашем эмуляторе.
Вам нужно изменить файл xml file_paths.xml из
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="my_images" path="images/"/>
</paths>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<fexeternal-path name="my_images" path="Android/data/com.example.marek.myapplication/files/Pictures"/>
</paths>
Проблема может быть не только в пути XML.
Следующее — мое исправление:
Просмотр корневого курса в android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile()
:
public File getFileForUri(Uri uri) {
String path = uri.getEncodedPath();
final int splitIndex = path.indexOf('/', 1);
final String tag = Uri.decode(path.substring(1, splitIndex));
path = Uri.decode(path.substring(splitIndex + 1));
final File root = mRoots.get(tag); // mRoots is parsed from path xml
if (root == null) {
throw new IllegalArgumentException("Unable to find configured root for " + uri);
}
// ...
}
Это означает, что mRoots
должен содержать тег запрошенного URI. Поэтому я пишу код для печати mRoots
и тега mRoots
, а затем легко найти теги не совпадают.
Если ничего не помогает, и вы получаете ошибку
failed to find configured root that contains /data/data/...
затем попробуйте изменить некоторую строку, например:
File directory = thisActivity.getDir("images", Context.MODE_PRIVATE);
File directory = new File(thisActivity.getFilesDir(), "images");
и в файле xml:
<files-path name="files" path="." />
что странно, поскольку папка, к которой я обращаюсь, — это /images
.
Привет друзья Попробуйте это
В этом кодексе
1) Как объявить 2 поставщика файлов в манифесте.
2) Первый провайдер для загрузки файла
3) второй провайдер, используемый для камеры и галерей
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="apks" path="." />
</paths>
<provider
android:name=".Utils.MyFileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities"
tools:ignore="InnerclassSeparator">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="storage/emulated/0" path="."/>
</paths>
Создать класс MyFileProvider (только создать класс, который не объявляет какой-либо метод)
Когда вы использовали File Provider used (.fileprovider), это имя использовалось для изображения (.provider).
Это зависит от того, какое хранилище вы хотите делать, ВНУТРЕННЕЕ или ВНЕШНЕЕ
для ВНЕШНЕГО ХРАНЕНИЯ
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="my_images" path="my_images" />
</paths>
но для ВНУТРЕННЕГО ХРАНЕНИЯ, будьте осторожны с этим путем, потому что он использует метод getFilesDir(), который означает, что ваш файл будет находиться в корневом каталоге приложения («/»)
File storeDir = getFilesDir(); // path "/"
Таким образом, ваш файл поставщика должен выглядеть следующим образом:
<paths>
<files-path name="my_images" path="/" />
</paths>
У меня была та же проблема, я попробовал приведенный ниже код для его работы.
1.Создать Xmlfile: provider_paths
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="my_images" path="myfile/"/>
</paths>
2. Файл Mainfest
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.ril.learnet.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
3.В файле Java.
File file = new File(getActivity().getFilesDir(), "myfile");
if (!file.exists()) {
file.mkdirs();
}
String destPath = file.getPath() + File.separator + attachmentsListBean.getFileName();
file mfile = new File(destPath);
Uri path;
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
path = FileProvider.getUriForFile(AppController.getInstance().getApplicationContext(), AppController.getInstance().getApplicationContext().getPackageName() + ".provider", mfile );
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
path = Uri.fromFile(mfile);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(path, "image/*");
getActivity().startActivity(intent);
Я заметил, что политика или поведение файла path.xml
изменилось между библиотекой поддержки 26 и 27. Для захвата изображения с камеры я увидел следующие изменения:
С 26 я должен был использовать
<external-path>
и полный путь, указанный в аргументеpath
.С 27 я должен был использовать
<external-files-path>
и только подпапку в аргументеpath
.
Итак, что конкретно работало для меня в Support Library 27, поскольку файл path.xml
был
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="camera_image" path="Pictures/"/>
</paths>
- Для пользователей Xamarin.Android
Это также может быть результатом не обновления пакетов поддержки при настройке Android 7.1, 8.0+. Обновите их до v25.4.0.2 +, и эта конкретная ошибка может исчезнуть (если вы правильно настроили файл file_path, как утверждают другие).
Предоставление контекста: я переключился на таргетинг Oreo из Нуга в приложение Xamarin.Forms и сделайте снимок с Xam.Plugin.Mediaначалось сбой с указанным выше сообщением об ошибке, поэтому обновление пакетов помогло нормально.
Я вижу, что по крайней мере вы не предоставляете тот же путь, что и другие, в file_paths.xml.
Поэтому, пожалуйста, убедитесь, что вы указали то же самое имя или путь пакета в трех местах, включая:
-
android:authorities
атрибут в манифесте -
path
атрибут в file_paths.xml -
authority
при вызове FileProvider.getUriForFile().
Я работал над Android 6 и 7, чтобы запустить камеру и сохранить фотографию.
Решение, которое я получил, работает 100% штрафа:
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CALL_PRIVILEGED" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.READ_CALENDAR" />
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.EXPAND_STATUS_BAR" />
<uses-permission android:name="com.android.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.lb.launcher.permission.READ_SETTINGS" />
<uses-permission android:name="com.lb.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.lb.launcher.permission.RECEIVE_LAUNCH_BROADCASTS" />
<uses-permission android:name="com.lb.launcher.permission.RECEIVE_FIRST_LOAD_BROADCAST" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
....
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.android.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
Где file_paths
— это файл XML, который должен быть создан в папке res/xml
.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="images" path="Pictures" />
</paths>
private void SavePhoto(String fileName, ContentValues values)
{
// If the parent dir doesn't exist, create it
File folder = new File(Environment.getExternalStorageDirectory(), "Pictures");
boolean success = true;
if (!folder.exists()) {
success = folder.mkdir();
}
if (success) {
// Do something on success
} else {
// Do something else on failure
}
File file = new File(folder, fileName);
imageUri = FileProvider.getUriForFile(context, "com.example.android.fileprovider", file);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);
if (intent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(intent, CAPTURE_IMAGE_ACTIVITY_REQUEST_CODE);
}
}
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path
name="my_images"
path="" />
</paths>
Ещё вопросы
When calling the system camera to take pictures, the process is like this
() {
Intent takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE);
(takePictureIntent.resolveActivity(getPackageManager()) != ) {
//Create a path to save the picture
photoFile = ImageUtil.createImageFile();
(photoFile != ) {
photoURI = Uri.fromFile(photoFile);
//Pass a Uri
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, TAKE_PHOTO);
}
}
}
Then process the photo results in the onActivityResult method.
( requestCode, resultCode, Intent data) {
.onActivityResult(requestCode, resultCode, data);
(requestCode) {
TAKE_PHOTO:
(resultCode == RESULT_OK) {
//Process the result of taking pictures
processTakePhoto(photoFile.getPath());
}
;
:
;
}
}
///storage/emulated//Android/data//files/Picture
s/_140305187933259 exposed beyond app through
ClipData()
Then I searched online and found that it is photoURI = Uri.fromFile(photoFile); There is something wrong with this way of creating Uri, which is not safe enough. You need to use FileProvider to create Uri.
Using FileProvider Tetralogy
The first step is to specify a FileProvider. Declare an entry in AndroidManifest.xml
<manifest xmlns:android=
package=>
<application
>
<provider
android:name=
android:authorities=
android:grantUriPermissions=
android:exported=>
<meta-data
android:name=
android:resource= />
</provider>
</application>
</manifest>
com.example.myapp is your package name
<?xml version="1.0" encoding="utf-8"?>
<!--Represents the file in the root directory of the external storage area Environment.getExternalStorageDirectory()/DCIM/camerademo directory-->
< = = />
<!--Represents the file in the root directory of the external storage area Environment.getExternalStorageDirectory()/Pictures/camerademo directory-->
< = = />
< = = />
< = = />
<!--Represents the files in the root directory of the external storage area of the app. The Pictures directory under the Context.getExternalFilesDir(Environment.DIRECTORY_PICTURES) directory-->
< = = />
<!--Represents the files in the root directory of the external storage area of the app. The images directory under the Context.getExternalCacheDir directory-->
< = = />
< = = />
Will replace /storage/emulated/0/Android/data/com.hm.camerademo/cache/images with hm_file
note
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/DCIM/camerademo/20170226_110056248725175.jpg The cause of the error.
< = = />
< = = />
< = = />
< = = />
Then I generated a Content URI.
File imageFile = ;
storagePath;
File storageDir;
timeStamp = SimpleDateFormat().format( ());
{
//The file path is the /camerademo directory under the public DCIM directory
storagePath = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM)
.getAbsolutePath() + File.separator + ;
storageDir = File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, , storageDir);
Log.e(TAG, imageFile.getAbsolutePath());
} (IOException e) {
e.printStackTrace();
}
imageFile;
/storage/emulated//DCIM/camerademo/_110056248725175.jpg
photoFile = ImageUtil.createImageFile();
photoURI = FileProvider.getUriForFile(, , photoFile);
java: Failed to find configured root
that contains
/storage/emulated//DCIM/camerademo/_110056248725175
I changed the path of the above generated file
//The path is the cameraademo directory under the Pictures directory of the public storage path
storagePath = Environment
(Environment_PICTURES)
() + File +
//The generated file path
/storage/emulated//Pictures/camerademo/_1104551680202685.jpg
//Uri path can be generated normally
/hm_file/_1104551680202685.jpg
external name path
external name path
The third step is to generate a Content URI for a file
File imageFile = ;
storagePath;
File storageDir;
timeStamp = SimpleDateFormat().format( ());
{
storagePath = App.getInstance().getExternalFilesDir(Environment.DIRECTORY_PICTURES).getAbsolutePath();
storageDir = File(storagePath);
storageDir.mkdirs();
imageFile = File.createTempFile(timeStamp, , storageDir);
} (IOException e) {
e.printStackTrace();
}
Uri photoURI = FileProvider.getUriForFile(, , imageFile );
It must be the same as specified in AndroidManifest.xml. Otherwise, you will get an error.
Step 4 Share a Content URI
In this example we are passing a Uri to the camera of the system
photoURI = FileProvider(this, , photoFile)
Log(TAG, photoURI())
takePictureIntent(MediaStore_OUTPUT, photoURI)
startActivityForResult(takePictureIntent, TAKE_PHOTO)
Finally, attach two pictures, the picture comes from the reference document 2
Figure 1: Use Uri.fromFile() to generate a Uri
Figure 1: Use FileProvider.getUriForFile(this, «com.hm.camerademo.fileprovider», photoFile); to generate a Uri
Я пытаюсь сделать снимок с камерой, но я получаю следующую ошибку:
FATAL EXCEPTION: main
Process: com.example.marek.myapplication, PID: 6747
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example.marek.myapplication/files/Pictures/JPEG_20170228_175633_470124220.jpg
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:711)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:400)
at com.example.marek.myapplication.MainActivity.dispatchTakePictureIntent(MainActivity.java:56)
at com.example.marek.myapplication.MainActivity.access$100(MainActivity.java:22)
at com.example.marek.myapplication.MainActivity$1.onClick(MainActivity.java:35)
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.example.marek.myapplication.fileprovider"
android:enabled="true"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// Ensure that there a camera activity to handle the intent
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException ex) {
Toast.makeText(getApplicationContext(), "Error while saving picture.", Toast.LENGTH_LONG).show();
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.marek.myapplication.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="my_images" path="images/"/>
</paths>
Ответ 1
Ваш файл хранится в getExternalFilesDir()
. Это сопоставляется с <external-files-path>
, а не <files-path>
. Кроме того, ваш путь к файлу не содержит images/
в нем, поэтому атрибут path
в вашем XML недопустим.
Замените res/xml/file_paths.xml
на:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="my_images" />
</paths>
Ответ 2
Аналогичная проблема возникла после активированных ароматов (dev, stage).
До того, как ароматы моего ресурса путей выглядели так:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="my_images"
path="Android/data/pl.myapp/files/Pictures" />
</paths>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="my_images"
path="." />
</paths>
Ответ 3
Это может решить любую проблему: все теги добавляются, поэтому вам не нужно беспокоиться о пути к папкам. Замените res/xml/file_paths.xml следующим образом:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
Ответ 4
Если вы используете внутренний кеш, используйте его.
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="/" />
</paths>
Ответ 5
Это тоже смущает меня.
Проблема заключается в атрибуте «path» в вашем XML файле.
Из этого документа FileProvider
‘path’ — это подкаталог,
но в другом документе (камера/фотобаза)
«путь» — полный путь.
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
</paths>
Я просто изменяю этот «путь» на полный путь, и он просто работает.
Ответ 6
Проверьте, сколько хранилищ вашего устройства предлагает — совместное использование файлов из вторичного хранилища не поддерживается. Посмотрите на источник FileProvider.java
(из поддержки-core-utils 25.3.1):
} else if (TAG_EXTERNAL_FILES.equals(tag)) {
File[] externalFilesDirs = ContextCompat.getExternalFilesDirs(context, null);
if (externalFilesDirs.length > 0) {
target = externalFilesDirs[0];
}
} else if (TAG_EXTERNAL_CACHE.equals(tag)) {
File[] externalCacheDirs = ContextCompat.getExternalCacheDirs(context);
if (externalCacheDirs.length > 0) {
target = externalCacheDirs[0];
}
}
Таким образом, они берут только первое хранилище.
Кроме того, вы можете видеть, что getExternalCacheDirs()
используется для получения списка хранилищ через интерфейс ContextCompat
. См. документация в своих пределах (он сказал, чтобы не распознавать USB-вспышки, например). Лучше всего сделать отладочный вывод списка хранилищ из этого API самостоятельно, чтобы вы могли проверить, что путь к хранилищу соответствует пути, переданному в getUriForFile()
.
Там уже отправлен билет (в соответствии с 06-2017) в трекер Google, запрашивая поддержку нескольких хранилищ. В конце концов, я нашел SO вопрос по этому вопросу.
Ответ 7
Я бы опоздал, но нашел решение для него. Работая отлично, я просто изменил файл XML пути:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<root-path name="root" path="." />
</paths>
Ответ 8
Имейте в виду, что внешний путь не указывает на ваше вторичное хранилище, а также «съемное хранилище» (несмотря на название «внешний»). Если вы получаете «Не удалось найти настроенный root», вы можете добавить эту строку в свой XML файл.
<root-path name="root" path="." />
Подробнее см. Здесь FileProvider и дополнительное внешнее хранилище
Ответ 9
Ничто из этого не сработало для меня. Единственный подход, который работает, заключается не в том, чтобы объявить явный путь в xml. Так сделайте это и будьте счастливы:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images" path="." />
</paths>
Здесь также есть отличный учебник по этому вопросу: https://www.youtube.com/watch?v=9ZxRTKvtfnY&t=613s
Ответ 10
Я уверен, что я опоздал на вечеринку, но ниже работал для меня.
<paths>
<root-path name="root" path="." />
</paths>
Ответ 11
Моя проблема заключалась в том, что у меня были перекрывающиеся имена в пути к файлам для разных типов, например:
<cache-path
name="cached_files"
path="." />
<external-cache-path
name="cached_files"
path="." />
После того, как я изменил имена («cached_files»), чтобы быть уникальными, я избавился от этой ошибки. Я предполагаю, что эти пути хранятся в некоторых HashMap или что-то, что не позволяет дублировать.
Ответ 12
1 — Убедитесь, что ваш поставщик тегов находится внутри приложения тегов
<application>
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.companyname.Pocidadao.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths"></meta-data>
</provider>
</application>
2 — Если вы безуспешно пробуете много путей, попробуйте следующее:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<cache-path name="cache" path="." />
<external-path name="external" path="." />
<root-path name="root" path="." />
<files-path name="my_images" path="/" />
<files-path name="my_images" path="myfile/"/>
<files-path name="files" path="." />
<external-path name="external_files" path="." />
<external-path name="images" path="Pictures" />
<external-path name="my_images" path="." />
<external-path name="my_images" path="Android/data/com.companyname.yourproject/files/Pictures" />
<external-path name="my_images" path="Android/data/com.companyname.yourproject/files/Pictures/" />
<external-files-path name="images" path="Pictures"/>
<external-files-path name="camera_image" path="Pictures/"/>
<external-files-path name="external_files" path="." />
<external-files-path name="my_images" path="my_images" />
<external-cache-path name="external_cache" path="." />
</paths>
3 — Не забудьте проверить, активирована ли камера в вашем эмуляторе.
Ответ 13
Это работало и для меня. Вместо того, чтобы давать полный путь, я дал путь = «Картинки», и он отлично работал.
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path
name="images"
path="Pictures">
</external-files-path>
</paths>
Ответ 14
Что я сделал для этого —
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.mydomain.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths"/>
</provider>
filepaths.xml(разрешить FileProvider обмениваться всеми файлами, находящимися в каталоге внешних файлов приложения)
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path name="files"
path="/" />
</paths>
и в классе java —
Uri fileProvider = FileProvider.getUriForFile(getContext(),"com.mydomain.fileprovider",newFile);
Ответ 15
ни один из вышеперечисленных не работал для меня, после нескольких часов отладки я узнал, что проблема в createImageFile()
, в частности absolute path
и relative path
Я предполагаю, что вы, ребята, используете официальное руководство по Android для фотосъемки. https://developer.android.com/training/camera/photobasics
private static File createImageFile(Context context) throws IOException {
// Create an image file name
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = context.getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
// Save a file: path for use with ACTION_VIEW intents
mCurrentPhotoPath = image.getAbsolutePath();
return image;
}
Обратите внимание на storageDir
, это место, где будет создан файл. Поэтому, чтобы получить абсолютный путь к этому файлу, я просто использую image.getAbsolutePath()
, этот путь будет использоваться в onActivityResult
если вам нужно изображение растрового изображения после съемки
ниже file_path.xml
, просто используйте .
так что он использует абсолютный путь
<paths>
<external-path
name="my_images"
path="." />
</paths>
и если вам нужно растровое изображение после съемки
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Bitmap bmp = null;
try {
bmp = BitmapFactory.decodeFile(mCurrentPhotoPath);
} catch (Exception e) {
e.printStackTrace();
}
}
Ответ 16
Я потратил 5 часов на это..
Я перепробовал все методы, описанные выше, но это зависит от того, какое хранилище использует ваше приложение.
Проверьте документацию, прежде чем пытаться коды.
В моем случае, так как files-path
будет Context.getFilesDir()
. Самое Context.getFilesDir()
что Context.getFilesDir()
друг на друга.
что я ищу это
data/user/0/com.psh.mTest/app_imageDir/20181202101432629.png
Context.getFilesDir()
поэтому тег должен быть
Тогда это работает!
Ответ 17
Следующая работа решает мою проблему
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="media" path="." />
</paths>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="[PACKAGE_NAME]"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
void shareImage() {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/*");
intent.putExtra(Intent.EXTRA_STREAM, FileProvider.getUriForFile(this,"com.slappstudio.pencilsketchphotomaker", selectedFilePath));
startActivity(Intent.createChooser(intent,getString(R.string.string_share_with)));
}
Ответ 18
Официальный документ Android говорит, что file_paths.xml должен иметь:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images"
path="Android/data/com.example.package.name/files/Pictures" />
</paths>
Но чтобы заставить его работать в последнем андроиде, в конце пути должно быть «/», например:
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="my_images"
path="Android/data/com.example.package.name/files/Pictures/" />
</paths>
Ответ 19
Следующее изменение файла xml file_paths сработало для меня.
<external-files-path name="my_images" path="Pictures"/>
<external-files-path name="my_movies" path="Movies"/>
Ответ 20
Я наконец нашел свою проблему. Я описал это в этой ссылке.
моя проблема заключалась в различии между идентификатором приложения в режиме отладки и выпуска
Ответ 21
Это зависит от того, какое хранилище вы хотите делать, ВНУТРЕННЕЕ или ВНЕШНЕЕ
для ВНЕШНЕГО ХРАНЕНИЯ
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="my_images" path="my_images" />
</paths>
но для ВНУТРЕННЕГО ХРАНЕНИЯ, будьте осторожны с этим путем, потому что он использует метод getFilesDir(), который означает, что ваш файл будет находиться в корневом каталоге приложения («/»)
File storeDir = getFilesDir(); // path "/"
Таким образом, ваш файл поставщика должен выглядеть следующим образом:
<paths>
<files-path name="my_images" path="/" />
</paths>
Ответ 22
Я вижу, что по крайней мере вы не предоставляете тот же путь, что и другие, в file_paths.xml.
Поэтому, пожалуйста, убедитесь, что вы указали то же самое имя или путь пакета в трех местах, включая:
-
android:authorities
атрибут в манифесте -
path
атрибут в file_paths.xml -
authority
при вызове FileProvider.getUriForFile().
Ответ 23
- Для пользователей Xamarin.Android
Это также может быть результатом не обновления пакетов поддержки при настройке Android 7.1, 8.0+. Обновите их до v25.4.0.2 +, и эта конкретная ошибка может исчезнуть (если вы правильно настроили файл file_path, как утверждают другие).
Предоставление контекста: я переключился на таргетинг Oreo из Нуга в приложение Xamarin.Forms и сделайте снимок с Xam.Plugin.Mediaначалось сбой с указанным выше сообщением об ошибке, поэтому обновление пакетов помогло нормально.
Ответ 24
Я заметил, что политика или поведение файла path.xml
изменилось между библиотекой поддержки 26 и 27. Для захвата изображения с камеры я увидел следующие изменения:
С 26 я должен был использовать
<external-path>
и полный путь, указанный в аргументеpath
.С 27 я должен был использовать
<external-files-path>
и только подпапку в аргументеpath
.
Итак, что конкретно работало для меня в Support Library 27, поскольку файл path.xml
был
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-files-path name="camera_image" path="Pictures/"/>
</paths>
Ответ 25
У меня была та же проблема, я попробовал приведенный ниже код для его работы.
1.Создать Xmlfile: provider_paths
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="my_images" path="myfile/"/>
</paths>
2. Файл Mainfest
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.ril.learnet.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
3.В файле Java.
File file = new File(getActivity().getFilesDir(), "myfile");
if (!file.exists()) {
file.mkdirs();
}
String destPath = file.getPath() + File.separator + attachmentsListBean.getFileName();
file mfile = new File(destPath);
Uri path;
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
{
path = FileProvider.getUriForFile(AppController.getInstance().getApplicationContext(), AppController.getInstance().getApplicationContext().getPackageName() + ".provider", mfile );
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
path = Uri.fromFile(mfile);
}
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setDataAndType(path, "image/*");
getActivity().startActivity(intent);
Ответ 26
Привет друзья Попробуйте это
В этом кодексе
1) Как объявить 2 поставщика файлов в манифесте.
2) Первый провайдер для загрузки файла
3) второй провайдер, используемый для камеры и галерей
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="apks" path="." />
</paths>
<provider
android:name=".Utils.MyFileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true"
tools:replace="android:authorities"
tools:ignore="InnerclassSeparator">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_path" />
</provider>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path name="storage/emulated/0" path="."/>
</paths>
Создать класс MyFileProvider (только создать класс, который не объявляет какой-либо метод)
Когда вы использовали File Provider used (.fileprovider), это имя использовалось для изображения (.provider).
Ответ 27
Если ничего не помогает, и вы получаете ошибку
failed to find configured root that contains /data/data/...
затем попробуйте изменить некоторую строку, например:
File directory = thisActivity.getDir("images", Context.MODE_PRIVATE);
File directory = new File(thisActivity.getFilesDir(), "images");
и в файле xml:
<files-path name="files" path="." />
что странно, поскольку папка, к которой я обращаюсь, — это /images
.
Ответ 28
Проблема может быть не только в пути XML.
Следующее — мое исправление:
Просмотр корневого курса в android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile()
:
public File getFileForUri(Uri uri) {
String path = uri.getEncodedPath();
final int splitIndex = path.indexOf('/', 1);
final String tag = Uri.decode(path.substring(1, splitIndex));
path = Uri.decode(path.substring(splitIndex + 1));
final File root = mRoots.get(tag); // mRoots is parsed from path xml
if (root == null) {
throw new IllegalArgumentException("Unable to find configured root for " + uri);
}
// ...
}
Это означает, что mRoots
должен содержать тег запрошенного URI. Поэтому я пишу код для печати mRoots
и тега mRoots
, а затем легко найти теги не совпадают.
Ответ 29
Вам нужно изменить файл xml file_paths.xml из
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="my_images" path="images/"/>
</paths>
<?xml version="1.0" encoding="utf-8"?>
<paths>
<fexeternal-path name="my_images" path="Android/data/com.example.marek.myapplication/files/Pictures"/>
</paths>
Ответ 30
Моя проблема была file_paths.xml
Имя файла: я file_paths.xml
в res/xml, в то время как для ресурса был задан файл provider_paths.xml
в манифесте:
<provider
android:authorities="ir.aghigh.radio.fileprovider"
android:name="android.support.v4.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
Я изменил provider_paths
на file_paths
и проблема решена.
Just getting into the Glide image loading library for Android.
Working with code from here: https://github.com/bumptech/glide/issues/459
Full project here:
https://github.com/mhurwicz/glide02
java.lang.IllegalArgumentException: Failed to find configured root that contains /data/data/com.example.glide02/cache/image_manager_disk_cache/5a992029460eed14244e8b970d969d45518b2f7ac10f71eb26bd0aaf7c3bcf06.0
The rest of the messages are:
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:711)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:400)
at com.example.glide02.ShareTask.onPostExecute(ShareTask.java:37)
at com.example.glide02.ShareTask.onPostExecute(ShareTask.java:15)
at android.os.AsyncTask.finish(AsyncTask.java:651)
at android.os.AsyncTask.-wrap1(AsyncTask.java)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:668)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
The error occurs on FileProvider.getUriForFile()
@Override protected void onPostExecute(File result) {
if (result == null) { return; }
Uri uri = FileProvider.getUriForFile(context, context.getPackageName(), result);
share(uri); // startActivity probably needs UI thread
}
I’ve looked at several other questions relating to this error, but can’t see how they relate to my case, perhaps due to my lack of familiarity with this whole area.
Any help will be greatly appreciated.
This is the full code for the class in which the above method occurs:
class ShareTask extends AsyncTask<String, Void, File> {
private final Context context;
public ShareTask(Context context) {
this.context = context;
}
@Override protected File doInBackground(String... params) {
String url = params[0]; // should be easy to extend to share multiple images at once
try {
return Glide
.with(context)
.load(url)
.downloadOnly(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
.get() // needs to be called on background thread
;
} catch (Exception ex) {
Log.w("SHARE", "Sharing " + url + " failed", ex);
return null;
}
}
@Override protected void onPostExecute(File result) {
if (result == null) { return; }
Uri uri = FileProvider.getUriForFile(context, context.getPackageName(), result);
share(uri); // startActivity probably needs UI thread
}
private void share(Uri result) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setType("image/jpeg");
intent.putExtra(Intent.EXTRA_SUBJECT, "Shared image");
intent.putExtra(Intent.EXTRA_TEXT, "Look what I found!");
intent.putExtra(Intent.EXTRA_STREAM, result);
context.startActivity(Intent.createChooser(intent, "Share image"));
}
Issue
On the main manifest xml:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
The res/xml/file_paths.xml file:
<?xml version="1.0" encoding="utf-8"?>
<paths>
<files-path name="my_files" path="." />
</paths>
String path = root.getAbsolutePath() + "/file.pdf";
final Uri data = FileProvider.getUriForFile(getApplicationContext(), BuildConfig.APPLICATION_ID+".fileprovider", new File(path));
getApplicationContext().grantUriPermission(getApplicationContext().getPackageName(), data, Intent.FLAG_GRANT_READ_URI_PERMISSION);
final Intent intent = new Intent(Intent.ACTION_VIEW).setDataAndType(data, "application/pdf").addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
getApplicationContext().startActivity(intent);
Caused by: java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/file.pdf
Solution
The documentation for FileProvider
says that <files-path>
:
Represents files in the
files/
subdirectory of your app’s internal storage area. This subdirectory is the same as the value returned byContext.getFilesDir()
.
Your file is not in internal storage. It is in external storage. For that, you need an <external-path>
element, not a <files-path>
element.
Answered By – CommonsWare
This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0