有效加载媒体缩略图

时间:2016-07-08 12:11:46

标签: java android multithreading image

我花了几天时间来解决这个问题,但仍无法找到解决方案。我是Android的新手,所以我的代码可能非常混乱!

我有一个RecyclerView(网格布局),可以显示图像和视频的缩略图。它将媒体文件加载到特定文件夹中。但是当我启动这项活动时,它会占用大量内存!

为了加载缩略图,我创建了两个线程。

线程1)MediaLoadThread ,用于查询SDCard中的媒体文件。它循环通过游标和队列缩略图解码任务到不同的线程。

线程2)ThumbnailLoaderThread ,用于解码每个缩略图。它接收内容解析器,媒体类型(图像或视频)和媒体ID。它使用基本的.getThumbnail()方法。在获取缩略图之后,它会触发对其调用者线程(MediaLoadThread)的响应回调。

3)当MediaLoadThread(线程1)收到回调时,它会触发另一个回调,让活动更新给定位置的适配器项。适配器更新UI,最后缩略图ImageView从占位符更改为实际缩略图。

:::这是我的代码:::

1)MediaLoadThread.java

@Override
public void run() {
    mMediaDataArr.clear();
    mLoaderThread.start(); // Prepping the thread 2
    mLoaderThread.prepareHandler(); 

      // .... SD Card query stuff .....     

        if (mediaCursor != null && mediaCursor.moveToFirst()) {
            do {
                mMediaDataArr.add(new MediaData(videoCursor.getInt(columnIndexId),
                        mediaCursor.getLong(columnIndexDate), //ID
                        mediaCursor.getInt(columnIndexType), //MEDIA TYPE
                        null); //THUMBNAIL BITMAP (NULL UNTIL THE ACTUAL THUMBNAIL IS DECODED)
            } while (mediaCursor.moveToNext());
            mediaCursor.close();
            mResponseHandler.post(new Runnable() {
                @Override
                public void run() {
                // This callback lets the activity activate the adapter and recyclerview so that the user can interact with recyclerview before the app finishes decoding thumbnails. 
                    mCallback.onVideoLoaded(mMediaDataArr); 
                }
            });

            //Passing tasks to thread 2
            for (int i = 0; i < mMediaDataArr.size(); i++) {
                mLoaderThread.queueTask(
                        mMediaDataArr.get(i).getmMediaType(),
                        i, mMediaDataArr.get(i).getmMediaId());
            }
        }
    }
}

// This is triggered by thread 2 when it finishes decoding 
@Override
public void onThumbnailLoaded(final int position, final Bitmap thumbnail) {
        mResponseHandler.post(new Runnable() {
            @Override
            public void run() {
                mCallback.onThumbnailPrepared(position, thumbnail);
            }
        });
}

2)ThumbnailLoaderThread.java

public void queueTask(int mediaType, int position, int videoId) {
    mWorkerHandler.obtainMessage(mediaType, position, videoId).sendToTarget();
}

public void prepareHandler() {
    mWorkerHandler = new Handler(getLooper(), new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            int type = msg.what;
            final int position = msg.arg1;
            int videoId = msg.arg2;
            try {
                if (type == MediaStore.Files.FileColumns.MEDIA_TYPE_IMAGE) {
                    Bitmap newThumb = MediaStore.Images.Thumbnails
                            .getThumbnail(mCr, videoId,
                                    MediaStore.Images.Thumbnails.MINI_KIND, null);
                    postResult(position, newThumb);

                } else if (type == MediaStore.Files.FileColumns.MEDIA_TYPE_VIDEO) {
                    Bitmap newThumb = MediaStore.Video.Thumbnails
                            .getThumbnail(mCr, videoId,
                                    MediaStore.Video.Thumbnails.MINI_KIND, null);
                    postResult(position, newThumb);
                } 
            } catch (Exception e) {
                e.printStackTrace();
            }
            return true;
        }
    });
}

private void postResult(final int position, final Bitmap newThumb) {
        mResponseHandler.post(new Runnable() {
            @Override
            public void run() {
                mCallback.onThumbnailLoaded(position, newThumb);
            }
        });
}

3)LibraryActivity.java

@Override
public void onThumbnailPrepared(int position, Bitmap thumbnail) {
    if (thumbnail != null && position < mData.size()) {
        MediaData updatedData = mData.get(position);
        updatedData.setmThumbnail(thumbnail);
        mData.set(position, updatedData);
        mVideoAdapter.notifyItemChanged(position);
    }
}

流程是这样的。

1)活动开始第1个线程。

2)线程1开始查询文件并启动线程2.它传递循环通过光标的媒体id。

3)线程2使用给定的媒体ID解码缩略图。

4)解码完成后,线程2用结果位图触发对线程1的回调。

5)线程1接收位图并通过回调将位图传递给活动。

6)Activity接收缩略图并使用给定的位图更新RecyclerView数据。

它运行正常,但是当系统为此任务分配了近50MB的内存时...考虑到它只加载了100个缩略图,我认为它非常重。

:::我尝试过的东西:::

1)我提取了每个缩略图的URI,并让recyclelerview适配器在绑定时使用给定的URI加载图像。它工作正常并没有消耗那么多内存,但是因为它在项目绑定时加载图像,所以每当我滚动屏幕一点点延迟时它都会重新加载缩略图。

2)我让适配器使用直接缩略图路径加载缩略图。但是当用户清理/.thumbnails文件夹时它将无法工作。

3)当线程解码缩略图时,我将BitmapFactory.Options samplsize设置为4。但是当它仍然很重,有时甚至更慢......

4)在MediaData对象中,它将缩略图位图保存为成员变量。所以我在适配器将它加载到ImageView后立即将其设为null。仍然很重,因为我将对象的缩略图变为null,它只是在我向后滚动时显示占位符。

我真的不知道。任何帮助将不胜感激!!

1 个答案:

答案 0 :(得分:1)

您可以使用nostra universal图像加载程序库来加载图像。这个库非常适合图像加载,还有一些其他库,如Picassoglide等可用,您可以使用它们而不是手动编码。