zoukankan      html  css  js  c++  java
  • Android项目框架之图片加载框架的选择


    本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处!


    从Android爆发以后,自定义的控件如EditTextWithDelete、ActionBar、PullToRresh逐步进入开发者的视野,想起11年的时候,基本项目中使用的UI还全都是Android提供的基础控件,少一点的动画+布局,下载和网络请求都是用HttpClient,图片加载当然也是下载后再使用,当时的程序资源可没有现在这么丰富,看看Github上开源的项目,现在的程序员应该感到幸福。

    项目开发从程序上来讲,万古不变两大事情,一是网络通信框架,二是图片加载框架,有关网络框架上一篇已经介绍了async-http和okhttp,而其他如volly同时拥有网络请求和图片加载两个框架,很多人图省事就一次性使用了,当然facebook自己的开源框架也是写的非常不错,接下来再一一介绍;先贴一张11年我们自己写的imageloader

    public class ImageDownloader {
    
    	private static ImageDownloader instance = null;
    	private static File cacheDir;
    	public static Map<String, SoftReference<Bitmap>> bitMapCache = new HashMap<String, SoftReference<Bitmap>>();
    
    	public static ImageDownloader getInstance() {
    		if (instance == null) {
    			instance = new ImageDownloader();
    		}
    		return instance;
    	}
    
    	private ImageDownloader() {
    		// Find the dir to save cached images
    		if (android.os.Environment.getExternalStorageState().equals(
    				android.os.Environment.MEDIA_MOUNTED))
    			cacheDir = new File(
    					android.os.Environment.getExternalStorageDirectory(),
    					"WholeMag");
    		else
    			cacheDir = WholeMagApplication.getInstance().getCacheDir();
    		if (!cacheDir.exists())
    			cacheDir.mkdirs();
    	}
    
    	public void download(String actName, String url, ImageView imageView) {
    		BitmapDownloaderTask task = new BitmapDownloaderTask(imageView, actName);
    		task.execute(url);
    		// return task.doInBackground(url);
    	}
    
    	class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    		// private String url;
    		// private boolean flag;
    		private final WeakReference<ImageView> imageViewReference; // 使用WeakReference解决内存问题
    		private String actName;
    
    		public BitmapDownloaderTask(ImageView imageView, String actName) {
    			imageViewReference = new WeakReference<ImageView>(imageView);
    			this.actName = actName;
    		}
    
    		@Override
    		protected Bitmap doInBackground(String... params) { // 实际的下载线程,内部其实是concurrent线程,所以不会阻塞
    			Bitmap rebmp = getLocalBitmap(params[0], actName);
    			if (rebmp == null)
    				rebmp = downloadBitmap(params[0], actName);
    			if (rebmp == null) {
    				doInBackground(params[0]);
    			}
    			return rebmp;
    		}
    
    		@Override
    		protected void onPostExecute(Bitmap bitmap) { // 下载完后执行的
    			if (isCancelled()) {
    				bitmap = null;
    			}
    			if (imageViewReference != null) {
    				ImageView imageView = imageViewReference.get();
    				if (imageView != null) {
    					imageView.setDrawingCacheEnabled(true);
    					Bitmap temp = imageView.getDrawingCache();
    					imageView.setDrawingCacheEnabled(false);
    					if (temp != null) {
    						temp.recycle();
    					}
    					double widthX = (float) WholeMagDatas.getDeviceWidth()
    							/ bitmap.getWidth(); // 图片宽度拉伸比例
    					int bitmapHight = bitmap.getHeight();// 图片高度
    					imageView.setImageBitmap(bitmap); // 下载完设置imageview为刚才下载的bitmap对象
    					if(actName.equals(AppData.NEWS_DETAIL_ACT)){
    						FrameLayout.LayoutParams ll = new FrameLayout.LayoutParams(
    								android.view.ViewGroup.LayoutParams.FILL_PARENT,
    								(int) (bitmapHight * widthX), Gravity.CENTER);
    						imageView.setLayoutParams(ll);
    					}
    					AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);// 创建一个AlphaAnimation对象
    					alphaAnimation.setDuration(500);// 设置动画执行的时间(单位:毫秒)
    					imageView.startAnimation(alphaAnimation);
    				}
    			}
    		}
    	}
    
    	static Bitmap getLocalBitmap(String url, String actName) {
    		if (bitMapCache.containsKey(url)) {
    			return bitMapCache.get(url).get();
    		}
    		// String tmp = url;
    		// String first = url.substring(url.lastIndexOf("/") + 1);
    		// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
    		// String second = tmp.substring(tmp.lastIndexOf("/") + 1);
    		// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
    		// String third = tmp.substring(tmp.lastIndexOf("/") + 1);
    		// String filename = third + second + first;
    		// File f = new File(cacheDir, filename);
    		File f = Tools.getFile(actName, url);
    		InputStream inputStream = null;
    		try {
    			// decode image size
    			BitmapFactory.Options o = new BitmapFactory.Options();
    			o.inPreferredConfig = Bitmap.Config.RGB_565;
    			o.inDither = false;
    			o.inPurgeable = true;
    			// o.inTempStorage = new byte[12 * 1024];
    			inputStream = new FileInputStream(f);
    			// Bitmap bitmap = BitmapFactory.decodeFile(f.getAbsolutePath());
    			Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, o);
    			bitMapCache.put(url, new SoftReference<Bitmap>(bitmap));
    			return bitmap;
    		} catch (Exception e) {
    		} finally {
    			if (null != inputStream) {
    				try {
    					inputStream.close();
    				} catch (Exception ex) {
    				}
    			}
    		}
    		return null;
    	}
    
    	static Bitmap downloadBitmap(String url, String actName) {
    		final AndroidHttpClient client = AndroidHttpClient.newInstance("linux");
    		final HttpGet getRequest = new HttpGet(url);
    
    		try {
    			HttpResponse response = client.execute(getRequest);
    			final int statusCode = response.getStatusLine().getStatusCode();
    			if (statusCode != HttpStatus.SC_OK) {
    				// Log.e("cwjDebug", "Error " + statusCode
    				// + " while retrieving bitmap from " + url);
    				return null;
    			}
    
    			final HttpEntity entity = response.getEntity();
    			if (entity != null) {
    				// String tmp = url;
    				// String first = url.substring(url.lastIndexOf("/") + 1);
    				// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
    				// String second = tmp.substring(tmp.lastIndexOf("/") + 1);
    				// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
    				// String third = tmp.substring(tmp.lastIndexOf("/") + 1);
    				// String filename = third + second + first;
    				// File f = new File(cacheDir, filename);
    				File f = Tools.getFile(actName, url);
    				OutputStream os = new FileOutputStream(f);
    				InputStream inputStream = null;
    				try {
    					inputStream = entity.getContent();
    					BitmapFactory.Options o = new BitmapFactory.Options();
    					o.inPreferredConfig = Bitmap.Config.RGB_565;
    					o.inDither = false;
    					o.inPurgeable = true;
    					final Bitmap bitmap = BitmapFactory.decodeStream(
    							inputStream, null, o);
    					bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
    					bitMapCache.put(url, new SoftReference<Bitmap>(bitmap));
    					return bitmap;
    				} finally {
    					if (inputStream != null) {
    						inputStream.close();
    					}
    					if (null != os) {
    						os.close();
    					}
    					entity.consumeContent();
    				}
    			}
    		} catch (Exception e) {
    			getRequest.abort();
    		} finally {
    			if (client != null) {
    				client.close();
    			}
    		}
    		return null;
    	}
    }
    
    当年为了防止图片爆掉出现OOM,使用了软引用,觉得还不错;图片加载的基本原理就是,把url+imageview抛过来,然后开启异步线程加载,根据获取的byte流decode成bitmap,最后在UI线程将图片加载到imageview上;也没有说做本地缓存,仅做了应用缓存;没有对图片进行压缩或者设置格式,使占用内存更小,展示也更合理;LruCache基本原理跟上面使用软引用的过程差不多,只不过多限制了图片占用内存的大小,计算图片使用的频率,对应用层、SD卡层均做了封装。

    上面介绍完,相信你也对图片加载有个大概的轮廓,我们拿开源的imageloader为例,来讲讲图片加载框架的一些细节

    public final class ImageLoaderConfiguration {
    
        final Resources resources;//主要给图片设计宽高时,获得屏幕宽高使用
        final int maxImageWidthForMemoryCache;//内存中最大的图片宽度
        final int maxImageHeightForMemoryCache;//内存中最大的图片高度
    
        final int maxImageWidthForDiskCache;//SD卡中最大的图片宽度
        final int maxImageHeightForDiskCache;//SD卡中最大的图片高度
        final BitmapProcessor processorForDiskCache;//从SD卡获得Bitmap的加载器
    
        final Executor taskExecutor;//加载图片时的执行器
        final Executor taskExecutorForCachedImages;//加载缓存时的执行器
        final boolean customExecutor;//是否使用默认执行器
        final boolean customExecutorForCachedImages;//是否使用默认缓存执行器
    
        final int threadPoolSize;//线程数,可以用来控制展示当前界面的item图片
        final int threadPriority;//线程的执行优先级
        final QueueProcessingType tasksProcessingType;//是LILO还是LIFO,默认是前者,但一般喜欢后者
    
        final MemoryCache memoryCache;//内存缓存对象,如不写可用默认
        final DiskCache diskCache;//SD卡缓存对象,如不写可用默认
        final ImageDownloader downloader;//图片加载器,根据网络(http/s)、file、content、drawable、asset来加载
        final ImageDecoder decoder;//图片解析器,根据获取的图片参数拿到Bitmap
        final DisplayImageOptions defaultDisplayImageOptions;//设置图片加载状态和结果,见下面源码
    
        final ImageDownloader networkDeniedDownloader;//不用网络下载图片的下载器,可理解为加载SD卡图片的加载器
        final ImageDownloader slowNetworkDownloader;//仅网络下载图片的下载器,支持断点续传

    拿这些变量来讲,基本就可以说明事情 

    public final class DisplayImageOptions {
    
        private final int imageResOnLoading;//图片是否加载中
        private final int imageResForEmptyUri;//图片是否来自于空url
        private final int imageResOnFail;//图片是否加载失败
        private final Drawable imageOnLoading;//加载中的图片
        private final Drawable imageForEmptyUri;//空数据的图片
        private final Drawable imageOnFail;//加载失败的图片
        private final boolean resetViewBeforeLoading;//加载完是否重置(意味着放弃之前的加载)
        private final boolean cacheInMemory;//是否缓存在内存中
        private final boolean cacheOnDisk;//是否缓存在SD卡中
        private final ImageScaleType imageScaleType;//要多大的图片,统一设置
        private final Options decodingOptions;//Bitmap的options对象
        private final int delayBeforeLoading;//是否延迟加载,可用于非当前页面图片
        private final boolean considerExifParams;//是否支持jpeg图片的rotate和flip等方法
        private final Object extraForDownloader;//额外数据
        private final BitmapProcessor preProcessor;//加载不在内存中的图片
        private final BitmapProcessor postProcessor;//加载在图片中的图片
        private final BitmapDisplayer displayer;//展示图片
        private final Handler handler;//这个就不用讲了吧,跟主线程交互必不可少的工具
        private final boolean isSyncLoading;//是否同步加载 

    补充一下,上面框架还支持给图片设置像素点占位大小;看到这么多功能,对于现在的项目基本满足要求,因此就不打算换了,再看看其他几种图片加载框架的异同

    fresco,facebook出品,最大的优势在于可展示加载过程,即加载进度、加载前图片、加载中图片、加载后图片、加载失败图片等,还可以设置图片的形状;并且提供加载Gif、Webp图片的方法。

    picasso,加载更快,因为默认设置图片格式占内存小

    Glide是升级版本的piccaso,支持跟fragment和activity生命周期绑定

    volly,基于老的imageloader又做了次封装,差别不是太大,功能弱化一些

    而后面这几种框架,都是在imageloader兴起之后出现的,所以也基本支持它,在我看来也仅有fresco和glide是真正写出了跟原框架不同的东西。


  • 相关阅读:
    四则运算3.2
    第二周进度条
    构建之法阅读笔记02
    四则运算2
    第一周进度条
    构建之法阅读笔记01
    四则运算 Python
    第一周第二周学习进度条
    《构建之法》学习中疑问
    小学四则运算1.0
  • 原文地址:https://www.cnblogs.com/fengju/p/6174393.html
Copyright © 2011-2022 走看看