图片加载核心就那些东西,这里设计一个图片加载框架,涉及到本地加载和网络加载,内存缓存和硬盘缓存,等等

思路

在getView的时候开始框架的调用

  1. 配置一系列环境,包括加载策略,缓存策略,线程数量
  2. 调用图片显示,从而添加请求到执行队列
  3. 请求及转发请求,调用加载器,根据需要从本地或者网络得到图片
  4. 得到的图片再选择缓存策略,硬盘缓存或者内存缓存
  5. 最后将图片显示出来

实现的功能和用到的知识

根据用户需求可以灵活配置
支持高并发,图片加载的优先级
支持可以选择不同的加载策略,对加载策略进行扩展
二级缓存:加载图片时内存中已经加载了,则从内存中加载,不存在去外置卡中加载,外置还不存在则从网络下载
并对缓存策略可以扩展
支持从加载过程中显示默认加载图片
支持加载失败时,显示默认错误图片
图片显示自适应,从网络加载下来的图片经最佳比例压缩后显示
不能失真变形
支持请求转发,下载

用到的模式:
生产者 消费者模式
建造者模式
单例模式
模板方法模式
策略模式

用到的知识点
内存缓存 LruCache技术
硬盘缓存技术DiskLruCache技术
图片下载时请求转发

实现代码

首先是配置类,DisplayConfig和ImageLoaderConfig,这两个类主要用于显示及下载的初始化配置

//显示图片配置
public class DisplayConfig {//默认显示的图片IDpublic int loadingImage = -1;public int failedImage = -1;
}
//图片下载配置
public class ImageLoaderConfig {//缓存策略private BitmapCache bitmapCache = new MemoryCache();//加载策略private LoadPolicy loadPolicy = new ReversePolicy();//默认线程数private int threadCount = Runtime.getRuntime().availableProcessors();//加载过程显示图片private DisplayConfig displayConfig= new DisplayConfig();private ImageLoaderConfig() {}//建造者模式,使用链式建造public static class Builder {private ImageLoaderConfig config;public Builder() {config = new ImageLoaderConfig();}//设置缓存策略public Builder setCachePolicy(BitmapCache bitmapCache) {config.bitmapCache = bitmapCache;return this;}//设置加载策略public Builder setLoadPolicy(LoadPolicy loadPolicy) {config.loadPolicy = loadPolicy;return this;}//设置线程数量public Builder setThreadCount(int count) {config.threadCount = count;return this;}//设置加载过程中的图片public Builder setLoadingImage(int resID) {config.displayConfig.loadingImage = resID;return this;}//设置加载失败的图片public Builder setFaildImage(int resID){config.displayConfig.failedImage = resID;return this;}//返回配置public ImageLoaderConfig build() {return config;}}public BitmapCache getBitmapCache() {return bitmapCache;}public LoadPolicy getLoadPolicy() {return loadPolicy;}public int getThreadCount() {return threadCount;}public DisplayConfig getDisplayConfig() {return displayConfig;}
}

然后是图片的请求类,包括BitmapRequest,RequestDispatcher和RequestQueue,用于完成Bitmap请求的封装,请求的转发及请求队列的管理

//bitmap请求
public class BitmapRequest implements Comparable<BitmapRequest> {//加载策略private LoadPolicy loadPolicy = SimpeImageLoader.getInstance().getConfig().getLoadPolicy();//编号private int serialNo;//持有ImageView的软引用private SoftReference<ImageView> imageViewSoft;//图片路径private String imageUrl;//MD5图片路径private String imageUrlMD5;//下载完成监听public SimpeImageLoader.ImageListener imageListener;//设置显示配置private DisplayConfig displayConfig;public BitmapRequest(ImageView imageView, String imageUrl, DisplayConfig displayConfig,SimpeImageLoader.ImageListener imageListener) {this.imageViewSoft = new SoftReference<>(imageView);//设置可见Image的Tag,防止图片错位imageView.setTag(imageUrl);this.imageUrl = imageUrl;this.imageUrlMD5 = MD5Utils.toMD5(imageUrl);if (displayConfig != null) {this.displayConfig = displayConfig;}this.imageListener = imageListener;}@Overridepublic boolean equals(Object o) {if (this == o) return true;if (o == null || getClass() != o.getClass()) return false;BitmapRequest that = (BitmapRequest) o;return serialNo == that.serialNo &&Objects.equals(loadPolicy, that.loadPolicy);}@Overridepublic int hashCode() {return Objects.hash(loadPolicy, serialNo);}public int getSerialNo() {return serialNo;}public void setSerialNo(int serialNo) {this.serialNo = serialNo;}public ImageView getImageView() {return imageViewSoft.get();}public String getImageUrl() {return imageUrl;}public String getImageUrlMD5() {return imageUrlMD5;}public DisplayConfig getDisplayConfig() {return displayConfig;}//间接比较,确定优先级@Overridepublic int compareTo(@NonNull BitmapRequest o) {return loadPolicy.compareto(o, this);}
}
//转发器,请求转发线程,从请求队列中获取请求
public class RequestDispatcher extends Thread{private static final String TAG = "RequestDispatcher";//请求队列private BlockingQueue<BitmapRequest> requests;public RequestDispatcher(BlockingQueue<BitmapRequest> requests) {this.requests = requests;}@Overridepublic void run() {while(!isInterrupted()){try {BitmapRequest request = requests.take();//处理请求对象//解析请求头String schema = pareSchema(request.getImageUrl());//获取加载器Loader loader = LoaderManager.getInstance().getLoader(schema);//加载图片loader.loadImage(request);} catch (InterruptedException e) {e.printStackTrace();}}}//判断图片路劲来源private String pareSchema(String imageUrl) {if(imageUrl.contains("://")){return imageUrl.split("://")[0];}else {Log.d(TAG, "不支持此文件类型");}return null;}
}
//请求队列
public class RequestQueue {private static final String TAG = "RequestQueue";//阻塞式队列,多线程共享private BlockingQueue<BitmapRequest> requests = new PriorityBlockingQueue<>();//转发器数量private int threadCount;//一组转发器private RequestDispatcher[] dispatchers;//请求编号private AtomicInteger count = new AtomicInteger(0);public RequestQueue(int threadCount) {this.threadCount = threadCount;}//添加请求对象public void addRequest(BitmapRequest request) {if (!requests.contains(request)) {//给请求进行编号request.setSerialNo(count.incrementAndGet());requests.add(request);} else {Log.d(TAG, "请求已存在:" + request.getSerialNo());}}//开始请求public void start() {stop(); //开始前要先停止starDispatchers();}private void starDispatchers() {dispatchers = new RequestDispatcher[threadCount];for (int i = 0; i < threadCount; i++) {RequestDispatcher dispatcher = new RequestDispatcher(requests);dispatchers[i] = dispatcher;dispatchers[i].start();}}//停止请求public void stop() {}
}

缓存策略类包括缓存接口BitmapCache,硬盘缓存DiskCache,内存缓存MemoryCache和双缓存DoubleCache

//缓存策略接口
public interface BitmapCache {//缓存Bitmapvoid put(BitmapRequest request, Bitmap bitmap);//获取BitmapBitmap get(BitmapRequest request);//移除缓存void remove(BitmapRequest request);
}
//硬盘缓存策略
public class DiskCache implements BitmapCache {//缓存路径private String cacheDir = "Image";//MBprivate static final int MB = 1024 * 1024;private DiskLruCache diskLruCache;//单利private static DiskCache instance;private DiskCache(Context context) {initDiskCache(context);}private void initDiskCache(Context context) {//缓存目录File dir = getDiskCache(cacheDir, context);if (!dir.exists()) {dir.mkdir();}try {//设置缓存容量diskLruCache = DiskLruCache.open(dir, 1, 1, 50 * MB);} catch (IOException e) {e.printStackTrace();}}private File getDiskCache(String cacheDir, Context context) {//默认存储路径return new File(Environment.getExternalStorageDirectory(), cacheDir);}public static DiskCache getInstance(Context context) {if (instance == null) {synchronized (DiskCache.class) {if (instance == null) {instance = new DiskCache(context);}}}return instance;}@Overridepublic void put(BitmapRequest request, Bitmap bitmap) {DiskLruCache.Editor editor = null;OutputStream outputStream = null;try {editor = diskLruCache.edit(request.getImageUrlMD5());//一个key对应一个文件outputStream = editor.newOutputStream(0);if (persistBitmap2Disk(bitmap, outputStream)) {editor.commit();} else {editor.abort();}} catch (IOException e) {e.printStackTrace();}}private boolean persistBitmap2Disk(Bitmap bitmap, OutputStream outputStream) {BufferedOutputStream bos = new BufferedOutputStream(outputStream);bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);try {bos.flush();} catch (IOException e) {e.printStackTrace();} finally {IOUtil.closeQuietly(bos);}return true;}@Overridepublic Bitmap get(BitmapRequest request) {try {DiskLruCache.Snapshot snapshot = diskLruCache.get(request.getImageUrlMD5());if(snapshot != null){InputStream inputStream = snapshot.getInputStream(0);return BitmapFactory.decodeStream(inputStream);}} catch (IOException e) {e.printStackTrace();}return null;}@Overridepublic void remove(BitmapRequest request) {try {diskLruCache.remove(request.getImageUrlMD5());} catch (IOException e) {e.printStackTrace();}}
}
//内存缓存策略
public class MemoryCache implements BitmapCache {private LruCache<String, Bitmap> lruCache;public MemoryCache() {//设置最大缓存值int maxSize = (int) (Runtime.getRuntime().freeMemory() / 1024 / 8);lruCache = new LruCache<String, Bitmap>(maxSize) {//告诉如何计算@Overrideprotected int sizeOf(String key, Bitmap value) {return value.getRowBytes() * value.getHeight();}};}@Overridepublic void put(BitmapRequest request, Bitmap bitmap) {lruCache.put(request.getImageUrlMD5(), bitmap);}@Overridepublic Bitmap get(BitmapRequest request) {return lruCache.get(request.getImageUrlMD5());}@Overridepublic void remove(BitmapRequest request) {lruCache.remove(request.getImageUrlMD5());}
}
public class DoubleCache implements BitmapCache{//内存缓存private MemoryCache memoryCache = new MemoryCache();//硬盘缓存private DiskCache diskCache;public DoubleCache(Context context){diskCache = DiskCache.getInstance(context);}@Overridepublic void put(BitmapRequest request, Bitmap bitmap) {memoryCache.put(request,bitmap);diskCache.put(request,bitmap);}@Overridepublic Bitmap get(BitmapRequest request) {Bitmap bitmap = memoryCache.get(request);if(bitmap == null){bitmap = diskCache.get(request);if(bitmap != null){//放在内存,方便读取memoryCache.put(request,bitmap);}}return bitmap;}@Overridepublic void remove(BitmapRequest request) {memoryCache.remove(request);diskCache.remove(request);}
}

硬盘缓存调用了一个开源库DiskLruCache,用到了其中的DiskLruCache.java,IOUtils.javaStrictLineReader.java
接下来时加载策略,包括加载策略接口LoadPolicy,ReversePolicy和SerialPolicy

//加载策略接口
public interface LoadPolicy {//优先级比较int compareto(BitmapRequest request1,BitmapRequest request2);
}
//逆序加载策略
public class ReversePolicy implements LoadPolicy{@Overridepublic int compareto(BitmapRequest request1, BitmapRequest request2) {return request2.getSerialNo() - request1.getSerialNo();}
}
//顺序加载策略
public class SerialPolicy implements LoadPolicy{@Overridepublic int compareto(BitmapRequest request1, BitmapRequest request2) {return request1.getSerialNo() - request2.getSerialNo();}
}

然后是工具类,包括图片解码类BitmapDecoder,图片宽高计算类ImageViewHelper和MD5工具类MD5Utils

//解码图片
public abstract class BitmapDecoder {public Bitmap decodeBitmap(int reqWidth, int reqHeight) {//初始化OptionsBitmapFactory.Options options = new BitmapFactory.Options();//读取部分信息,获得图片宽高options.inJustDecodeBounds = true;//根据bitmap加载图片decodeBitmapWithOption(options);//计算图片缩放比例caculateSizeWithOption(options, reqWidth, reqHeight);//返回缩放后的Bitmapreturn decodeBitmapWithOption(options);}private void caculateSizeWithOption(BitmapFactory.Options options, int reqWidth, int reqHeight) {int width = options.outWidth;int height = options.outHeight;int inSampleSize = 1;if (width > reqWidth || height > reqHeight) {int widthRatio = Math.round((float) width / (float) reqWidth);int heightRatio = Math.round((float) height / (float) reqHeight);inSampleSize = Math.max(widthRatio, heightRatio);}options.inSampleSize = inSampleSize;options.inPreferredConfig = Bitmap.Config.RGB_565;options.inJustDecodeBounds = false;//内存不足时回收Bitmapoptions.inPurgeable = true;options.inInputShareable = true;}public abstract Bitmap decodeBitmapWithOption(BitmapFactory.Options options);
}
public class ImageViewHelper {//默认的图片宽高private static int DEFAULT_WIDTH = 200;private static int DEFAULT_HEIGHT = 200;//获取ImageView控件的宽度public static int getImageViewWidth(ImageView imageView){if(imageView != null){LayoutParams params = imageView.getLayoutParams();int width = 0;if(params != null && params.width != LayoutParams.WRAP_CONTENT){width = imageView.getWidth();}if(width <= 0 && params != null){width = params.width;}if(width <= 0){width = getImageViewFieldValue(imageView,"mMaxWidth");}return width;}return DEFAULT_WIDTH;}//获取图片的高度public static int getImageViewHeight(ImageView imageView){if(imageView != null){LayoutParams params = imageView.getLayoutParams();int height = 0;if(params != null && params.height != LayoutParams.WRAP_CONTENT){height = imageView.getWidth();}if(height <= 0 && params != null){height = params.height;}if(height <= 0){height = getImageViewFieldValue(imageView,"mMaxHeight");}return height;}return DEFAULT_HEIGHT;}private static int getImageViewFieldValue(ImageView imageView,String fieldName) {try {Field field = ImageView.class.getDeclaredField(fieldName);field.setAccessible(true);int fieldValue = (Integer)field.get(imageView);if(fieldValue > 0 && fieldValue < Integer.MAX_VALUE){return fieldValue;}} catch (Exception e) {e.printStackTrace();}return 0;}
}
public class MD5Utils {private static final String TAG = "MD5Utils";private static MessageDigest digest;static {try {digest = MessageDigest.getInstance("MD5");} catch (NoSuchAlgorithmException e) {e.printStackTrace();Log.d(TAG, "MD5算法不支持");}}//MD5加密public static String toMD5(String key) {if (digest == null) {return String.valueOf(key.hashCode());}//更新字节digest.update(key.getBytes());//获取最终的摘要return convert2HexString(digest.digest());}//转为16进制字符串private static String convert2HexString(byte[] bytes) {StringBuffer sb = new StringBuffer();for (byte b : bytes) {String hex = Integer.toHexString(0xFF & b);if (hex.length() == 1) {sb.append('0');}sb.append(hex);}return sb.toString();}
}

最后是加载类,包括加载接口Loader,加载抽象类AbstractLoader,硬盘加载器LocalLoader,网络加载器UrlLoader,空加载器NullLoader,图片加载器SimpeImageLoader,加载器管理LoaderManager

//加载器接口
public interface Loader {//加载图片void loadImage(BitmapRequest request);
}
//抽象加载器
public abstract class AbstractLoader implements Loader {//持有缓存策略,得到自定义缓存策略private BitmapCache bitmapCache = SimpeImageLoader.getInstance().getConfig().getBitmapCache();//拿到显示配置private DisplayConfig displayConfig = SimpeImageLoader.getInstance().getConfig().getDisplayConfig();@Overridepublic void loadImage(BitmapRequest request) {//从缓存中读取bitmapBitmap bitmap = bitmapCache.get(request);if (bitmap == null) {//显示默认加载图片showLoadingImage(request);//加载图片bitmap = onLoad(request);//缓存图片cacheBitmap(request, bitmap);}deliveryToUIThread(request, bitmap);}//交给主线程显示protected void deliveryToUIThread(final BitmapRequest request, final Bitmap bitmap) {ImageView imageView = request.getImageView();if (imageView != null) {imageView.post(new Runnable() {@Overridepublic void run() {updateImageView(request, bitmap);}});}}//更新ImageViewprivate void updateImageView(BitmapRequest request, Bitmap bitmap) {ImageView imageView = request.getImageView();//加载正常if (bitmap != null && imageView.getTag().equals(request.getImageUrl())) {imageView.setImageBitmap(bitmap);}//加载失败if (bitmap == null && request.getDisplayConfig() != null &&request.getDisplayConfig().failedImage != -1) {imageView.setImageResource(displayConfig.failedImage);}//监听 回调if (request.imageListener != null) {request.imageListener.onComplete(imageView, bitmap, request.getImageUrl());}}//缓存图片private void cacheBitmap(BitmapRequest request, Bitmap bitmap) {if (request != null && bitmap != null) {synchronized (AbstractLoader.class) {bitmapCache.put(request, bitmap);}}}//抽象的加载方法,由子类去实现protected abstract Bitmap onLoad(BitmapRequest request);//加载前显示的图片protected void showLoadingImage(BitmapRequest request) {if (hasLoadingPlaceHolder()) {final ImageView imageView = request.getImageView();if (imageView != null) {imageView.post(new Runnable() {@Overridepublic void run() {imageView.setImageResource(displayConfig.loadingImage);}});}}}protected boolean hasLoadingPlaceHolder() {return (displayConfig != null && displayConfig.loadingImage > 0);}protected boolean hasFailedPlaceHolder() {return (displayConfig != null && displayConfig.failedImage > 0);}
}
//硬盘加载器
public class LocalLoader extends AbstractLoader{@Overrideprotected Bitmap onLoad(BitmapRequest request) {//得到本地图片路径final String path = Uri.parse(request.getImageUrl()).getPath();File file = new File(path);if(!file.exists()){return null;}BitmapDecoder decoder = new BitmapDecoder() {@Overridepublic Bitmap decodeBitmapWithOption(BitmapFactory.Options options) {return BitmapFactory.decodeFile(path,options);}};return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView()),ImageViewHelper.getImageViewHeight(request.getImageView()));}
}
//网络加载器
public class UrlLoader extends AbstractLoader {@Overrideprotected Bitmap onLoad(final BitmapRequest request) {//下载之后读取downloadImgByUrl(request.getImageUrl(), getCache(request.getImageUrlMD5()));BitmapDecoder decoder = new BitmapDecoder() {@Overridepublic Bitmap decodeBitmapWithOption(BitmapFactory.Options options) {return BitmapFactory.decodeFile(getCache(request.getImageUrlMD5()).getAbsolutePath(), options);}};return decoder.decodeBitmap(ImageViewHelper.getImageViewWidth(request.getImageView()), ImageViewHelper.getImageViewHeight(request.getImageView()));}public static boolean downloadImgByUrl(String urlStr, File file) {FileOutputStream fos = null;InputStream is = null;try {URL url = new URL(urlStr);HttpURLConnection conn = (HttpURLConnection) url.openConnection();is = conn.getInputStream();fos = new FileOutputStream(file);byte[] buf = new byte[512];int len = 0;while ((len = is.read(buf)) != -1) {fos.write(buf, 0, len);}fos.flush();return true;} catch (Exception e) {e.printStackTrace();} finally {try {if (is != null)is.close();} catch (IOException e) {e.printStackTrace();}try {if (fos != null)fos.close();} catch (IOException e) {e.printStackTrace();}}return false;}private File getCache(String unipue) {File file = new File(Environment.getExternalStorageDirectory(), "ImageLoader");if (!file.exists()) {file.mkdir();}return new File(file, unipue);}
}
public class NullLoader extends AbstractLoader {@Overrideprotected Bitmap onLoad(BitmapRequest request) {return null;}
}
//图片加载器,单利对象
public class SimpeImageLoader {//配置文件private ImageLoaderConfig config;//请求队列private RequestQueue queue;//单利private static volatile SimpeImageLoader instance;private SimpeImageLoader() {}private SimpeImageLoader(ImageLoaderConfig config) {this.config = config;queue = new RequestQueue(config.getThreadCount());//开启请求队列queue.start();}public static SimpeImageLoader getInstance(ImageLoaderConfig config) {if (instance == null) {synchronized (SimpeImageLoader.class) {if (instance == null) {instance = new SimpeImageLoader(config);}}}return instance;}//第二次获取单利public static SimpeImageLoader getInstance() {if (instance == null) {throw new UnsupportedOperationException("未初始化参数");}return instance;}//获取全局配置public ImageLoaderConfig getConfig(){return config;}//获取图片public void displayImage(ImageView imageView, String url) {displayImage(imageView, url, null, null);}//扩展,重载public void displayImage(ImageView imageView, String url,DisplayConfig displayConfig, ImageListener imageListener) {//实例化请求BitmapRequest bitmapRequest = new BitmapRequest(imageView,url,displayConfig,imageListener);//添加请求到队列queue.addRequest(bitmapRequest);}//扩展接口public interface ImageListener {void onComplete(ImageView imageView, Bitmap bitmap, String url);}
}
//加载器管理
public class LoaderManager {//缓存支持的Loader类型private Map<String, Loader> loaderMap = new HashMap<>();//单例模式private static LoaderManager instance = new LoaderManager();private LoaderManager() {register("http", new UrlLoader());register("https", new UrlLoader());register("file", new LocalLoader());}public static LoaderManager getInstance() {return instance;}private void register(String schema, Loader Loader) {loaderMap.put(schema, Loader);}public Loader getLoader(String schema){if(loaderMap.containsKey(schema)){return loaderMap.get(schema);}return new NullLoader();}
}

最后是测试类,这里我是用tomcat搭建服务器,使用975张图片做测试

public class MainActivity extends AppCompatActivity {private SimpeImageLoader imageLoader;private static final int COUNT = 975;private static final String path = "http://192.168.1.2:8080/test/";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.list);GridView listview = (GridView) findViewById(R.id.listview);listview.setAdapter(new MyAdapter(this));//配置ImageLoaderConfig.Builder build = new ImageLoaderConfig.Builder();build.setThreadCount(3) //线程数量.setLoadPolicy(new ReversePolicy()) //加载策略.setCachePolicy(new DoubleCache(this)) //缓存策略.setLoadingImage(R.drawable.loading).setFaildImage(R.drawable.not_found);ImageLoaderConfig config = build.build();//初始化imageLoader = SimpeImageLoader.getInstance(config);}class MyAdapter extends BaseAdapter {private LayoutInflater inflater;public MyAdapter(Context context) {inflater = LayoutInflater.from(context);}@Overridepublic int getCount() {return COUNT;}@Overridepublic Object getItem(int position) {return getUrl(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {View item = inflater.inflate(R.layout.item, null);ImageView imageView = (ImageView) item.findViewById(R.id.iv);//请求图片imageLoader.displayImage(imageView, getUrl(position));return item;}}public String getUrl(int position) {if (position < 10)return path + "00" + position + ".jpg";else if (position < 100)return path + "0" + position + ".jpg";else if (position < COUNT)return path + position + ".jpg";elsereturn null;}
}

其实,这就是缩水版的Glide

移动架构-图片加载框架设计相关推荐

  1. Android设计一个图片加载框架

    本文不是具体编码去实现一个图片加载的框架,而是从理论上来讲解设计一个图片加载框架的注意事项和涉及的知识点,提供一个思路,或者帮助童鞋们应付面试.目前Android 发展至今优秀的图片加载框架太多,例如 ...

  2. 图片加载框架Picasso - 源码分析

    简书:图片加载框架Picasso - 源码分析 前一篇文章讲了Picasso的详细用法,Picasso 是一个强大的图片加载缓存框架,一个非常优秀的开源库,学习一个优秀的开源库,,我们不仅仅是学习它的 ...

  3. Android Glide图片加载框架(二)源码解析之with()

    文章目录 一.前言 二.如何阅读源码 三.源码解析 1.with() Android Glide图片加载框架系列文章 Android Glide图片加载框架(一)基本用法 Android Glide图 ...

  4. Android高效异步图片加载框架

    概述 Android高效异步图片加载框架:一个高效的异步加载显示的图片加载框架,同时具备图片压缩,缓存机制等特性. 详细 代码下载:http://www.demodashi.com/demo/1214 ...

  5. Android图片加载框架最全解析(二),从源码的角度理解Glide的执行流程

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/53939176 本文同步发表于我的微信公众号,扫一扫文章底部的二维码或在微信搜索 郭 ...

  6. Android 框架练成 教你打造高效的图片加载框架

    转载请标明出处: http://blog.csdn.net/lmj623565791/article/details/41874561 ,本文出自: [张鸿洋的博客] 1.概述 优秀的图片加载框架不要 ...

  7. Android图片加载框架比较

    做android的同学肯定都使用过imageloader这款图片加载框架. 图片加载对于中低级的安卓开发人员来说是相当不容易的,因为图片加载时做容易造成安卓内存溢出的原因,而要解决这些问题还需要很多相 ...

  8. Android Glide图片加载框架(三)缓存机制

    文章目录 一.缓存简介 二.缓存用法 内存缓存方式 磁盘缓存方式 三.缓存KEY 四.内存缓存 内存缓存流程 五.磁盘缓存 磁盘缓存流程 Android Glide图片加载框架系列文章 Android ...

  9. Android图片加载框架最全解析(八),带你全面了解Glide 4的用法

    本文转载自郭神的Glide分析系列:http://blog.csdn.net/guolin_blog/article/details/78582548 本文同步发表于我的微信公众号,扫一扫文章底部的二 ...

最新文章

  1. 在线作图丨数据降维方法⑥——消除趋势对应分析(Detrended correspondence analysis, DCA)
  2. altium designer显示Analyzing GND,变得很卡
  3. MaxCompute Tunnel SDK数据上传利器——BufferedWriter使用指南
  4. linux last 命令年份,【帝联运维课堂】(第七十二期)Linux下last命令如何显示年份...
  5. MATLAB机器学习系列-12:蚁群算法优化原理及其matlab实现
  6. 这文字的起始位置_ae制作文字动画?ae文字动画教程
  7. Linux服务器的gou,开源跨平台移动项目Langou【简介】
  8. windows下使用word2vec训练维基百科中文语料全攻略!(三
  9. vue2.0配置 https://github.com/wike933/vuebook
  10. netzapper操作
  11. day39-Spring 14-Spring的JDBC模板:DBCP连接池配置
  12. python制作的项目进度管理_项目管理必备——使用燃尽图监控项目整体进度
  13. 教务管理系统数据字典mysql_学校教务管理系统--数据库课程设计
  14. 联想电脑linux显卡驱动,如何安装从联想官网下载的显卡驱动
  15. 记录一个小程序 input输入框格式手机号方法
  16. 生信搬运工-02-sra文件的下载
  17. 长亭科技安服实习面试
  18. 全面理解搜索Query:当你在搜索引擎中敲下回车后,发生了什么?
  19. CCCC 天梯赛初赛心得
  20. html5文本缩进,CSS怎样缩进文本?

热门文章

  1. reaver使用相关
  2. java调用短信接口实现发送短信
  3. python下载所需要的库时,下载速度太慢,这篇文章教你如何解决
  4. 计算机网络安全及防范措施论文开题报告,计算机网络安全发展论文开题报告
  5. java文件夹命名规则
  6. css3新增属性有哪些?css3中常用的新增属性
  7. css3 新增的文本属性
  8. 热门游戏引擎排行(中国地区)
  9. 软工实践第四次作业-团队展示
  10. Extjs 二级联动