Как запросить контент-провайдера Android MediaStore, избегая потерянных изображений?
Я пытаюсь предоставить активность в приложении, которая отображает миниатюры фотографий в
устройство хранения носителей и разрешить пользователю выбирать один. После того, как пользователь сделает
выбор, приложение читает оригинальное полноразмерное изображение и делает с ним что-то.
Я использую следующий код для создания Cursor
по всем изображениям на внешнем
Хранение:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView( R.layout.image_select );
mGridView = (GridView) findViewById( R.id.image_select_grid );
// Query for all images on external storage
String[] projection = { MediaStore.Images.Media._ID };
String selection = "";
String [] selectionArgs = null;
mImageCursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
projection, selection, selectionArgs, null );
// Initialize an adapter to display images in grid
if ( mImageCursor != null ) {
mImageCursor.moveToFirst();
mAdapter = new LazyCursorAdapter(this, mImageCursor, R.drawable.image_select_default);
mGridView.setAdapter( mAdapter );
} else {
Log.i(TAG, "System media store is empty.");
}
}
И следующий код для загрузки уменьшенного изображения (отображается код Android 2.x):
// ...
// Build URI to the main image from the cursor
int imageID = cursor.getInt( cursor.getColumnIndex(MediaStore.Images.Media._ID) );
Uri uri = Uri.withAppendedPath( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
Integer.toString(imageID) );
loadThumbnailImage( uri.toString() );
// ...
protected Bitmap loadThumbnailImage( String url ) {
// Get original image ID
int originalImageId = Integer.parseInt(url.substring(url.lastIndexOf("/") + 1, url.length()));
// Get (or create upon demand) the micro thumbnail for the original image.
return MediaStore.Images.Thumbnails.getThumbnail(mContext.getContentResolver(),
originalImageId, MediaStore.Images.Thumbnails.MICRO_KIND, null);
}
И следующий код для загрузки исходного изображения из URL-адреса после выбора пользователя:
public Bitmap loadFullImage( Context context, Uri photoUri ) {
Cursor photoCursor = null;
try {
// Attempt to fetch asset filename for image
String[] projection = { MediaStore.Images.Media.DATA };
photoCursor = context.getContentResolver().query( photoUri,
projection, null, null, null );
if ( photoCursor != null && photoCursor.getCount() == 1 ) {
photoCursor.moveToFirst();
String photoFilePath = photoCursor.getString(
photoCursor.getColumnIndex(MediaStore.Images.Media.DATA) );
// Load image from path
return BitmapFactory.decodeFile( photoFilePath, null );
}
} finally {
if ( photoCursor != null ) {
photoCursor.close();
}
}
return null;
}
Проблема, которую я вижу на некоторых устройствах Android, включая мой личный телефон, заключается в том, что
курсор, который я получаю из запроса в onCreate()
, содержит несколько записей, для которых отсутствует фактический полноразмерный файл изображения (JPG или PNG). (В случае моего телефона изображения были импортированы и впоследствии стерты iPhoto).
Приоритетные записи могут иметь или не иметь эскизы, в зависимости от того, будут ли миниатюры, сгенерированные до фактического медиафайла, когда AWOL. Конечным результатом является то, что приложение отображает миниатюры для изображений, которые на самом деле не существуют.
У меня есть несколько вопросов:
- Есть ли запрос, который я могу сделать для поставщика контента
MediaStore
, который будет отфильтровываться
изображения с отсутствующим носителем в возвращаемом Cursor
?
- Есть ли средство или API для принудительного повторного сканирования
MediaStore
и устранения сиротских записей? На моем телефоне я подключил USB-устройство, затем отключил внешний носитель, который должен запускать повторное сканирование. Но сиротские записи остаются.
- Или есть что-то принципиально неправильное в моем подходе, вызывающем эту проблему?
Спасибо.
Ответы
Ответ 1
Хорошо, я нашел проблему с этим примером кода.
В методе onCreate()
у меня была следующая строка:
mImageCursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
projection, selection, selectionArgs, null );
Проблема заключается в том, что он запрашивает миниатюры, а не фактические изображения. Приложение камеры на устройствах HTC по умолчанию не создает миниатюры, поэтому этот запрос не сможет вернуть изображения, которые еще не вычисленны эскизы.
Вместо этого запросите сами изображения:
mImageCursor = managedQuery( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
projection, selection, selectionArgs, null );
Это вернет курсор, содержащий все полноразмерные изображения в системе. Вы можете позвонить:
Bitmap bm = MediaStore.Images.Thumbnails.getThumbnail(context.getContentResolver(),
imageId, MediaStore.Images.Thumbnails.MINI_KIND, null);
который вернет миниатюру среднего размера для соответствующего полноразмерного изображения, генерируя его, если необходимо. Чтобы получить мини-миниатюру, просто используйте MediaStore.Images.Thumbnails.MICRO_KIND
.
Это также решило проблему поиска миниатюр, которые обмениваются ссылками на оригинальные полноразмерные изображения.
Ответ 2
Обратите внимание, что ситуация скоро изменится, метод managedQuery устарел. Вместо этого используйте CursorLoader (начиная с уровня api 11).