【发布时间】:2020-12-06 01:35:52
【问题描述】:
我需要在 RecyclerView 中显示一些图像。 当用户滚动列表时,这些图像应该“即时”呈现。渲染每张图片需要很长时间:50-500ms。在图像显示给用户之前,会显示一个进度条。
由于渲染时间长,这部分被放置到 AsyncTask 中。
请看下面的代码:
class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
class ViewHolder extends RecyclerView.ViewHolder {
ImageView myImg;
ProgressBar myProgressBar;
public ViewHolder(View view) {
super(itemView);
myImg = view.findViewById(R.id.myImg);
myProgressBar = view.findViewById(R.id.myProgressBar);
}
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
holder.myProgressBar.setVisibility(View.VISIBLE);
AsyncTask.execute(() -> {
Bitmap bitmap = longRenderingFunction();
holder.myImg.post(() -> {
holder.myImg.setImageBitmap(bitmap);
holder.myProgressBar.setVisibility(View.GONE);
如果用户正在缓慢滚动列表,例如每秒 1-2 个屏幕,图像加载正确。有时可以注意到进度条,它很快就消失了。
但如果用户滚动速度非常快,图像会出现然后被替换,有时会被替换两次:
总的来说,在快速滚动时很明显:
- 调用多个
onBindViewHolder,并启动每个可回收物品ViewHolder多个AsyncTasks。 - 即使是针对同一项目触发了新的
onBindViewHolder,旧的AsyncTask也会继续运行 - 然后
AsyncTasks为同一个ViewHolder一个接一个完成。 - 每个
AsyncTask将自己的生成位图放到ImageView。
我的意图是:
- 最低:不要从过时的
setImageBitmapAsyncTasks - 最大:尽快停止已经过时的
AsyncTasks以节省系统资源
我希望听到一些提示或解决此问题的方法。
【问题讨论】:
-
你在正确的轨道上。只需取消
AsyncTask。棘手的部分是在哪里保存对AsyncTask的引用以让您取消它。我认为您应该在适配器中创建一个HashMap<int, AsyncTask>来存储每个位置的每个任务。 -
虽然您可以自行管理,但我强烈建议您查看诸如 Glide 之类的库,这些库旨在为您在加载图像时管理大量繁重的工作。
-
@CarsonHolzheimer 取消任务是个好主意。在这里,我仍然缺少在 Android 中执行此操作的正确方法。我的渲染器被打包成一个非常紧凑的可运行文件。
AsyncTask.execute也是一个紧凑的结构。我想保持紧凑。如果我创建一个正确AsyncTask,那么它就会变成一个带有doInBackground等的怪物。所以我在这里考虑ExecutorService,它可以返回Future<>,并且可以取消任何时间。你怎么看?
标签: android android-recyclerview android-asynctask android-image