我已经更新了我的项目来实施Providers。整个实现看起来是正确的,但我StringIndexOutOfBoundsException在FileProvider课堂上收到异常。
在我的例子中,创建了一个意图,用户可以在其中选择从他的图库中获取图像还是拍摄新照片。
这就是创建我的意图之一的地方,然后将其包含在列表中以创建选择器。
File file = getTempFile(context);
if (file != null) {
Intent takePhotoIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
takePhotoIntent.putExtra("return-data", true);
takePhotoIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
takePhotoIntent.putExtra(MediaStore.EXTRA_OUTPUT,
FileProvider.getUriForFile(context, // line 43
BuildConfig.APPLICATION_ID.concat(".fileprovider"),
file));
intentList = addIntentsToList(context, intentList, takePhotoIntent);
}
Run Code Online (Sandbox Code Playgroud)
getTempFile方法:
private static File getTempFile(Context context) {
File imageFile = new File(context.getExternalCacheDir(), TEMP_IMAGE_NAME);
if (imageFile.getParentFile().mkdirs()) {
return imageFile;
} else {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
抛出的异常:
java.lang.StringIndexOutOfBoundsException: length=86; index=87
at java.lang.String.substring(String.java:1939)
at android.support.v4.content.FileProvider$SimplePathStrategy.getUriForFile(FileProvider.java:728)
at android.support.v4.content.FileProvider.getUriForFile(FileProvider.java:404)
at com.project.helper.ImagePicker.getPickImageIntent(ImagePicker.java:43)
Run Code Online (Sandbox Code Playgroud)
在这种情况下我该如何进行?也许这是这个版本的 Provider …
我有一个生成视频文件的应用程序,它可以放在内部存储或 SD 卡中,需要通过 Intent.ACTION_VIEW 由用户首选的视频播放器应用程序打开。该应用程序在面向 API 22 时按预期工作,但是当我尝试将其升级到 27 时遇到了问题。
该应用程序根据存储位置生成不同类型的 URI。对于内部存储,它使用文件 URI,如下所示:
file:///storage/emulated/0/VideoRecorder/2018_06_22_12_25_50.mp4
Run Code Online (Sandbox Code Playgroud)
对于放置在 SD 卡中的视频,它使用内容 URI:
content://com.android.externalstorage.documents/tree/72D1-C625%3Avideostest%2Ftest2/document/72D1-C625%3Avideostest%2Ftest2%2F2018_06_22_12_41_27.mp4
Run Code Online (Sandbox Code Playgroud)
Nougat 不喜欢 file:// URI,所以我使用 FileProvider(下面的代码)来解决这个问题,将文件 URI 转换为其他应用程序可以使用 ACTION_VIEW 打开的内容 URI。我认为 SD 卡 URI 不需要任何更改,因为它已经是内容 URI,但似乎只有默认照片应用程序可以使用 ACTION_VIEW 意图打开该 URI,而用户安装的应用程序(如 VLC Player)则不能,失败,但出现以下异常:
java.lang.SecurityException: Permission Denial: reading com.android.externalstorage.ExternalStorageProvider uri content://com.android.externalstorage.documents/tree/72D1-C625:videostest/test2/document/72D1-C625:videostest/test2/2018_06_22_12_27_45.mp4 from pid=7809, uid=10022 requires android.permission.MANAGE_DOCUMENTS, or grantUriPermission()
Run Code Online (Sandbox Code Playgroud)
检查了 FileProvider 生成的 URI 后,我想我明白了这个问题。它生成的内容 URI 的格式与我用于 SD 卡视频的内容 URI 的格式完全不同:
content://io.github.androidtests.videorecorder.videosfileprovider/external_files/VideoRecorder/2018_06_22_12_25_50.mp4
Run Code Online (Sandbox Code Playgroud)
我的问题是,SD 卡视频是否也需要通过 FileProvider 共享?我该怎么做,因为我只有内容 URI,没有文件,而且 FileProvider 似乎专门设计用于将文件转换为 URI?
编辑:在测试了更多视频播放器应用程序之后,其中一些似乎能够使用内容 URI,就像照片应用程序一样。这只是一些应用程序不支持内容 URI 的情况吗?
代码:
Uri uri …Run Code Online (Sandbox Code Playgroud) 我希望能够通过FileProvider将一些文件(通过发送意图)共享为单个压缩文件,但无需实际创建该文件。
对于意图,您所做的就是添加ArrayList<Uri>作为参数,如下所示:
ArrayList<Uri> uris = MyFileProvider.prepareFileProviderFiles(...)
sharingIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris)
Run Code Online (Sandbox Code Playgroud)
FileProvider 可用于将真实文件传递给外部应用程序。
我不想让我的应用程序中的一些垃圾文件(压缩的文件,仅用于共享)毫无目的地保留下来,以防使用它们的应用程序因某种原因完成、崩溃或停止。
根据 FileProvider 的 API,我应该实现真正的文件处理:
默认情况下,FileProvider 自动返回与 content:// Uri 关联的文件的 ParcelFileDescriptor。要获取 ParcelFileDescriptor,请调用 ContentResolver.openFileDescriptor。要重写此方法,您必须提供您自己的 FileProvider 子类。
所以它返回一个 ParcelFileDescriptor ,但是根据创建 ParcelFileDescriptor 的所有函数,我需要一个真实的文件:
是否有可能让它提供一个并不真正存在的文件,但实际上是不同文件的压缩文件?也许是压缩文件的流?
如果这是不可能的,有什么办法可以避免这些垃圾文件吗?这意味着我可以确定删除我过去共享的压缩文件是安全的吗?
如果连这都不可能,我如何决定何时可以删除它们?只是将它们放在缓存文件夹中吗?我记得操作系统并没有真正自动很好地处理缓存文件夹,而是在需要时删除旧文件。不还是正确的吗?
由于我尝试了很长时间,所以我只会接受我可以自己测试的可行解决方案。
编辑:根据下面的答案,我在这里做了一个小样本。这是它的代码:
显现
...
<provider
android:name=".ZipFilesProvider"
android:authorities="${applicationId}.zip_file_provider"
android:exported="false"
android:grantUriPermissions="true"/>
Run Code Online (Sandbox Code Playgroud)
ZipFilesProvider.kt
import android.content.ContentProvider
import android.content.ContentValues
import android.content.Context
import android.content.pm.ProviderInfo
import android.database.Cursor
import android.database.MatrixCursor
import android.net.Uri
import android.os.Build
import android.os.ParcelFileDescriptor
import android.provider.MediaStore
import …Run Code Online (Sandbox Code Playgroud) 我正在创建电子邮件意图并附加文件(使用 FileProvider),但 Gmail 客户端报告“无法附加文件”。
我以前有这个工作,但现在坏了。我没有更改我的代码(我知道),但已经更新了操作系统和 Gmail 客户端。
// Build email Intent
Uri mailToUri = Uri.fromParts("mailto", "", null);
Intent emailIntent = new Intent(Intent.ACTION_SEND, mailToUri);
emailIntent.setType("text/plain");
emailIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
emailIntent.putExtra(Intent.EXTRA_SUBJECT, "A subject");
emailIntent.putExtra(Intent.EXTRA_TEXT, "A body");
// Build the attachment URI
File attachFile = new File(aDirectory, aFileName);
Uri attachmentUri = FileProvider.getUriForFile(this,
FILE_PROVIDER_AUTHORITY, attachFile);
emailIntent.putExtra(Intent.EXTRA_STREAM, attachmentUri);
// Start the share activity
startActivity(emailIntent);
// Alternative methods for creating the URI
// Uri attachmentUri = Uri.parse("file://"
// + attachFile.getAbsolutePath());
// Uri attachmentUri = Uri.parse("content://"
// + FILE_PROVIDER_AUTHORITY
// …Run Code Online (Sandbox Code Playgroud) 我正在从服务器下载 PDF 文件并将响应正文字节流传递到下面的函数中,该函数将 PDF 文件成功存储在用户下载文件夹中。
@RequiresApi(Build.VERSION_CODES.Q)
fun saveDownload(pdfInputStream: InputStream) {
val values = ContentValues().apply {
put(MediaStore.Downloads.DISPLAY_NAME, "test")
put(MediaStore.Downloads.MIME_TYPE, "application/pdf")
put(MediaStore.Downloads.IS_PENDING, 1)
}
val resolver = context.contentResolver
val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val itemUri = resolver.insert(collection, values)
if (itemUri != null) {
resolver.openFileDescriptor(itemUri, "w").use { parcelFileDescriptor ->
ParcelFileDescriptor.AutoCloseOutputStream(parcelFileDescriptor)
.write(pdfInputStream.readBytes())
}
values.clear()
values.put(MediaStore.Downloads.IS_PENDING, 0)
resolver.update(itemUri, values, null, null)
}
}
Run Code Online (Sandbox Code Playgroud)
现在,一旦该函数返回,我想打开保存的 PDF 文件。我尝试了多种方法来使其正常工作,但选择器总是说没有任何内容可以打开文件。我认为要么仍然存在权限问题(也许我使用的 FileProvider 错误?),要么路径错误,或者可能完全是其他问题。
以下是我尝试过的几个示例:
fun uriFromFile(context: Context, file: File): Uri {
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", file)
}
Run Code Online (Sandbox Code Playgroud)
A)
val openIntent …Run Code Online (Sandbox Code Playgroud) 我已经阅读了关于这个论点的所有答案,但我总是收到收到我的照片的应用程序的错误.
不仅如此方式工作对我来说,所有的应用程序,是这样的(它的工作原理,因为SD卡的文件都是公开的所有应用程序):
final File tmpFile = new File(context.getExternalCacheDir(), "exported.jpg");
Uri tmpFileUri = Uri.fromFile(tmpFile);
Intent shareIntent = new Intent();
shareIntent.setAction(Intent.ACTION_SEND);
shareIntent.setDataAndType(tmpFileUri, "image/jpeg");
shareIntent.putExtra(Intent.EXTRA_STREAM, tmpFileUri);
context.startActivity(Intent.createChooser(shareIntent, context.getString(R.string.share_image)));
Run Code Online (Sandbox Code Playgroud)
现在,我坚持如何共享位于私人文件夹中的文件.我使用了google文档提供的代码:
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.test.myapp.fileprovider"
android:exported="false"
android:grantUriPermissions="true" >
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
...
...
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<files-path name="internal_files" path="/"/>
<cache-path name="internal_cache" path="/" />
</paths>
Run Code Online (Sandbox Code Playgroud)
这是使用该文件共享文件的代码,FileProvider但不适用于任何应用程序,除非是最新的:
final File tmpFile = new File(context.getCacheDir(), "exported.jpg");
Uri tmpFileUri = FileProvider.getUriForFile(context, context.getPackageName() + ".fileprovider", tmpFile);
//Remove the uri permission because we overwrite …Run Code Online (Sandbox Code Playgroud) 我使用这种方法从Uri获取文件路径https://github.com/iPaulPro/aFileChooser/blob/master/aFileChooser/src/com/ipaulpro/afilechooser/utils/FileUtils.java#L257
但是当我传递Uri时会抛出异常("_data"列未找到),如下所示:
public static Uri uriFromFile(Context context, String path) {
if (path == null) return null;
return FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", new File(path));
}
Run Code Online (Sandbox Code Playgroud)
我只需要在我的活动之间传递文件路径..
所以这是场景:
我正在从网络下载 PDF 并将其保存在/0/Download/NSIT - Notices/"fileName".pdf
现在发送 Intent 像这样:
private void openPDF(String fileName) {
Log.d(TAG, "openPDF: called");
Intent intent = new Intent(Intent.ACTION_VIEW);File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/Download", "NSIT - Notices");
File myPDF = new File(Environment.getExternalStorageDirectory() + "/Download/NSIT - Notices", fileName + ".pdf");
Uri uri = FileProvider.getUriForFile(context, BuildConfig.APPLICATION_ID + ".provider", myPDF);
Log.d(TAG, "openPDF: intent with uri: " + uri);
intent.setDataAndType(uri, "application/pdf");
context.startActivity(Intent.createChooser(intent, "Open with..."));
}
Run Code Online (Sandbox Code Playgroud)
现在任何 PDF 阅读器(Google Drive PDF 阅读器、系统 PDF 查看器)都在说
The File Does Not Exist
Run Code Online (Sandbox Code Playgroud)
谷歌没有做任何事情(这就是为什么不包括它的图像)
Mainfest.xml
<provider
android:name="android.support.v4.content.FileProvider" …Run Code Online (Sandbox Code Playgroud) pdf android android-intent android-fileprovider android-storage
我开始一个ACTION_GET_CONTENT意图来选择 PDF:
override fun routeToFilePicker() {
val intent = Intent()
intent.type = MediaType.PDF.toString()
intent.action = Intent.ACTION_GET_CONTENT
activity.startActivityForResult(
Intent.createChooser(intent, "Select PDF"),
REQUEST_CODE_PDF_PICKER
)
}
Run Code Online (Sandbox Code Playgroud)
然后onActivityResult我尝试从 Uri ( ) 创建 PDF content//:path:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == Activity.RESULT_OK && requestCode == REQUEST_CODE_PDF_PICKER ) {
data?.data?.let { pdfUri: Uri ->
val pdfFile: File = pdfUri.toFile() <-- chrash
...
}
}
}
Run Code Online (Sandbox Code Playgroud)
pdfUri.toFile()导致致命异常:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1003, result=-1, data=Intent …Run Code Online (Sandbox Code Playgroud) 我的应用程序通过 ACTION_PICK Intent 收集媒体以获取
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI
内容类型。当我检索我想要的内容的 URI 时,效果很好,我可以使用以下命令查看它:
val intent = Intent(Intent.ACTION_VIEW, myURI)
intent.setDataAndType(myURI,contentResolver.getType(myURI))
intent.addFlags(FLAG_GRANT_READ_URI_PERMISSION)
startActivity(intent)
Run Code Online (Sandbox Code Playgroud)
我使用方法存储 Uri Uri.toString,一旦关闭活动并再次打开它,就会收到以下错误:
java.lang.SecurityException: UID 10315 does not have permission to content://com.miui.gallery.open/raw/%2Fstorage%2Femulated%2F0%2FDCIM%2FCamera%2FVID_20200814_214253.mp4 [user 0]
Run Code Online (Sandbox Code Playgroud)
再往下还有:
Caused by: android.os.RemoteException: Remote stack trace:
at com.android.server.uri.UriGrantsManagerService.checkGrantUriPermission(UriGrantsManagerService.java:1192)
at com.android.server.uri.UriGrantsManagerService.checkGrantUriPermissionFromIntent(UriGrantsManagerService.java:579)
at com.android.server.uri.UriGrantsManagerService.grantUriPermissionFromIntent(UriGrantsManagerService.java:618)
at com.android.server.uri.UriGrantsManagerService$LocalService.grantUriPermissionFromIntent(UriGrantsManagerService.java:1392)
Run Code Online (Sandbox Code Playgroud)
理想情况下,我会获取媒体的文件路径并通过 fileProvider 从文件生成 URI,但我还没有找到从 ContentURI 获取绝对路径的方法。
通读https://commonsware.com/blog/2016/08/10/uri-access-lifetime-shorter-than-you-might-think.html,它更好地解释了正在发生的事情,那么我如何重用或存储我通过 URI 检索到的文件?我可以通过某种方式检索文件路径而不是 URI 吗?