Как проверить, доступен ли ресурс, указанный Uri? - PullRequest
13 голосов
/ 04 октября 2011

У меня есть ресурс (музыкальный файл), на который указывает Ури.Как я могу проверить, доступен ли он, прежде чем пытаться воспроизвести его с MediaPlayer?

Его Uri хранится в базе данных, поэтому, когда файл удаляется или на внешнем хранилище, которое отключено, тогда я просто получаю исключение, когдаЯ вызываю MediaPlayer.prepare ().

В вышеуказанной ситуации я хотел бы играть системную мелодию звонка по умолчанию.Конечно, я мог бы сделать это после того, как поймал вышеприведенное исключение, но, может быть, есть более элегантное решение?

edit: я забыл упомянуть, что музыкальные файлы Uri на самом деле получены с помощью RingtonePreference.Это означает, что я могу получить указание Uri на мелодию звонка на внутреннем хранилище, внешнем хранилище или на мелодию звонка системы по умолчанию.

Примеры Uri:

  • content: // settings / system / ringtone- для выбора мелодии звонка по умолчанию
  • контент: // media / internal / audio / media / 60 - для мелодии звонка во внутренней памяти
  • контент: // media / external / audio / media / 192 -для рингтона на внешнем хранилище

Я был доволен предложенным методом «новый файл (путь) .exists (), так как он спас меня от упомянутого исключения, но через некоторое время я заметил, что он возвращает false длявсе мои мелодии звонка ... Есть еще идеи?

Ответы [ 4 ]

23 голосов
/ 04 октября 2011

Причина, по которой предлагаемый метод не работает, заключается в том, что вы используете 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 
}

У меня естьне использовал этот метод со звуковыми файлами или внутренним хранилищем, но он должен работать.Запрос должен вернуть одну строку непосредственно в ваш файл.

3 голосов
/ 15 мая 2017

Начиная с Kitkat, вы можете и должны сохранять URI, которые ваше приложение использует при необходимости. Насколько я знаю, существует ограничение в 128 URI, которое вы можете сохранить для каждого приложения, поэтому вы должны максимально использовать эти ресурсы.

Лично я не буду иметь дело с прямыми путями в этом случае, а скорее проверю, существует ли постоянный URI, так как, когда ресурс (файл) удаляется с устройства, ваше приложение теряет права на этот URI, поэтому делает эту проверку как просто как следующее:

getContext().getContentResolver().getPersistedUriPermissions().forEach( {element -> element.uri == yourUri});

Между тем вам не нужно проверять разрешения URI, когда устройство находится ниже уровня API Kitkat.

Обычно при чтении файлов из URI вы собираетесь использовать ParcelFileDescriptor, поэтому он будет выдавать, если для этого конкретного URI файла нет, поэтому вам следует обернуть его блоком try/catch.

1 голос
/ 03 мая 2018

У меня тоже была эта проблема - я действительно хотел проверить, доступен ли 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
}

Если у кого-то есть более элегантная или правильная альтернатива, пожалуйста, оставьте комментарий.

0 голосов
/ 28 сентября 2017

Попробуйте такую ​​функцию, как:

public static boolean checkURIResource(Context context, Uri uri) {
        Cursor cursor = context.getContentResolver().query(uri, null, null, null, null);
        return (cursor != null && cursor.moveToFirst());
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...