zoukankan      html  css  js  c++  java
  • [原]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实现多线程并发加载图片的原理:

    一个类,两个方法:

    class AsyncDrawable extends BitmapDrawable{...}
    
    boolean cancelPotentialWork(String url, ImageView imageView){...}
    
    ImageViewResizeTask getBitmapWorkerTask(ImageView imageView){...}
    

    具体代码实现如下:

    /**
     * 扩展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;
    }

    使用方法:

    /**
     * 异步加载图片
     * @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事件,在滑动的时候不加载图片

    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()方法中调用恰当方法非常有用

    @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();
    }

    官网

    Processing Bitmaps Off the UI Thread   

  • 相关阅读:
    linux清理内存
    华为代码注释标准
    Spring缓存机制的理解
    jQuery实现动态分割div—通过拖动分隔栏实现上下、左右动态改变左右、上下两个相邻div的大小
    模仿iframe框架,由分隔栏动态改变左右两侧div大小———基于jQuery
    js实现由分隔栏决定两侧div的大小—js动态分割div
    java基于socket的简单聊天系统
    中国移动归属地区号大全
    将本地光盘做成yum源
    windows下设置计划任务自动执行PHP脚本
  • 原文地址:https://www.cnblogs.com/purediy/p/3462822.html
Copyright © 2011-2022 走看看