我花了几天时间来解决这个问题,但仍无法找到解决方案。我是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,它只是在我向后滚动时显示占位符。
我真的不知道。任何帮助将不胜感激!!
答案 0 :(得分:1)
您可以使用nostra universal图像加载程序库来加载图像。这个库非常适合图像加载,还有一些其他库,如Picasso,glide等可用,您可以使用它们而不是手动编码。