Как хранить большие капли в поставщике содержимого 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;
}
Ответы
Ответ 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?