Как сделать запрос Android MediaStore Content Provider, избегая осиротевших изображений? - PullRequest
37 голосов
/ 10 сентября 2010

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

Я использую следующий код для создания 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. Конечным результатом является то, что приложение отображает эскизы для изображений, которые на самом деле не существуют.

У меня есть несколько вопросов:

  1. Могу ли я сделать запрос поставщику контента MediaStore, который отфильтрует изображения с отсутствующим носителем в возвращенных Cursor?
  2. Есть ли средство или API, чтобы заставить MediaStore выполнить повторное сканирование и удалить потерянные записи? На моем телефоне я смонтировал USB, затем отключил внешний носитель, который должен вызвать повторное сканирование. Но оставшиеся сиротами записи остаются.
  3. Или в моем подходе что-то не так, что вызывает эту проблему?

Спасибо.

Ответы [ 2 ]

61 голосов
/ 12 февраля 2011

Хорошо, я обнаружил проблему с этим примером кода.

В методе 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.

Это также решило проблему с поиском миниатюр, которые имеют висячие ссылки на оригинальные полноразмерные изображения.

7 голосов
/ 25 августа 2011

Обратите внимание, что скоро все изменится, метод managedQuery устарел. Вместо этого используйте CursorLoader (начиная с уровня API 11).

...