Подтвердить что ты не робот

Как хранить большие капли в поставщике содержимого Android?

У меня есть несколько больших файлов (изображений и видео), которые мне нужно хранить в контент-провайдере. Документация по Android указывает...

Если вы выставляете данные байта, слишком большой, чтобы положить в стол сам - таких как большой файл растрового изображения - поле, которое предоставляет данные клиентам должен содержать контент: URI строка. Это поле, которое дает клиенты получают доступ к файлу данных. запись должна также иметь другое поле, с именем "_data", в котором перечислены путь к файлу на этом файле. Это поле не предназначено для чтения клиентом, но ContentResolver. Клиент будет звонить ContentResolver.openInputStream() on поле, обращенное к пользователю, содержащее URI для предмета. ContentResolver будет запросите поле "_data" для этого запись, а потому, что она выше разрешений, чем клиент, он должен иметь доступ к этому файлу напрямую и возвратить прочитанную оболочку для файла для клиента. - http://developer.android.com/guide/topics/providers/content-providers.html#creating

Мне трудно найти пример. В частности, я хочу использовать растровое изображение в контексте ImageView. Рассмотрим следующий код квазикода (он не работает)...

ImageView iv = ....
String iconUri = cursor.getString(cursor.getColumnIndex(Table.ICON));
iv.setImageURI(Uri.parse(iconUri));

Замечания/Проблемы...

  • Как восстановить восстановленный или восстановленный uri правильно? (это текст в таблице)
  • Реализация setImageURI использует разрешение контента openInputStream, поэтому это должно работать.

    String scheme = mUri.getScheme();
    ...
    } else if (ContentResolver.SCHEME_CONTENT.equals(scheme)
            || ContentResolver.SCHEME_FILE.equals(scheme)) {
      try {
        d = Drawable.createFromStream(
                mContext.getContentResolver().openInputStream(mUri),
                null);
    

    - база/основа/ядро ​​/Java/Android/виджет/ImageView.java

Я заработал. Я сделал намек на MediaStore и MediaProvider. Файлы, содержащие данные, называются на основе поставщика контента (каталога), имени столбца, идентификатора строки и типа носителя. Затем распознаватель содержимого получает файловый дескриптор так...

Uri iconUri = Uri.withAppendedPath(Table.getUri(cursor), Table.ICON);
ib.setImageURI(iconUri);

... и поставщик контента отвечает натурой...

@Override
public ParcelFileDescriptor openFile (Uri uri, String mode) {
int imode = 0;
if (mode.contains("w")) imode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY;
if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;
List<String> pseg = uri.getPathSegments();
if (pseg.size() < 3) return null;

try {
    File filePath = filePathFromRecord(pseg.get(2), pseg.get(1));
    return ParcelFileDescriptor.open(filePath, imode);
} catch (FileNotFoundException e) {
    e.printStackTrace();
}
return null;
}
4b9b3361

Ответ 1

Решение породы дает в нижней половине вопроса в основном правильно. Я попытаюсь добавить здесь несколько подробностей.

Когда вы выполните getContentResolver().openInputStream(...), контент-резольвер отправится к вашему провайдеру контента и вызовет его метод openFile. Вот как выглядит openFile ContentProvider.java:

public ParcelFileDescriptor openFile(Uri uri, String mode)
     throws FileNotFoundException {
 throw new FileNotFoundException("No files supported by provider at "
         + uri);
}

Итак, это объясняет, откуда происходит ошибка "Без файлов...". Вы обойдете это, переопределив метод openFile в вашем подклассе и предоставив свою собственную реализацию. Он опрятен: вы отлично контролируете, где ваши файлы размещаются, когда любой клиент делает openInputStream или openOutputStream.

Пример кода в вопросе о пороге дает подсказку, как может выглядеть реализация. Здесь моя слегка измененная версия, которая также создает каталоги и файлы по мере необходимости. Я новичок в этом, так что это может быть не оптимальный способ делать что-то, но он дает представление. Во-первых, вероятно, следует проверить, доступно ли внешнее хранилище.

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
    File root = new File(Environment.getExternalStorageDirectory(), 
            "/Android/data/com.example.myapp/cache");
    root.mkdirs();
    File path = new File(root, uri.getEncodedPath());
    // So, if the uri was content://com.example.myapp/some/data.xml,
    // we'll end up accessing /Android/data/com.example.myapp/cache/some/data.xml

    int imode = 0;
    if (mode.contains("w")) {
            imode |= ParcelFileDescriptor.MODE_WRITE_ONLY;
            if (!path.exists()) {
                try {
                    path.createNewFile();
                } catch (IOException e) {
                    // TODO decide what to do about it, whom to notify...
                    e.printStackTrace();
                }
            }
    }
    if (mode.contains("r")) imode |= ParcelFileDescriptor.MODE_READ_ONLY;
    if (mode.contains("+")) imode |= ParcelFileDescriptor.MODE_APPEND;        

    return ParcelFileDescriptor.open(path, imode);
}

Ответ 2

Android предоставляет вспомогательный метод openFileHelper(), который упрощает реализацию метода openFile(). Все, что вам нужно сделать, чтобы использовать этот метод, - это указать расположение файла в столбце с именем "_data".

@Override
public ParcelFileDescriptor openFile(Uri uri, String mode)
      throws FileNotFoundException {
   if (URI_MATCHER.match(uri) != PHOTO_ID) {
      throw new IllegalArgumentException
            ("URI invalid. Use an id-based URI only.");
   }
   return openFileHelper(uri, mode);
}

Нажмите здесь для подробного описания

Ответ 3

Однако есть проблемы с написанием файлов. Как поставщик контента знает, что запись завершена?

При закрытии OutputStream, полученном с помощью ContentResolver.openOutputStream() Я желаю соответствующего (настраиваемого) ContentProvider для выполнения некоторых пользовательских действий. В настоящее время я использую создание FileObserver в временном файле но это похоже на неправильный способ сделать это. Моя первая мысль заключалась в подтипе ParcelFileDescriptor, который был создан ContentProvider.openFile(), но это, похоже, не работает для меня.

При этом были некоторые ошибки, которые привели к использованию MyParcelFileDescriptor.

  • Я не гарантировал, что файловый дескриптор не будет предварительно собран с мусором.
  • Я не пытался переопределить finally() (документация, похоже, предполагает, что место для этого, но закрыть(), казалось правильным выбором для меня.

Нижняя строка, есть ли какое-либо взаимодействие между файловыми объектами, как показано ContentResolver, а ContentProvider?