图片加载功能实现

一、考虑方向

关于图片加载功能,目前针对如下几个场景考虑性能:

  1. 避免主线程因加载图片导致block,并需要考虑涉及到快速加载多张图片的情形;
    ==>所以此处加载图片需要开工作线程处理,最好可以使用线程池;
  2. 用户使用时有反复加载某张图片的场景;
    ==>所以添加buffer存放最近有加载过的图片,该buffer使用LRU算法更新;
  3. 大图片的加载会耗时很久,而在此项目800*480分辨率屏幕上显示效果并不能体现;
    ==>所以加载图片时会获取对应分辨率大小的图片;
  4. 使用效果优化,如果加载时间较长,则用户体验不好;
    ==>需要添加预加载功能,此部分因为显示是使用的viewpager,已经支持预加载;
  5. 快速切换图片场景,大量图片快速切换,则切换过程中的图片是否需要完成加载并显示部分需要考虑,
    ==>需要添加取消图片加载的功能,则需要建立一个队列获取图片,如果当前已经切换到其他图片时可以cancel加载过程;

二、功能逻辑

则针对图片加载功能,分为如下三个方向的处理:

  1. 针对于图片加载策略:
    ==>工作队列记录,需要加载的图片添加到工作队列中,加载完成则从工作队列中移除;
    ==>添加到工作队列的任务添加到线程池中处理(目前设置为5个线程,任务添加超过5个会block)
    ==>加载图片时:

    • 判断当前mem中是否已经存储了此图片,有则直接取出返回,没有则进入第二步;
    • 直接从数据库中decode图片并返回,如果取数据失败,则加载默认图片image demage;
    • 加载完成,将其添加到mem中,给后续使用;
      ==>在加载图片时添加判断,如果该图片已经不再当前显示范围内则直接返回,不做后续处理,以提高性能;(待后续测试确认是否需要添加此功能)
  2. 针对于图片加载过程的处理:
    ==>此部分主要考虑图片过大情况下加载时间过长的情况,则在加载图片过程中计算src大小,并根据显示分辨率计算缩放比,获取缩放过的图片,可以提高性能;

  3. 针对于图片显示策略:
    ==>使用viewpager显示控件,该控件提供预加载功能(涉及到图片切换后更新显示的问题,当前已去除此功能)

三、具体实现:

  • 工作队列
  • 线程池
  • 缓存机制
  • 图片加载过程
  • 图片显示
    目前上述各个功能主要都是使用android现有工具类:

3.1 工作队列:

3.1.1 逻辑:
  1. 在ImageLoader构造工作类,并建立该工作类list;
  2. 实现向该队列中添加任务函数,此处增加一个判断,如果该任务已经添加过,则不需要重复添加;
  3. 实现从该队列中移除任务函数;
3.1.2 code:
private List<LoadPhotoTask> mTaskQueue = null;
class LoadPhotoTask implements Runnable {
	private String path;
	private ImageView view;
	private boolean isThumbnail;
	private onImageLoadedListener callback;
	private Bitmap bitmap;

	LoadPhotoTask(String path,
		ImageView imageView,
		boolean isThumbnail,
		onImageLoadedListener callback) {
			Debug.d(TAG, "loadImage new LoadPhotoTask");
			this.path = path;
			this.view = imageView;
			this.isThumbnail = isThumbnail;
			this.callback = callback;
			this.bitmap = null;
		}
		
		@Override
		public void run() {
		//加载图片逻辑
		}
		public ImageView getView() {
			return view;
		}
	}
	public void loadImage(ImageView view,
		String path,
		boolean isThumbnail,
		int reqWidth,
		int reqHeight,
		onImageLoadedListener callback) {
		if(isTaskExisted(view)) {
			return;
		}
		LoadPhotoTask task = new LoadPhotoTask(path, view, isThumbnail, callback);
		synchronized (mTaskQueue) {
			mTaskQueue.add(task);
		}
		mExecutor.execute(task);
	}
	private boolean isTaskExisted(ImageView view) {
		if(view == null)
			return false;
		synchronized (mTaskQueue) {
			int size = mTaskQueue.size();
			for(int i=0; i<size; i++) {
				LoadPhotoTask task = mTaskQueue.get(i);
				if(task != null && task.getView().equals(view))
					return true;
			}
		}
		return false;
	}
	
	
	private void removeTask(LoadPhotoTask task) {
		synchronized (mTaskQueue) {
			mTaskQueue.remove(task);
		}
	}

3.2 线程池

这里直接使用java提供的线程池ExecutorService:

3.2.1 逻辑:
  1. 在ImagLoader类建立线程池;
  2. 上述工作队列中执行加载图片过程时,使用该线程池处理;
3.2.2 code:
private ExecutorService mExecutor;
mExecutor = Executors.newFixedThreadPool(Constant.LOAD_IMAGE_THREAD);
mExecutor.execute(task);

3.3 buffer

此部分使用Android提供的LRU buffer:

3.3.1 逻辑:
  1. 在ImagLoader类中添加buffer,设置buffer大小(buffer更新机制为LRU);
  2. 实现buffer添加和读取接口,获取到图片后,将其添加到buffer中;
  3. 在view adapter获取图片时,首先在buffer中查询,有则返回,没有则到数据库中读取;
3.3.2 code:
	private LruCache<String, Bitmap> mCache = null;
	ActivityManager am =(ActivityManager)mContext.getSystemService(Context.ACTIVITY_SERVICE);
	int maxSize = am.getMemoryClass()*1024*1024/8;
	mCache = new LruCache<String, Bitmap>(maxSize){
		@Override
		protected int sizeOf(String key, Bitmap value) {
			return value.getRowBytes() * value.getHeight();
		}
	};
	public void putBitmapInCache(String path, Bitmap bitmap){
		if(null == path) return;
		if(null == bitmap) return;
		if(null == getBitmapFromCache(path)){
			this.mCache.put(path, bitmap);
		}
	}
	public Bitmap getBitmapFromCache(String path){
		return mCache.get(path);
	}
	String key = String.valueOf(isThumbnail) + path;
	bitmap = getBitmapFromCache(key);
	if (null == bitmap) {
		String tag = view.getTag().toString();
		if (tag.equals(path)) {
			if (isThumbnail) {
				bitmap = loadThumbnailInBackground(path, 92, 49);
			} else {
				bitmap = loadImageInBackground(path, 800, 480);
			}
			String cacheKey = String.valueOf(isThumbnail) + path;
			putBitmapInCache(cacheKey, bitmap);
		}
	}
	show(view, callback, bitmap);
	removeTask(this);

3.4. 加载图片时根据屏幕分辨率进行缩放处理

3.4.1 逻辑:
  1. 获取图片源信息;
  2. 计算缩放比;
  3. load 图片;
3.4.2 code:
	BitmapFactory.Options options = new BitmapFactory.Options();
	options.inJustDecodeBounds=true;//设置为true则不会load图片,而是获取图片信息填充到option中
	BitmapFactory.decodeFile(path, options);
	public int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
		final int height = options.outHeight;
		final int width = options.outWidth;
		int inSampleSize = 1;
		int beWidth = Math.round((float) width / (float) reqWidth);
		int beHeight = Math.round((float) height / (float) reqHeight);
		if (height > reqHeight || width > reqWidth) {
			if (beWidth < beHeight) {
				inSampleSize = beWidth;
			} else {
				inSampleSize = beHeight;
			}
			if (inSampleSize <= 0) {
				inSampleSize = 1;
			}
		}
	return inSampleSize;
	}
	options.inJustDecodeBounds = false;
	Bitmap bitmap= BitmapFactory.decodeFile(path, options);

3.5. 图片显示

3.5.1逻辑:
  1. 通过message handler的方式在主线程显示;
  2. 设置显示图片类,传递view、bitmap、callback;
  3. 上述callback为adapter实现,具体操作获取图片后的处理;
3.5.2 code:
private void show(ImageView imageView, onImageLoadedListener callback, Bitmap bitmap) {
        BitmapDisplayer bitmapDisplayer = new BitmapDisplayer(imageView, callback, bitmap);
        Message message = Message.obtain();
        message.obj = bitmapDisplayer;
        message.what = Constant.MSG_PHOTO_LOAD_CMPLETE;
        mUIHandler.sendMessage(message);
    }
    class BitmapDisplayer {
        private Bitmap bitmap;
        private ImageView imageView;
        private onImageLoadedListener callback;
        public BitmapDisplayer(ImageView imageView, onImageLoadedListener callback, Bitmap b) {
            this.bitmap = b;
            this.imageView = imageView;
            this.callback = callback;
        }
    }
    Handler mUIHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case Constant.MSG_PHOTO_LOAD_CMPLETE:
                    BitmapDisplayer b = (BitmapDisplayer)msg.obj;
                    Bitmap bitmap = b.bitmap ;
                    onImageLoadedListener callback = b.callback;
                    ImageView view = b.imageView;
                    callback.displayImage(view, bitmap);
                    break;
                default:
                    break;
            }
        }
    };
//call back
    public interface onImageLoadedListener {
        void displayImage(ImageView view, Bitmap bitmap);
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值