如何检查Uri指向的资源是否可用?
我有 Uri 指向的资源(音乐文件)。在尝试使用 MediaPlayer 播放之前如何检查它是否可用?
它的 Uri 存储在数据库中,因此当文件被删除或在卸载的外部存储上时,当我调用 MediaPlayer.prepare() 时,我只会收到异常。
在上述情况下我想播放系统默认铃声。我当然可以在捕获上述异常后这样做,但也许有一些更优雅的解决方案?
编辑: 我忘了提及 Uri 的音乐文件实际上是通过使用 RingtonePreference 获取的。这意味着我可以让 Uri 指向内部存储、外部存储上的铃声或默认系统铃声。
Uri 的示例为:
- content://settings/system/ringtone - 用于选择默认铃声
- content://media/internal/audio/media/60 - 用于内部存储上的铃声
- content://media/external/audio/media/192 - 对于外部存储上的铃声,
我对提议的“新 File(path).exists() 方法感到满意,因为它使我免于提到的异常,但一段时间后,我注意到它对于我所有的铃声选择都返回 false... 还有其他想法吗?
I have resource (music file) pointed by Uri. How can I check if it is available before I try to play it with MediaPlayer?
Its Uri is stored in database, so when the file is deleted or on external storage that is unmounted, then I just get exception when I call MediaPlayer.prepare().
In above situation I would like to play systems default ringtone. I could of course do that after I catch above exception, but maybe there is some more elegant solution?
edit:
I forgot to mention that music files Uri's are actually acquired by using RingtonePreference. This means that I can get Uri pointing to ringtone on Internal Storage, External Storage or to default systems ringtone.
Uri's examples are:
- content://settings/system/ringtone - for choosing default ringtone
- content://media/internal/audio/media/60 - for ringtone on Internal Storage
- content://media/external/audio/media/192 - for ringtone on External Storage
I was happy with proposed "new File(path).exists() method, as it saved me from mentioned exception, but after some time I noticed that it returns false for all of my ringtone choices...
Any other ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
建议的方法不起作用的原因是您使用的是
ContentProvider
URI 而不是实际的文件路径。要获取实际的文件路径,您必须使用光标来获取文件。假设 String contentUri 等于内容 URI,例如 content://media/external/audio/media/192
我还没有在声音文件或内部文件中使用过此方法存储,但它应该可以工作。该查询应将一行直接返回到您的文件。
The reason the proposed method doesn't work is because you're using the
ContentProvider
URI rather than the actual file path. To get the actual file path, you have to use a cursor to get the file.Assuming
String contentUri
is equal to the content URI such ascontent://media/external/audio/media/192
I haven't used this method with sound files or internal storage, but it should work. The query should return a single row directly to your file.
我也遇到了这个问题 - 我真的想在尝试加载 Uri 之前检查它是否可用,因为不必要的失败最终会挤满我的 Crashlytics 日志。
自从 StorageAccessFramework (SAF)、DocumentProviders 等出现后,处理 Uris 变得更加复杂。这就是我最终使用的:
如果有人有更优雅或正确的替代方案,请发表评论。
I too had this problem - I really wanted to check if a Uri was available before trying to load it, as unnecessary failures would end up crowding my Crashlytics logs.
Since the arrival of the StorageAccessFramework (SAF), DocumentProviders, etc., dealing with Uris has become more complicated. This is what I eventually used:
If someone has a more elegant -- or correct -- alternative, please do comment.
尝试使用如下函数:
Try a function like:
对于那些仍在寻找解决方案的人[截至 2020 年 12 月,工作完美]并且在所有边缘情况下都表现得如预期,解决方案如下:
如果与 URI 对应的文件存在,那么您将有一个输入流对象一起工作,否则会抛出异常。
如果文件确实存在,请不要忘记关闭输入流。
注意:
上面的 DocumentFile 代码行确实处理了大多数边缘情况,但我发现,如果以编程方式创建文件并将其存储在某个文件夹中,则用户将访问该文件夹并手动删除文件(当应用程序运行时),DocumentFile.fromSingleUri 错误地表示该文件存在。
For those still looking out for a solution [works perfectly fine as of Dec 2020] and behaves as expected for all edge cases, the solution is a follows:
If the file corresponding to the URI exists, then you will have an input stream object to work with, else an exception will be thrown.
Do not forget to close the input stream if the file does exist.
NOTE:
The above lines of code for DocumentFile, does handle most edge cases, but what I found out was that if a file is created programmatically and stored in some folder, the user then visits the folder and manually deletes the file (while the app is running), DocumentFile.fromSingleUri wrongly says that the file exists.
从 Kitkat 开始,您可以而且应该在必要时保留应用程序使用的 URI。据我所知,每个应用程序可以保留 128 个 URI 限制,因此您可以最大限度地利用这些资源。
就我个人而言,在这种情况下,我不会处理直接路径,而是检查持久化 URI 是否仍然存在,因为当从设备中删除资源(文件)时,您的应用程序将失去对该 URI 的权限,因此使检查变得像以下:
同时,当设备低于 Kitkat API 级别时,您无需检查 URI 权限。
通常,当从 URI 读取文件时,您将使用
ParcelFileDescriptor
,因此如果没有可用于该特定 URI 的文件,它将抛出异常,因此您应该使用try/catch< /代码> 块。
As of Kitkat you can, and you should, persist URIs that your app uses if necessary. As far as I know, there's a 128 URI limit you can persist per app, so it's up to you to maximize usage of those resources.
Personally I wouldn't deal with direct paths in this case, but rather check if persisted URI still exists, since when resource (a file) is deleted from a device, your app loses rights to that URI therefore making that check as simple as the following:
Meanwhile you won't need to check for URI permissions when a device is below Kitkat API level.
Usually, when reading files from URIs you're going to use
ParcelFileDescriptor
, thus it's going to throw if no file is available for that particular URI, therefore you should wrap it withtry/catch
block.GitHub
GitHub ???? https://github.com/javakam/FileOperator/blob/master/library_core/src/main/java/ando/file/core/FileUtils.kt#L317
Based on the hottest solution, give me a solution (compatible with Q, P):
DocumentProvider 应用于 uri 时的方法很少,如果 uri 下没有文档,则返回 null。我选择 getType(uri) 它返回文档/文件的 mime 类型。如果 uri 中不存在表示的文档/文件,则返回 null。因此,要检测文档/文件是否存在,您可以使用如下方法。
提到的其他方法(例如查询 uri 以获取文档 ID 或打开输入流/输出流)不起作用,因为如果文档/文件不存在,它们会抛出 filenotfound 异常,从而导致应用程序崩溃。
如果文档/文件不存在,您可以尝试其他返回 null 而不是抛出 filenotfoundException 的方法。
There are few methods of DocumentProvider when applied to uri, return null if there is no document underlying uri. I chose getType(uri) which returns mime type of the document/file. If no document/file exists represented in uri, it returns null. Hence, to detect whether documennt/file exists or not, you can use this method like below.
Other methods mentioned like querying the uri to get documentID or opening inputstream/outputstream did not work because they throw filenotfound exception if document/file does not exist, which resulted in crashing of app.
You may attempt other methods which return null instead of throwing filenotfoundexception, if document/file does not exist.