一种异步加载资源的方法(源于SDK文档)
来源:互联网 发布:java高性能并发框架 编辑:程序博客网 时间:2024/06/06 03:30
From: http://johnsonxu.iteye.com/blog/1929520
今天闲着有空,看了下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中自定义的资源标识即可。
不要拘泥于上面的代码。
- 一种异步加载资源的方法(源于SDK文档)
- 图片异步加载的一种处理方法
- 图片缓存(源于SDK文档)
- 图片缓存(源于SDK文档)
- Android开发之一种简单的异步加载图片方法
- 一种保护应用程序的方法 模拟Windows PE加载器,从内存资源中加载DLL
- 一种保护应用程序的方法 模拟Windows PE加载器,从内存资源中加载DLL
- 一种保护应用程序的方法 模拟Windows PE加载器,从内存资源中加载DLL
- 一种保护应用程序的方法 模拟Windows PE加载器,从内存资源中加载DLL
- Unity资源加载的选择(同步/异步)
- Unity资源加载的选择(同步/异步)
- 一种加载驱动的方法
- SDK编程中菜单资源的加载?
- Android sdk 文档资源
- 关于Unity资源异步加载的研究
- Android 图片资源的异步加载2
- 异步加载资源的loading界面
- 异步加载数据的方法
- OpenStack之调试——Pdb调试
- 【java】同步,一定要加锁吗?
- Xcode快捷键—图文详解
- Python写自动化之http文件下载
- mfc树形控件选中的节点,在创建对话框时,选中没有生效 CHECKBOX
- 一种异步加载资源的方法(源于SDK文档)
- Android学习笔记<二>
- Oracle Coherence中文教程三:配置
- NPOI导出Excel2007
- iOS应用内支付(IAP)的那些坑
- 类 事件响应
- 开放接口使用积累
- ActionBar -- 添加Action按钮
- [leetcode] Remove Element