如何查询 Android MediaStore Content Provider,避免出现孤立图像?

发布于 2024-09-18 07:49:27 字数 3232 浏览 6 评论 0原文

我正在尝试提供一个应用程序内活动,该活动显示照片缩略图 设备的媒体存储,并允许用户选择一个。用户做出后 选择后,应用程序会读取原始的全尺寸图像并对其进行处理。

我使用以下代码在外部的所有图像上创建一个 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 删除)。

孤立条目可能有也可能没有缩略图,具体取决于擅离职守时缩略图是否在实际媒体文件之前生成。最终结果是应用程序显示实际不存在的图像的缩略图。

我有几个问题:

  1. 我是否可以向 MediaStore 内容提供商进行查询以过滤掉 返回的光标中缺少媒体的图像?
  2. 有没有办法或 API 来强制 MediaStore 重新扫描并消除孤立条目?在我的手机上,我安装了 USB,然后卸载了外部介质,这应该会触发重新扫描。但孤儿条目仍然存在。
  3. 或者是我的方法存在根本性错误导致了这个问题?

谢谢。

I'm trying to provide an in-app Activity which displays thumbnails of photos in the
device's media store, and allow the user to select one. After the user makes a
selection, the application reads the original full-size image and does things with it.

I'm using the following code to create a Cursor over all the images on the external
storage:

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

And the following code to load the thumbnail image (Android 2.x code is shown):

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

And the following code to load the original image from the URL once the user makes a selection:

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

The problem I'm seeing on some Android devices, including my own personal phone, is that the
cursor I get from the query in onCreate() contains a few entries for which the actual full-sized image file (JPG or PNG) is missing. (In the case of my phone, the images had been imported and subsequently erased by iPhoto).

The orphaned entries may or may not have thumbnails, depending upon whether thumbnails where generated before the actual media file when AWOL. The end result is that the app displays thumbnails for images that don't actually exist.

I have a few questions:

  1. Is there a query I can make to the MediaStore content provider that will filter out
    images with missing media in the returned Cursor?
  2. Is there a means, or an API to force the MediaStore to rescan, and eliminate the orphan entries? On my phone, I USB-mounted then unmounted the external media, which is supposed to trigger a rescan. But the orphan entries remain.
  3. Or is there something fundamentally wrong with my approach that's causing this problem?

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

夏夜暖风 2024-09-25 07:49:27

好的,我发现了这个代码示例的问题。

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 即可。

这也解决了查找对原始全尺寸图像具有悬空引用的缩略图的问题。

Okay, I've found the problem with this code sample.

In the onCreate() method, I had this line:

mImageCursor = managedQuery( MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
                             projection, selection, selectionArgs, null );

The problem here is that it's querying for the thumbnails, rather than the actual images. The camera app on HTC devices does not create thumbnails by default, and so this query will fail to return images that do not already have thumbnails calculated.

Instead, query for the actual images themselves:

mImageCursor = managedQuery( MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                             projection, selection, selectionArgs, null );

This will return a cursor containing all the full-sized images on the system. You can then call:

Bitmap bm = MediaStore.Images.Thumbnails.getThumbnail(context.getContentResolver(),
        imageId, MediaStore.Images.Thumbnails.MINI_KIND, null);

which will return the medium-sized thumbnail for the associated full-size image, generating it if necessary. To get the micro-sized thumbnail, just use MediaStore.Images.Thumbnails.MICRO_KIND instead.

This also solved the problem of finding thumbnails that have dangling references to the original full-sized images.

我恋#小黄人 2024-09-25 07:49:27

请注意,情况很快就会发生变化, ManagedQuery 方法已被弃用。请改用 CursorLoader(自 api 级别 11 起)。

Please note that things will be changing soon, managedQuery method is deprecated. Use CursorLoader instead(since api level 11).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文