Как хранить большие капли в поставщике контента Android? - PullRequest
13 голосов
/ 07 октября 2010

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

Если вы выставляете байтовые данные, это слишком большой, чтобы положить в саму таблицу - такой как большой растровый файл - поле, которое предоставляет данные клиентам должен фактически содержать контент: URI строка. Это поле, которое дает доступ клиентов к файлу данных. запись также должна иметь другое поле, с именем "_data", в котором перечислены точные Путь к файлу на устройстве для этого файла. Это поле не предназначено для чтения клиентом, но ContentResolver. Клиент позвонит ContentResolver.openInputStream () на пользовательское поле, содержащее 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));

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

  1. Как правильно восстановить сохраненный / восстановленный URI? (это текст в таблице)
  2. Реализация 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;
}

Ответы [ 3 ]

12 голосов
/ 02 декабря 2010

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

Когда вы выполните 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);
}
1 голос
/ 17 февраля 2013

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);
}

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

0 голосов
/ 13 ноября 2010

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

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...