今天闲着有空,看了下SDK文档,发现里面有一篇《Processing Bitmaps Off the UI Thread》中特地介绍了异步加载图片的思路。特地记录一下。
大家都知道,加载图片是很费时的,尤其是从磁盘或网络上获取的时候。
因此,我们在设计程序时,往往将加载操作放到一个异步任务(线程)里去执行。
大致程序是这样的:
首先,我们会定义一个异步任务,传入一个View对象。当加载完毕后,设置其内容。
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { private final WeakReference<ImageView> imageViewReference; private int data = 0; public BitmapWorkerTask(ImageView imageView) { // 用WeakReference来保证ImageView能够及时地被回收 imageViewReference = new WeakReference<ImageView>(imageView); } // 在后台解析图片 @Override protected Bitmap doInBackground(Integer... params) { data = params[0]; return decodeSampledBitmapFromResource(getResources(), data, 100, 100); } // 一旦解析完毕,确保ImageView仍然有效,并设置图片 @Override protected void onPostExecute(Bitmap bitmap) { if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); if (imageView != null) { imageView.setImageBitmap(bitmap); } } } }
通常,这个异步任务会在加载图片的地方被创建并执行。
public void loadBitmap(int resId, ImageView imageView) { BitmapWorkerTask task = new BitmapWorkerTask(imageView); task.execute(resId); }
但如果有这样一种场景,我们要在ListView或GridView里显示数据,但用户不可能每次都等你加载完了才滑屏。通常是看到几行加载好了,就快速地往后翻。
那么问题就来了:“按照之前的逻辑,每次getView的时候都启动一个异步任务。当加载完成后,设置该View的内容。但是像ListView这种,列表项的View对象是可以复用的。也就是说,当你滑到某一行的时候,为了减少资源的占用,我们应该取消之前这个View上的加载任务,然后启动一个新的任务来加载新的数据。要不然,如果老的任务后完成,图片就出错了。”
于是,文中提出了一种改进的解决方案。
首先,定义一种替代的数据对象类型,该类型维护了异步任务的引用。如下面的AsyncDrawable。
static class AsyncDrawable extends BitmapDrawable { private final WeakReference<BitmapWorkerTask> bitmapWorkerTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask bitmapWorkerTask) { super(res, bitmap); bitmapWorkerTaskReference = new WeakReference<BitmapWorkerTask>(bitmapWorkerTask); } public BitmapWorkerTask getBitmapWorkerTask() { return bitmapWorkerTaskReference.get(); } }
然后,在加载资源时,先取消该View上绑定的异步任务。再创建新的任务与数据对象,将数据对象绑定到View上。如下图所示。
public void loadBitmap(int resId, ImageView imageView) { if (cancelPotentialWork(resId, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(getResources(), mPlaceHolderBitmap, task); imageView.setImageDrawable(asyncDrawable); task.execute(resId); } }
取消任务的方法如下,主要思路就是:“从View上获取绑定的数据对象中的任务引用。如果是同样的数据加载请求,则没必要取消。否则,取消之。”
public static boolean cancelPotentialWork(int data, ImageView imageView) { final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null) { final int bitmapData = bitmapWorkerTask.data; if (bitmapData != data) { // 取消任务 bitmapWorkerTask.cancel(true); } else { // 加载同样的数据,没必要再启动一次 return false; } } // 没有任务绑定或任务已取消 return true; }
获取任务的引用的方法如下,其实就是从View上把数据对象取出来。如果是我们之前定义的类型,则强转并调用相应的方法获取异步任务对象。
private static BitmapWorkerTask getBitmapWorkerTask(ImageView imageView) { if (imageView != null) { final Drawable drawable = imageView.getDrawable(); if (drawable instanceof AsyncDrawable) { final AsyncDrawable asyncDrawable = (AsyncDrawable) drawable; return asyncDrawable.getBitmapWorkerTask(); } } return null; }
异步任务的逻辑修改如下:1、加了一个是否已取消的判断。2、在设置内容前,先判断View上绑定的任务对象是不是自己。只有在是自身的情况下,才往上设置内容。
class BitmapWorkerTask extends AsyncTask<Integer, Void, Bitmap> { ... @Override protected void onPostExecute(Bitmap bitmap) { if (isCancelled()) { bitmap = null; } if (imageViewReference != null && bitmap != null) { final ImageView imageView = imageViewReference.get(); final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (this == bitmapWorkerTask && imageView != null) { imageView.setImageBitmap(bitmap); } } } }
至此,ListView或GridView异步加载资源的方法描述完毕。
我们在实际使用时,可以将imageView.setImageDrawable方法换成view.setTag,将AsyncDrawable换成自定义的AsyncData,将bitmapWorkerTask.data换成我们在BitmapWorkerTask中自定义的资源标识即可。
不要拘泥于上面的代码。
相关推荐
2. `AssetBundle`:AssetBundle是Unity3D中另一种资源异步加载的方式。它可以将游戏的模型、纹理、音频等资源打包成独立的文件,然后在需要时按需加载。这种方式更加灵活,可以实现资源热更新和动态下载。创建...
当项目js(css)文件使用越来越多,js 文件的加载也成了性能上的一个问题,此资源能够在页面全部加载完成后异步加载js等资源文件,它可以顺序加载资源列表,也可以并发加载资源列表,它包含一个方法调用接口:...
【标题】"ztree异步加载demo(有说明文档)" 涉及到的知识点主要围绕着ZTree,这是一个非常流行的JavaScript组件,主要用于构建树形结构的界面,尤其适用于网站导航、文件目录管理和组织结构展示等场景。异步加载是...
2. 实现异步数据源:如果你的数据源是可枚举的(例如IEnumerable或IAsyncEnumerable),你可以创建一个异步版本的获取数据的方法,以便在后台线程中逐个加载元素。 3. 数据转换器:在数据绑定过程中,你可能需要...
在这里,我们创建一个ViewModel类,它包含异步加载DataGrid所需的方法,并暴露给View。例如,可以有一个`LoadDataAsync`方法,这个方法使用async/await异步获取数据,并填充到ObservableCollection,这是一个支持...
在Android应用开发中,图片异步加载是一种常见的优化技术,特别是在构建类似照片墙或ListView这样的大量图片展示场景中。这个话题主要关注如何有效地处理图片资源,避免UI阻塞,提高用户体验。以下是对"图片异步加载...
3. **异步加载资源**:使用`LoadAsync`方法异步加载资源,并通过回调函数处理加载完成后的逻辑。 ```csharp AssetBundleRequest request = assetBundle.LoadAssetAsync("assetname"); request.completed += ...
2. **AsyncTask加载**:AsyncTask是Android提供的一种轻量级异步任务框架,适用于执行短时间的后台操作。它包含三个泛型参数,分别表示后台操作类型、进度更新类型和结果返回类型。AsyncTask提供了方便的方法如...
在JavaScript(JS)中,异步加载图片是一种优化网页性能的技术。它允许浏览器在不影响页面主要功能的情况下,按需或后台加载非关键资源,如图片。这样可以减少页面初次加载的时间,提高用户体验,特别是对于含有大量...
在本篇文章中,介绍了如何利用layui框架实现异步加载table表中某一列数据的示例。layui是一个轻量级的前端UI框架,提供了一整套的解决方案,方便前端开发者快速构建界面。本文将重点阐述如何在layui框架下,利用异步...
- 利用`<link rel="preload">`预加载资源:这是一种预加载策略,可以预先获取资源,但不保证执行。需要配合`<script>`标签来实际执行脚本。 4. **异步加载的优缺点**: - 优点:加快页面初始加载速度,改善用户...
在WPF应用开发中,异步加载图片是一个常见的需求,特别是在处理大尺寸或者网络资源时,为了提升用户体验,我们希望图片加载不影响界面的响应速度。本文将深入探讨如何实现这一功能,并在加载失败时切换到本地备份...
Android 实现 ListView 异步加载图片是一种常见的技术,旨在提高应用程序的性能和用户体验。本文将详细介绍 Android 中实现 ListView 异步加载图片的方法,并对相关的技术概念进行解释。 1. 异步加载图片的必要性 ...
- 在ViewModel中创建一个异步加载图片的方法,该方法返回一个包含图片的`ImageSource`对象。 - ViewModel中声明一个依赖属性,用于存储图片源。当图片加载完成时,通过数据绑定更新UI中的Image控件的Source属性。 ...
在Cocos2d-x游戏开发中,资源管理是至关重要的,特别是对于大型或者资源密集型的游戏,异步加载资源能够显著提升用户体验,避免因等待资源加载而造成的卡顿。本篇将深入探讨如何利用Cocos2d-x和Boost库实现多线程...
在"Music"这个子文件夹可能包含了音乐相关的图片加载需求,常见的图片加载库如Glide、Picasso和Fresco,它们都支持异步加载和缓存机制,能有效地处理图片资源,防止内存泄漏。 综上所述,"listview异步加载源码...
一种常见的异步图片加载库是Universal Image Loader (UIL),但现在更推荐使用Glide或Picasso。这些库都提供了简洁的API,能轻松实现图片的异步加载、缓存管理和内存优化。 以Glide为例,我们可以这样使用它来加载...
基于Unity封装的AB包资源管理器,实现同步加载/异步加载AB包以及依赖包,详细可以到我的博客查看文章,里面也有完整代码。
在Android开发中,异步加载图片是一个至关重要的技术,尤其对于图像密集型的应用,如相册、社交媒体等。异步加载能确保用户界面的流畅性,避免因为加载大量图片而阻塞主线程,提高用户体验。这里我们将深入探讨四种...
4. ListView适配器:在ListView的Adapter中,为每个列表项创建一个ImageView,并在getView方法中启动异步加载任务。根据当前项的位置,判断图片是否已经加载,如果已加载,直接显示;否则,启动加载任务。 5. 优化...