我有Uri指出的资源(音乐文件)。在尝试使用MediaPlayer播放之前,如何检查它是否可用?
它的Uri存储在数据库中,所以当删除文件或卸载的外部存储时,我只是在调用MediaPlayer.prepare()时遇到异常。
在上述情况下我想播放系统默认铃声。在我遇到上述异常后,我当然可以这样做,但也许有一些更优雅的解决方案?
编辑: 我忘了提到音乐文件Uri实际上是通过使用RingtonePreference获得的。这意味着我可以让Uri指向内部存储,外部存储或默认系统铃声的铃声。
Uri的例子是:
我对提议的“新文件(路径).exists()方法感到满意,因为它将我从提到的异常中保存下来,但过了一段时间后我注意到它为我的所有铃声选择返回false ... 还有其他想法吗?
答案 0 :(得分:23)
提出的方法不起作用的原因是因为您使用的是ContentProvider
URI而不是实际的文件路径。要获取实际的文件路径,必须使用游标来获取文件。
假设String contentUri
等于内容URI,例如content://media/external/audio/media/192
ContentResolver cr = getContentResolver();
String[] projection = {MediaStore.MediaColumns.DATA}
Cursor cur = cr.query(Uri.parse(contentUri), projection, null, null, null);
if (cur != null) {
if (cur.moveToFirst()) {
String filePath = cur.getString(0);
if (new File(filePath).exists()) {
// do something if it exists
} else {
// File was not found
}
} else {
// Uri was ok but no entry found.
}
cur.close();
} else {
// content Uri was invalid or some other error occurred
}
我没有将这种方法用于声音文件或内部存储,但它应该工作。查询应该将一行直接返回到您的文件。
答案 1 :(得分:3)
从Kitkat开始,您可以,并且应该保留应用在必要时使用的URI。据我所知,每个应用程序可以保留128个URI限制,因此您可以最大限度地利用这些资源。
在这种情况下我个人不会处理直接路径,而是检查持久URI是否仍然存在,因为当从设备中删除资源(文件)时,您的应用程序将失去对该URI的权限,因此检查如下简单:
getContext().getContentResolver().getPersistedUriPermissions().forEach( {element -> element.uri == yourUri});
同时,当设备低于Kitkat API级别时,您不需要检查URI权限。
通常,当您从URI中读取文件时,您将使用ParcelFileDescriptor
,因此如果没有可用于该特定URI的文件,它将会抛出,因此您应该使用{ {1}}阻止。
答案 2 :(得分:1)
我也有这个问题 - 我真的想在尝试加载之前检查一下Uri是否可用,因为不必要的失败最终会挤满我的Crashlytics日志。
由于StorageAccessFramework(SAF),DocumentProviders等的到来,处理Uris变得更加复杂。这就是我最终使用的:
fun yourFunction() {
val uriToLoad = ...
val validUris = contentResolver.persistedUriPermissions.map { uri }
if (isLoadable(uriToLoad, validUris) != UriLoadable.NO) {
// Attempt to load the uri
}
}
enum class UriLoadable {
YES, NO, MAYBE
}
fun isLoadable(uri: Uri, granted: List<Uri>): UriLoadable {
return when(uri.scheme) {
"content" -> {
if (DocumentsContract.isDocumentUri(this, uri))
if (documentUriExists(uri) && granted.contains(uri))
UriLoadable.YES
else
UriLoadable.NO
else // Content URI is not from a document provider
if (contentUriExists(uri))
UriLoadable.YES
else
UriLoadable.NO
}
"file" -> if (File(uri.path).exists()) UriLoadable.YES else UriLoadable.NO
// http, https, etc. No inexpensive way to test existence.
else -> UriLoadable.MAYBE
}
}
// All DocumentProviders should support the COLUMN_DOCUMENT_ID column
fun documentUriExists(uri: Uri): Boolean =
resolveUri(uri, DocumentsContract.Document.COLUMN_DOCUMENT_ID)
// All ContentProviders should support the BaseColumns._ID column
fun contentUriExists(uri: Uri): Boolean =
resolveUri(uri, BaseColumns._ID)
fun resolveUri(uri: Uri, column: String): Boolean {
val cursor = contentResolver.query(uri,
arrayOf(column), // Empty projections are bad for performance
null,
null,
null)
val result = cursor?.moveToFirst() ?: false
cursor?.close()
return result
}
如果某人有更优雅或更正确的选择,请发表评论。
答案 3 :(得分:0)
对于那些仍在寻找解决方案的人[截至2020年12月,它的工作状况非常好],并且在所有极端情况下的行为均符合预期,解决方案如下:
boolean bool = false;
if(null != uri) {
try {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
inputStream.close();
bool = true;
} catch (Exception e) {
Log.w(MY_TAG, "File corresponding to the uri does not exist " + uri.toString());
}
}
如果存在与URI相对应的文件,则将有一个输入流对象可以使用,否则将引发异常。
如果文件确实存在,请不要忘记关闭输入流。
注意:
DocumentFile sourceFile = DocumentFile.fromSingleUri(context, uri);
boolean bool = sourceFile.exists();
DocumentFile的上述代码行确实可以处理大多数情况,但是我发现,如果以编程方式创建文件并将其存储在某个文件夹中,则用户可以访问该文件夹并手动删除该文件(应用正在运行),DocumentFile.fromSingleUri错误地指出该文件存在。
答案 4 :(得分:-1)
尝试类似的功能:
public static boolean checkURIResource(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
return (cursor != null && cursor.moveToFirst());
}
答案 5 :(得分:-1)
应用于uri时,DocumentProvider的方法很少,如果uri中没有文档,则返回null。我选择了getType(uri),它返回文档/文件的mime类型。如果不存在以uri表示的文档/文件,则返回null。因此,要检测文件/文件是否存在,可以使用如下所示的方法。
reduce
提到的其他方法(例如查询uri以获取documentID或打开inputstream / outputstream)不起作用,因为如果document / file不存在,它们会抛出filenotfound异常,从而导致应用程序崩溃。
如果文档/文件不存在,则可以尝试其他方法,这些方法返回null而不是抛出filenotfoundexception。