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   

  • 相关阅读:
    Fix Installing .NET Framework 3.5 failed Error Code 0x800F0954 on Windows 10
    RHEL8安装五笔输入法
    Enable EPEL and Local Repository on RHEL8
    Why is Yum Replaced by DNF?
    检查Linux服务器是否被攻击的常用命令及方法
    IDEA 主题
    IDEA 如何显示一个类中所有的方法
    Appium 安装以及安装过程中遇到的问题
    Maven 如何发布 jar 包到 Nexus 私库
    java泛型的基本使用
  • 原文地址:https://www.cnblogs.com/purediy/p/3462822.html
Copyright © 2011-2022 走看看