Java 高效加载媒体缩略图
我花了几天时间来解决这个问题,但仍然找不到解决办法。我是Android新手,所以我的代码可能很混乱 我有一个RecyclerView(网格布局),可以显示图像和视频的缩略图。它加载特定文件夹中的媒体文件。但当我启动此活动时,它占用了太多内存 为了加载缩略图,我创建了两个线程 线程1)MediaLoadThread,用于查询SD卡中的媒体文件。它通过游标循环,并将缩略图解码任务队列循环到不同的线程 线程2)ThumbnailLoaderThread对每个缩略图进行解码。它接收内容解析器、媒体类型(图像或视频)和媒体id。它使用basic.getThumbnail()方法。获取缩略图完成后,它将触发对其调用线程(MediaLoadThread)的响应回调 3) 当MediaLoadThread(线程1)收到回调时,它会触发另一个回调,让活动更新给定位置的适配器项。适配器将更新UI,最终缩略图图像视图将从占位符更改为实际缩略图 ::这是我的代码:: 1)MediaLoadThread.javaJava 高效加载媒体缩略图,java,android,multithreading,image,Java,Android,Multithreading,Image,我花了几天时间来解决这个问题,但仍然找不到解决办法。我是Android新手,所以我的代码可能很混乱 我有一个RecyclerView(网格布局),可以显示图像和视频的缩略图。它加载特定文件夹中的媒体文件。但当我启动此活动时,它占用了太多内存 为了加载缩略图,我创建了两个线程 线程1)MediaLoadThread,用于查询SD卡中的媒体文件。它通过游标循环,并将缩略图解码任务队列循环到不同的线程 线程2)ThumbnailLoaderThread对每个缩略图进行解码。它接收内容解析器、媒体类型(
@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);
}
});
}
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);
}
});
}
@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);
}
}
3)LibraryActivity.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);
}
});
}
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);
}
});
}
@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);
}
}
@覆盖
已准备好的公用空白(整型位置,位图缩略图){
if(缩略图!=null&&position
流程是这样的
1) 该活动启动线程1
2) 线程1开始查询文件并启动线程2。它通过光标循环传递媒体id
3) 线程2解码具有给定媒体id的缩略图
4) 解码完成后,线程2将使用结果位图触发对线程1的回调
5) 线程1接收位图并通过回调将位图传递给活动
6) 活动接收缩略图并使用给定位图更新RecyclerView数据
它工作正常,但当系统为此任务分配近50MB内存时。。。考虑到它只加载了100个缩略图,我认为它相当重
:::我所尝试的::
1) 我提取了每个缩略图的URI,并让recyclerview适配器在绑定时加载具有给定URI的图像。它工作正常,没有消耗太多内存,但因为它在项目绑定时加载图像,所以每当我稍微延迟一点滚动屏幕时,它就会重新加载缩略图
2) 我让适配器加载带有直接缩略图路径的缩略图。但当用户清理/.thumbnails文件夹时,它将不起作用
3) 当线程解码缩略图时,我将BitmapFactory.Options samplsize设置为4。但是当它仍然很重,有时甚至更慢的时候
4) 在MediaData对象中,它将缩略图位图作为成员变量保存。因此,在适配器将其加载到ImageView之后,我立即将其设置为null。仍然很重,而且因为我将对象的缩略图设置为null,所以当我向后滚动时,它只显示占位符
我真的不知道。任何帮助都将不胜感激 可以使用图像加载程序库加载图像。该库非常适合图像加载,也可以使用其他库,如etc,来代替手动编码 我尝试使用毕加索,但它加载了实际的图像。不是缩略图。加载图像文件本身不是太重吗(您可以根据布局大小调整图像大小。下面的代码是毕加索。with(上下文)。load(url)。resize(50,50)。centerCrop()。into(图像视图)谢谢。那我应该在哪里添加那一行呢?如果我在适配器的bindViewHolder方法中添加它,每当我滚动时它都会调整图像的大小。好的,我在bindViewHolder方法中添加了这一行。Picasso.with(context)。load(currentData.getUri()).centerCrop().resize(mWidth,mWidth)。进入(holder.mThumbnailIV)稍好一点。但在绑定项目时仍会加载图像。