MediaStore.Images.Thumbnails.getThumbnail возвращает неправильную миниатюру вместо NULL

Рассмотрим сценарий, как на этой картинке:

Нативная галерея

Три фото, одно из них большой GIF-файл (3MP).

Я запрашиваю MediaStore, чтобы получить соответствующие миниатюры. Если я инициализирую курсор через CursorLoader с этим sortOrder:

MediaStore.Images.Media.DATE_ADDED + " DESC""

Что происходит? MediaStore возвращает предыдущую успешно полученную миниатюру:

DESC

Ожидаемое поведение: когда MediaStore не может получить миниатюру данного изображения по какой-либо причине, он должен вернуть NULL в соответствии со своим документом Javadoc: "... Возвращает экземпляр Bitmap. Он может быть нулевым если исходное изображение, связанное с origId, не существует или недостаточно памяти."

Если я инициализирую курсор этим sortOrder:

MediaStore.Images.Media.DATE_ADDED + " ASC""

Он работает нормально:

АСЦ

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

Below is my sample code and here is the complete sample project as well as три изображения, используемые для воспроизведения.

package com.example.getimagefrommediastore;

import android.app.Activity;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.content.CursorLoader;
import android.widget.ImageView;
import android.widget.TextView;

public class GetThumbnailsFromMediaStoreSampleActivity extends Activity {

TextView mThumb_id_01;
TextView mThumb_id_02;
TextView mThumb_id_03;
ImageView mImg_01;
ImageView mImg_02;
ImageView mImg_03;
boolean isThumb01 = true; // Simple flag to control this example
boolean isThumb02 = true;
Cursor mCursorLoader;
int mColumnIndex;
long mOrigId; // Original image id associated with thumbnail of interest

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Just initializing views
    mThumb_id_01 = (TextView) findViewById(R.id.thumb_id_01);
    mThumb_id_02 = (TextView) findViewById(R.id.thumb_id_02);
    mThumb_id_03 = (TextView) findViewById(R.id.thumb_id_03);
    mImg_01 = (ImageView) findViewById(R.id.thumb_01);
    mImg_02 = (ImageView) findViewById(R.id.thumb_02);
    mImg_03 = (ImageView) findViewById(R.id.thumb_03);

    // Initializing CursorLoader
    mCursorLoader = initializeCursorLoader();
    mColumnIndex = mCursorLoader.getColumnIndex(MediaStore.Images.Media._ID);

    // Go thru all the images in the device (EXTERNAL_CONTENT_URI)
    // In this example there are only three images
    for (int i = 0; i < mCursorLoader.getCount(); i++) {
        mCursorLoader.moveToPosition(i);
        mOrigId = mCursorLoader.getInt(mColumnIndex);

        // Update views
        chooseViewToUpdate();
    }
}

private Cursor initializeCursorLoader() {
    String[] COLUMNS = {
            MediaStore.Images.Thumbnails._ID, MediaStore.Images.Media.DATA
    };

    CursorLoader cursorLoader = new CursorLoader(
    GetThumbnailsFromMediaStoreSampleActivity.this, // Context
    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, // Uri
    COLUMNS, // Projection
    null, // Selection
    null, // Selection Args

    // Sort Order: DESC = newest first
    // Sort Order: ASC = oldest first

    MediaStore.Images.Media.DATE_ADDED + " DESC");

    // *** NOTE ***
    // With:
    //
    // MediaStore.Images.Media.DATE_ADDED + " ASC"
    //
    // It runs just fine (MediaStore returns 'null' for invalid thumbnails)
    // The problem seems to reside on the " DESC" tag.
    //
    // How bizarre is that?

    return cursorLoader.loadInBackground();
}

private void chooseViewToUpdate() {
    if (isThumb01) {
        updateUI(mThumb_id_01, mImg_01);
        isThumb01 = false;
    } else if (isThumb02) {
        updateUI(mThumb_id_02, mImg_02);
        isThumb02 = false;
    } else {
        updateUI(mThumb_id_03, mImg_03);
    }
}

private void updateUI(TextView textView, ImageView imgView) {
    textView.setText("ID:" + String.valueOf(mOrigId));

    Bitmap mediaStoreThumbmail = MediaStore.Images.Thumbnails.getThumbnail(
            this.getContentResolver(),
            mOrigId,
            MediaStore.Images.Thumbnails.MICRO_KIND, null);

    if (mediaStoreThumbmail != null) {
        imgView.setImageBitmap(mediaStoreThumbmail);
    }
}

Я что-то упускаю? Кто-нибудь знает, что может быть не так?

Я все равно заполнил ошибку в Android.

ИЗМЕНИТЬ

Похоже, что эта проблема исправлена ​​в Lollipop. последний комментарий в этой теме).


person backslash-f    schedule 30.11.2012    source источник
comment
Выведите mOrigId для каждого полученного изображения, оно может быть одинаковым для изображения 2 и 3. Или есть 4-е изображение, которое перезаписывает данные. В вашем коде есть несколько причин, которые могут вызвать такую ​​ошибку. Тем более, что вы получаете только идентификатор изображения в своем запросе ASC/DESC, а затем разрешаете изображение во втором несвязанном запросе.   -  person zapl    schedule 30.11.2012
comment
Привет! На второй картинке (моя вторая ссылка - cl.ly/image/2U0z1Z2b1m1v) я поставил идентификаторы выше картинки. Они разные: 7858, 7854 и 7851. Я не уверен, что вы подразумеваете под тем, что вы получаете только идентификатор изображения в своем запросе ASC/DESC, а затем разрешаете изображение в несвязанном втором запросе. Спасибо, что посмотрели.   -  person backslash-f    schedule 01.12.2012
comment
Я собрал небольшой пример ASC/DESC, который у меня отлично работает (эмулятор 4.0.3). dropbox.com/s/in048v3fh3xae2m/AscDescGallery.zip – Особенно... означает: на первом этапе вы запрашиваете список изображений из медиамагазина. Либо ASC, либо DESC. Затем вы запрашиваете у MediaStore миниатюры в независимом запросе, который не знает / не заботится о том, как вы получили идентификаторы. Таким образом, изменение порядка в первом запросе не должно влиять на результат Thumbnails.getThumbnail, если вы используете правильные идентификаторы.   -  person zapl    schedule 01.12.2012
comment
Я понимаю что ты имеешь ввиду. Что ж, спасибо за пример. У меня сейчас нет доступа к проблемному устройству (ASUS Transformer), но я проверю его в понедельник. Потом вышлю обновление. Просто отметим, что мой пример также работал на эмуляторе 4.0.3, так что это может быть связано с самим устройством. Спасибо.   -  person backslash-f    schedule 01.12.2012
comment
А вот и обновление: я запустил ваш пример на этом устройстве Asus. Та же проблема. На самом деле в этом примере оба запроса (ASC и DESC) получают неправильную миниатюру из Media Store. :D Смотрите: Что у меня есть в галерее: cl.ly/image/1W1L023E2B0k Какой у вас пример ASC возвращает: cl.ly/image/2P1J220j0T2U Что возвращает ваш пример DESC: cl.ly/image/3Y230t1c1G3a Это подтверждает мою идею о проблеме, связанной с устройством. Странный...   -  person backslash-f    schedule 03.12.2012
comment
Действительно странно. Изображение 7851, похоже, содержит ошибки. Сгенерированные эскизы по умолчанию сохраняются в /mnt/sdcard/DCIM/.thumbnails/, а микроминиатюры — в специальный файл (.thumbdata3-[random number]), большие миниатюры сохраняются в виде файлов .jpg. Вы можете проверить, что с ними в порядке, и, возможно, удалить их + принудительно остановить и удалить данные для приложения Media Storage (или переведенного) в настройках системы > приложения > все приложения. Это должно сбросить всю базу данных мультимедиа.   -  person zapl    schedule 03.12.2012
comment
Да, большие пальцы кажутся в порядке в указанном вами месте. Но я все равно сбрасывал базу данных мультимедиа - не повезло. Интересно, какое колдовство использует родная галерея для чтения этого файла, потому что родная галерея может показывать миниатюру И исходную версию без каких-либо проблем. Что ж, похоже, я облажался. :-) Буду следить за статусом бага, который открыл против Android. Большое спасибо за ваше время!   -  person backslash-f    schedule 03.12.2012
comment
Та же ошибка возникает на моем HTC Flyer (Honeycomb), но результат правильный на Nexus7 (Kitkat). Моя прогулка заключается в создании масштабированного растрового изображения непосредственно с использованием исходных имен файлов изображений (столбец DATA), возвращенных из запроса MediaStore.images. Но это намного медленнее, чем получение эскизов напрямую от поставщика контента.   -  person YourBestBet    schedule 27.12.2013


Ответы (2)


Я просто догадываюсь. Когда вы запрашиваете MICRO_KIND, ОС создает новое изображение, которое становится следующим в строке курсора DESC, снова создавая то же изображение.

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

Возможность попробовать ваш код с использованием MINI_KIND и bmoptions.inSampleSize = 2;

 final BitmapFactory.Options bmOptions = new BitmapFactory.Options();
            bmOptions.inSampleSize =2;



                    Bitmap bm = MediaStore.Images.Thumbnails.getThumbnail(
                            context.getContentResolver(), newImageId,
                            MediaStore.Images.Thumbnails.MINI_KIND,
                            bmOptions);
person danny117    schedule 16.09.2014