如何检查Uri指向的资源是否可用?

时间:2011-10-04 09:42:48

标签: android 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 - 用于外置存储上的铃声

我对提议的“新文件(路径).exists()方法感到满意,因为它将我从提到的异常中保存下来,但过了一段时间后我注意到它为我的所有铃声选择返回false ... 还有其他想法吗?

6 个答案:

答案 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。