[原]Android官方图片加载利器BitmapFun解析
通过BitmapFun在项目中使用,结合代码了解一下BitmapFun加载图片的原理,以及最佳使用实践。本文说明不包括BitmapFun的缓存部分。
Android开发在使用ListView和GridView时,可能会有很多网络图片需要加载,通常我们会为每个图片加载启动一个Thread或者直接使用官方提供的AsyncTask,来做Http异步加载,但当每个ImageView子视图都触发一个AsyncTask来异步加载图片时,这样就会产生如下问题:
1. 当用户快速滑动时,ImageView已经被回收,而绑定的线程还在运行,浪费CPU,浪费内存。
2. 无法确保当前视图在结束时,分配的视图已经进入循环队列中给另外一个子视图进行重用,意思就是,图片显示错位了,不该显示到当前问题的图片却显示了,这个是经常遇到的问题。可以结合Adapter中的getView方法的convertView参数理解,ListView是回收和重复利用item的。
3. 无法确保所有的异步任务能够按顺序执行。
在这些问题下,官网给出的答案是,使用下面的方法来保证:
1. ImageView和Task绑定准确的加载对应图片;
2. ImageView和Task无法对应时则取消任务;
BitmapFun实现多线程并发加载图片的原理:
一个类,两个方法:
1 2 3 4 5 | class AsyncDrawable extends BitmapDrawable{...} boolean cancelPotentialWork(String url, ImageView imageView){...} ImageViewResizeTask getBitmapWorkerTask(ImageView imageView){...} |
具体代码实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | /** * 扩展BitmapDrawable类,通过弱引用关联任务BitmapWorkerTask * BitmapDrawable被用来作为占位图片,绑定任务到ImageView中 */ private class AsyncDrawable extends BitmapDrawable { private final WeakReference<BitmapWorkerTask> viewResizeTaskReference; public AsyncDrawable(Resources res, Bitmap bitmap, BitmapWorkerTask viewResizeTask) { super (res, bitmap); viewResizeTaskReference = new WeakReference<BitmapWorkerTask>(viewResizeTask); } public BitmapWorkerTask getBitmapWorkerTask() { return viewResizeTaskReference.get(); } } /** * 确保ImageView执行的是它对应的Task,否则取消任务 * @param url * @param imageView * @return */ private boolean cancelPotentialWork(String url, ImageView imageView) { // 获得ImageView对应的Task final BitmapWorkerTask bitmapWorkerTask = getBitmapWorkerTask(imageView); if (bitmapWorkerTask != null ) { final String imgUrl = bitmapWorkerTask.url; if (imgUrl == null || !imgUrl.equals(url)) { // Cancel previous task bitmapWorkerTask.cancel( true ); } else { // The same work is already in progress return false ; } } // No task associated with the ImageView, or an existing task was cancelled return true ; } /** * 获得已经被分配到ImageView的指定的Task * @param imageView * @return */ private 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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | /** * 异步加载图片 * @param url * @param imageView */ private void loadBitmap(String url, ImageView imageView) { if (cancelPotentialWork(url, imageView)) { final BitmapWorkerTask task = new BitmapWorkerTask(imageView); final AsyncDrawable asyncDrawable = new AsyncDrawable(context.getResources(), null , task); imageView.setImageDrawable(asyncDrawable); task.execute(url); } } /* * 异步加载图片Task类 */ 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); } } } } |
最佳使用实践,提高流畅度
1. 设置ListView的OnScrollListener事件,在滑动的时候不加载图片
1 2 3 4 5 6 7 8 9 | public void onScrollStateChanged(AbsListView view, int scrollState) { if (scrollState == AbsListView.OnScrollListener.SCROLL_STATE_FLING) { if (!Utils.hasHoneycomb()) { mImageFetcher.setPauseWork( true ); } } else { mImageFetcher.setPauseWork( false ); } } |
2. 在Activity或Fragment的onResume(),onPause(),onDestroy()方法中调用恰当方法非常有用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | @Override public void onResume() { super .onResume(); mImageFetcher.setExitTasksEarly( false ); } @Override public void onPause() { super .onPause(); mImageFetcher.setPauseWork( false ); mImageFetcher.setExitTasksEarly( true ); mImageFetcher.flushCache(); } @Override public void onDestroy() { super .onDestroy(); mImageFetcher.closeCache(); } |
官网
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步