Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解

本篇文章继续为大家介绍Universal-Image-Loader这个开源的图片加载框架,介绍的是图片缓存策略方面的,如果大家对这个开源框架的使用还不了解,大家可以看看我之前写的一篇文章Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用,我们一般去加载大量的图片的时候,都会做缓存策略,缓存又分为内存缓存和硬盘缓存,我之前也写了几篇异步加载大量图片的文章,使用的内存缓存是LruCache这个类,LRU是Least Recently Used 近期最少使用算法,我们可以给LruCache设定一个缓存图片的最大值,它会自动帮我们管理好缓存的图片总大小是否超过我们设定的值, 超过就删除近期最少使用的图片,而作为一个强大的图片加载框架,Universal-Image-Loader自然也提供了多种图片的缓存策略,下面就来详细的介绍下

内存缓存


首先我们来了解下什么是强引用和什么是弱引用?

强引用是指创建一个对象并把这个对象赋给一个引用变量, 强引用有引用变量指向时永远不会被垃圾回收。即使内存不足的时候宁愿报OOM也不被垃圾回收器回收,我们new的对象都是强引用

弱引用通过weakReference类来实现,它具有很强的不确定性,如果垃圾回收器扫描到有着WeakReference的对象,就会将其回收释放内存

现在我们来看Universal-Image-Loader有哪些内存缓存策略

1. 只使用的是强引用缓存

  • LruMemoryCache(这个类就是这个开源框架默认的内存缓存类,缓存的是bitmap的强引用,下面我会从源码上面分析这个类)

2.使用强引用和弱引用相结合的缓存有

  • UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)
  • LRULimitedMemoryCache(这个也是使用的lru算法,和LruMemoryCache不同的是,他缓存的是bitmap的弱引用)
  • FIFOLimitedMemoryCache(先进先出的缓存策略,当超过设定值,先删除最先加入缓存的bitmap)
  • LargestLimitedMemoryCache(当超过缓存限定值,先删除最大的bitmap对象)
  • LimitedAgeMemoryCache(当 bitmap加入缓存中的时间超过我们设定的值,将其删除)

3.只使用弱引用缓存

  • WeakMemoryCache(这个类缓存bitmap的总大小没有限制,唯一不足的地方就是不稳定,缓存的图片容易被回收掉)

上面介绍了Universal-Image-Loader所提供的所有的内存缓存的类,当然我们也可以使用我们自己写的内存缓存类,我们还要看看要怎么将这些内存缓存加入到我们的项目中,我们只需要配置ImageLoaderConfiguration.memoryCache(...),如下

ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)
		.memoryCache(new WeakMemoryCache())
		.build();

下面我们来分析LruMemoryCache这个类的源代码

package com.nostra13.universalimageloader.cache.memory.impl;

import android.graphics.Bitmap;
import com.nostra13.universalimageloader.cache.memory.MemoryCacheAware;

import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * A cache that holds strong references to a limited number of Bitmaps. Each time a Bitmap is accessed, it is moved to
 * the head of a queue. When a Bitmap is added to a full cache, the Bitmap at the end of that queue is evicted and may
 * become eligible for garbage collection.<br />
 * <br />
 * <b>NOTE:</b> This cache uses only strong references for stored Bitmaps.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @since 1.8.1
 */
public class LruMemoryCache implements MemoryCacheAware<String, Bitmap> {

	private final LinkedHashMap<String, Bitmap> map;

	private final int maxSize;
	/** Size of this cache in bytes */
	private int size;

	/** @param maxSize Maximum sum of the sizes of the Bitmaps in this cache */
	public LruMemoryCache(int maxSize) {
		if (maxSize <= 0) {
			throw new IllegalArgumentException("maxSize <= 0");
		}
		this.maxSize = maxSize;
		this.map = new LinkedHashMap<String, Bitmap>(0, 0.75f, true);
	}

	/**
	 * Returns the Bitmap for {@code key} if it exists in the cache. If a Bitmap was returned, it is moved to the head
	 * of the queue. This returns null if a Bitmap is not cached.
	 */
	@Override
	public final Bitmap get(String key) {
		if (key == null) {
			throw new NullPointerException("key == null");
		}

		synchronized (this) {
			return map.get(key);
		}
	}

	/** Caches {@code Bitmap} for {@code key}. The Bitmap is moved to the head of the queue. */
	@Override
	public final boolean put(String key, Bitmap value) {
		if (key == null || value == null) {
			throw new NullPointerException("key == null || value == null");
		}

		synchronized (this) {
			size += sizeOf(key, value);
			Bitmap previous = map.put(key, value);
			if (previous != null) {
				size -= sizeOf(key, previous);
			}
		}

		trimToSize(maxSize);
		return true;
	}

	/**
	 * Remove the eldest entries until the total of remaining entries is at or below the requested size.
	 *
	 * @param maxSize the maximum size of the cache before returning. May be -1 to evict even 0-sized elements.
	 */
	private void trimToSize(int maxSize) {
		while (true) {
			String key;
			Bitmap value;
			synchronized (this) {
				if (size < 0 || (map.isEmpty() && size != 0)) {
					throw new IllegalStateException(getClass().getName() + ".sizeOf() is reporting inconsistent results!");
				}

				if (size <= maxSize || map.isEmpty()) {
					break;
				}

				Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next();
				if (toEvict == null) {
					break;
				}
				key = toEvict.getKey();
				value = toEvict.getValue();
				map.remove(key);
				size -= sizeOf(key, value);
			}
		}
	}

	/** Removes the entry for {@code key} if it exists. */
	@Override
	public final void remove(String key) {
		if (key == null) {
			throw new NullPointerException("key == null");
		}

		synchronized (this) {
			Bitmap previous = map.remove(key);
			if (previous != null) {
				size -= sizeOf(key, previous);
			}
		}
	}

	@Override
	public Collection<String> keys() {
		synchronized (this) {
			return new HashSet<String>(map.keySet());
		}
	}

	@Override
	public void clear() {
		trimToSize(-1); // -1 will evict 0-sized elements
	}

	/**
	 * Returns the size {@code Bitmap} in bytes.
	 * <p/>
	 * An entry‘s size must not change while it is in the cache.
	 */
	private int sizeOf(String key, Bitmap value) {
		return value.getRowBytes() * value.getHeight();
	}

	@Override
	public synchronized final String toString() {
		return String.format("LruCache[maxSize=%d]", maxSize);
	}
}

我们可以看到这个类中维护的是一个LinkedHashMap,在LruMemoryCache构造函数中我们可以看到,我们为其设置了一个缓存图片的最大值maxSize,并实例化LinkedHashMap, 而从LinkedHashMap构造函数的第三个参数为ture,表示它是按照访问顺序进行排序的,
我们来看将bitmap加入到LruMemoryCache的方法put(String key, Bitmap value),  第61行,sizeOf()是计算每张图片所占的byte数,size是记录当前缓存bitmap的总大小,如果该key之前就缓存了bitmap,我们需要将之前的bitmap减掉去,接下来看trimToSize()方法,我们直接看86行,如果当前缓存的bitmap总数小于设定值maxSize,不做任何处理,如果当前缓存的bitmap总数大于maxSize,删除LinkedHashMap中的第一个元素,size中减去该bitmap对应的byte数

我们可以看到该缓存类比较简单,逻辑也比较清晰,如果大家想知道其他内存缓存的逻辑,可以去分析分析其源码,在这里我简单说下FIFOLimitedMemoryCache的实现逻辑,该类使用的HashMap来缓存bitmap的弱引用,然后使用LinkedList来保存成功加入到FIFOLimitedMemoryCache的bitmap的强引用,如果加入的FIFOLimitedMemoryCache的bitmap总数超过限定值,直接删除LinkedList的第一个元素,所以就实现了先进先出的缓存策略,其他的缓存都类似,有兴趣的可以去看看。

硬盘缓存


接下来就给大家分析分析硬盘缓存的策略,这个框架也提供了几种常见的缓存策略,当然如果你觉得都不符合你的要求,你也可以自己去扩展

  • FileCountLimitedDiscCache(可以设定缓存图片的个数,当超过设定值,删除掉最先加入到硬盘的文件)
  • LimitedAgeDiscCache(设定文件存活的最长时间,当超过这个值,就删除该文件)
  • TotalSizeLimitedDiscCache(设定缓存bitmap的最大值,当超过这个值,删除最先加入到硬盘的文件)
  • UnlimitedDiscCache(这个缓存类没有任何的限制)

下面我们就来分析分析TotalSizeLimitedDiscCache的源码实现

/*******************************************************************************
 * Copyright 2011-2013 Sergey Tarasevich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.nostra13.universalimageloader.cache.disc.impl;

import com.nostra13.universalimageloader.cache.disc.LimitedDiscCache;
import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
import com.nostra13.universalimageloader.utils.L;

import java.io.File;

/**
 * Disc cache limited by total cache size. If cache size exceeds specified limit then file with the most oldest last
 * usage date will be deleted.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @see LimitedDiscCache
 * @since 1.0.0
 */
public class TotalSizeLimitedDiscCache extends LimitedDiscCache {

	private static final int MIN_NORMAL_CACHE_SIZE_IN_MB = 2;
	private static final int MIN_NORMAL_CACHE_SIZE = MIN_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;

	/**
	 * @param cacheDir     Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
	 *                     needed for right cache limit work.
	 * @param maxCacheSize Maximum cache directory size (in bytes). If cache size exceeds this limit then file with the
	 *                     most oldest last usage date will be deleted.
	 */
	public TotalSizeLimitedDiscCache(File cacheDir, int maxCacheSize) {
		this(cacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxCacheSize);
	}

	/**
	 * @param cacheDir          Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
	 *                          needed for right cache limit work.
	 * @param fileNameGenerator Name generator for cached files
	 * @param maxCacheSize      Maximum cache directory size (in bytes). If cache size exceeds this limit then file with the
	 *                          most oldest last usage date will be deleted.
	 */
	public TotalSizeLimitedDiscCache(File cacheDir, FileNameGenerator fileNameGenerator, int maxCacheSize) {
		super(cacheDir, fileNameGenerator, maxCacheSize);
		if (maxCacheSize < MIN_NORMAL_CACHE_SIZE) {
			L.w("You set too small disc cache size (less than %1$d Mb)", MIN_NORMAL_CACHE_SIZE_IN_MB);
		}
	}

	@Override
	protected int getSize(File file) {
		return (int) file.length();
	}
}

这个类是继承LimitedDiscCache,除了两个构造函数之外,还重写了getSize()方法,返回文件的大小,接下来我们就来看看LimitedDiscCache

/*******************************************************************************
 * Copyright 2011-2013 Sergey Tarasevich
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *******************************************************************************/
package com.nostra13.universalimageloader.cache.disc;

import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;

import java.io.File;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Abstract disc cache limited by some parameter. If cache exceeds specified limit then file with the most oldest last
 * usage date will be deleted.
 *
 * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
 * @see BaseDiscCache
 * @see FileNameGenerator
 * @since 1.0.0
 */
public abstract class LimitedDiscCache extends BaseDiscCache {

	private static final int INVALID_SIZE = -1;

	//记录缓存文件的大小
	private final AtomicInteger cacheSize;
	//缓存文件的最大值
	private final int sizeLimit;
	private final Map<File, Long> lastUsageDates = Collections.synchronizedMap(new HashMap<File, Long>());

	/**
	 * @param cacheDir  Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
	 *                  needed for right cache limit work.
	 * @param sizeLimit Cache limit value. If cache exceeds this limit then file with the most oldest last usage date
	 *                  will be deleted.
	 */
	public LimitedDiscCache(File cacheDir, int sizeLimit) {
		this(cacheDir, DefaultConfigurationFactory.createFileNameGenerator(), sizeLimit);
	}

	/**
	 * @param cacheDir          Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
	 *                          needed for right cache limit work.
	 * @param fileNameGenerator Name generator for cached files
	 * @param sizeLimit         Cache limit value. If cache exceeds this limit then file with the most oldest last usage date
	 *                          will be deleted.
	 */
	public LimitedDiscCache(File cacheDir, FileNameGenerator fileNameGenerator, int sizeLimit) {
		super(cacheDir, fileNameGenerator);
		this.sizeLimit = sizeLimit;
		cacheSize = new AtomicInteger();
		calculateCacheSizeAndFillUsageMap();
	}

	/**
	 * 另开线程计算cacheDir里面文件的大小,并将文件和最后修改的毫秒数加入到Map中
	 */
	private void calculateCacheSizeAndFillUsageMap() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				int size = 0;
				File[] cachedFiles = cacheDir.listFiles();
				if (cachedFiles != null) { // rarely but it can happen, don‘t know why
					for (File cachedFile : cachedFiles) {
						//getSize()是一个抽象方法,子类自行实现getSize()的逻辑
						size += getSize(cachedFile);
						//将文件的最后修改时间加入到map中
						lastUsageDates.put(cachedFile, cachedFile.lastModified());
					}
					cacheSize.set(size);
				}
			}
		}).start();
	}

	/**
	 * 将文件添加到Map中,并计算缓存文件的大小是否超过了我们设置的最大缓存数
	 * 超过了就删除最先加入的那个文件
	 */
	@Override
	public void put(String key, File file) {
		//要加入文件的大小
		int valueSize = getSize(file);

		//获取当前缓存文件大小总数
		int curCacheSize = cacheSize.get();
		//判断是否超过设定的最大缓存值
		while (curCacheSize + valueSize > sizeLimit) {
			int freedSize = removeNext();
			if (freedSize == INVALID_SIZE) break; // cache is empty (have nothing to delete)
			curCacheSize = cacheSize.addAndGet(-freedSize);
		}
		cacheSize.addAndGet(valueSize);

		Long currentTime = System.currentTimeMillis();
		file.setLastModified(currentTime);
		lastUsageDates.put(file, currentTime);
	}

	/**
	 * 根据key生成文件
	 */
	@Override
	public File get(String key) {
		File file = super.get(key);

		Long currentTime = System.currentTimeMillis();
		file.setLastModified(currentTime);
		lastUsageDates.put(file, currentTime);

		return file;
	}

	/**
	 * 硬盘缓存的清理
	 */
	@Override
	public void clear() {
		lastUsageDates.clear();
		cacheSize.set(0);
		super.clear();
	}

	/**
	 * 获取最早加入的缓存文件,并将其删除
	 */
	private int removeNext() {
		if (lastUsageDates.isEmpty()) {
			return INVALID_SIZE;
		}
		Long oldestUsage = null;
		File mostLongUsedFile = null;

		Set<Entry<File, Long>> entries = lastUsageDates.entrySet();
		synchronized (lastUsageDates) {
			for (Entry<File, Long> entry : entries) {
				if (mostLongUsedFile == null) {
					mostLongUsedFile = entry.getKey();
					oldestUsage = entry.getValue();
				} else {
					Long lastValueUsage = entry.getValue();
					if (lastValueUsage < oldestUsage) {
						oldestUsage = lastValueUsage;
						mostLongUsedFile = entry.getKey();
					}
				}
			}
		}

		int fileSize = 0;
		if (mostLongUsedFile != null) {
			if (mostLongUsedFile.exists()) {
				fileSize = getSize(mostLongUsedFile);
				if (mostLongUsedFile.delete()) {
					lastUsageDates.remove(mostLongUsedFile);
				}
			} else {
				lastUsageDates.remove(mostLongUsedFile);
			}
		}
		return fileSize;
	}

	/**
	 * 抽象方法,获取文件大小
	 * @param file
	 * @return
	 */
	protected abstract int getSize(File file);
}

在构造方法中,第69行有一个方法calculateCacheSizeAndFillUsageMap(),该方法是计算cacheDir的文件大小,并将文件和文件的最后修改时间加入到Map中

然后是将文件加入硬盘缓存的方法put(),在106行判断当前文件的缓存总数加上即将要加入缓存的文件大小是否超过缓存设定值,如果超过了执行removeNext()方法,接下来就来看看这个方法的具体实现,150-167中找出最先加入硬盘的文件,169-180中将其从文件硬盘中删除,并返回该文件的大小,删除成功之后成员变量cacheSize需要减掉改文件大小。

FileCountLimitedDiscCache这个类实现逻辑跟TotalSizeLimitedDiscCache是一样的,区别在于getSize()方法,前者返回1,表示为文件数是1,后者返回文件的大小。

等我写完了这篇文章,我才发现FileCountLimitedDiscCache和TotalSizeLimitedDiscCache在最新的源码中已经删除了,加入了LruDiscCache,由于我的是之前的源码,所以我也不改了,大家如果想要了解LruDiscCache可以去看最新的源码,我这里就不介绍了,还好内存缓存的没变化,下面分析的是最新的源码中的部分,我们在使用中可以不自行配置硬盘缓存策略,直接用DefaultConfigurationFactory中的就行了

我们看DefaultConfigurationFactory这个类的createDiskCache()方法

	/**
	 * Creates default implementation of {@link DiskCache} depends on incoming parameters
	 */
	public static DiskCache createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator,
			long diskCacheSize, int diskCacheFileCount) {
		File reserveCacheDir = createReserveDiskCacheDir(context);
		if (diskCacheSize > 0 || diskCacheFileCount > 0) {
			File individualCacheDir = StorageUtils.getIndividualCacheDirectory(context);
			LruDiscCache diskCache = new LruDiscCache(individualCacheDir, diskCacheFileNameGenerator, diskCacheSize,
					diskCacheFileCount);
			diskCache.setReserveCacheDir(reserveCacheDir);
			return diskCache;
		} else {
			File cacheDir = StorageUtils.getCacheDirectory(context);
			return new UnlimitedDiscCache(cacheDir, reserveCacheDir, diskCacheFileNameGenerator);
		}
	}

如果我们在ImageLoaderConfiguration中配置了diskCacheSize和diskCacheFileCount,他就使用的是LruDiscCache,否则使用的是UnlimitedDiscCache,在最新的源码中还有一个硬盘缓存类可以配置,那就是LimitedAgeDiscCache,可以在ImageLoaderConfiguration.diskCache(...)配置

今天就给大家分享到这里,有不明白的地方在下面留言,我会尽量为大家解答的,下一篇文章我将继续更深入的分析这个框架,希望大家继续关注!

Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解,布布扣,bubuko.com

时间: 2024-10-05 05:07:14

Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解的相关文章

【Android应用开发】 推送原理解析 极光推送使用详解 (零基础精通推送)

作者 : octopus_truth 转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/45046283 推送技术产生场景 : -- 服务器端主动性 : 客户端与服务器交互都是客户端主动的, 服务器一般不能主动与客户端进行数据交互, 因为服务器端无法得知客户端的 IP 地址 及 状态; -- 数据实时性 : 如果服务器端有紧急数据要传递给客户端, 就必须主动向客户端发送数据; -- 基本原理 : 使客户端实时获取服务器端消息,

Android 开源框架Universal-Image-Loader完全解析(三)---源代码解读

本篇文章主要是带大家从源码的角度上面去解读这个强大的图片加载框架,自己很久没有写文章了,感觉生疏了许多,距离上一篇文章三个月多了,确实是自己平常忙,换了工作很多东西都要去看去理解,然后加上自己也懒了,没有以前那么有激情了,我感觉这节奏不对,我要继续保持以前的激情,正所谓好记性不如烂笔头,有时候自己也会去翻看下之前写的东西,我觉得知识写下来比在脑海中留存的更久,今天就给大家来读一读这个框架的源码,我感觉这个图片加载框架确实写的很不错,读完代码自己也学到了很多.我希望大家可以先去看下Android

Android——开源框架Universal-Image-Loader + Fragment使用+轮播广告

原文地址: Android 开源框架Universal-Image-Loader完全解析(一)--- 基本介绍及使用 Android 开源框架Universal-Image-Loader完全解析(二)--- 图片缓存策略详解 Android Fragment使用(一) 基础篇 温故知新 Android Fragment使用(二) 嵌套Fragments (Nested Fragments) 的使用及常见错误 Android Fragment使用(三) Activity, Fragment, We

开源项目Universal Image Loader for Android 说明文档 (1) 简介

 When developing applications for Android, one often facesthe problem of displaying some graphical content from the Internet. So, youshould provide image loading from the Web in an Android app, their processingand displaying with limited memory aga

Android开源框架Universal-Image-Loader解析(一)

来自xiaanming的一篇博客:Android开源框架Universal-Image-Loader解析之基本介绍及使用. 相信大家平时做Android应用的时候,多少会接触到异步加载图片,或者加载大量图片的问题,而加载图片我们常常会遇到许多的问题,比如说图片的错乱,OOM等问题,对于新手来说,这些问题解决起来会比较吃力,所以就有很多的开源图片加载框架应运而生,比较著名的就是Universal-Image-Loader,相信很多朋友都听过或者使用过这个强大的图片加载框架,今天这篇文章就是对这个框

Android开源框架 Android-Universal-Image-Loader

Android开源框架Universal-Image-Loader就像图片加载守护者,为我们提供了丰富的功能特性: (1)多线程加载图像(异步或同步): (2)高度可定制化imageloader配置(线程池.图片下载器.解码器.内存和磁盘缓存.显示图像选项等): (3)每一个显示图像有许多自定义选项(存根图片,缓存开关,解码选项,位图处理和显示等): (4)支持内存和磁盘上的图像缓存(设备的文件系统和SD卡): (5)监听加载过程(包括下载进度): 下来我们详解如何配置使用Universal-I

Android开源框架Afinal第一篇——揭开圣女的面纱

Android开源框架Afinal第一篇——揭开圣女的面纱 分类: Android开源框架哪点事2013-09-02 14:25 260人阅读 评论(0) 收藏 举报 Afinal 这是Afinal在github的地址:https://github.com/yangfuhai/afinal Afinal这个框架主要分4块: 1.FinalDB模块:android中的orm框架,一行代码就可以进行增删改查.支持一对多,多对一等查询. 2.FinalActivity模块:android中的ioc框架

android 开源框架推荐

同事整理的 android 开源框架,个个都堪称经典.32 个赞! 1.volley 项目地址 https://github.com/smanikandan14/Volley-demo (1)  JSON,图像等的异步下载: (2)  网络请求的排序(scheduling) (3)  网络请求的优先级处理 (4)  缓存 (5)  多级别取消请求 (6)  和Activity和生命周期的联动(Activity结束时同时取消所有网络请求) 2.android-async-http  项目地址:ht

六款值得推荐的Android开源框架简介

技术不再多,知道一些常用的.不错的就够了.下面就是最近整理的“性价比”比较高的Android开源框架,应该是相对实用的. 1.volley 项目地址 https://github.com/smanikandan14/Volley-demo JSON,图像等的异步下载: 网络请求的排序(scheduling) 网络请求的优先级处理 缓存 多级别取消请求 和Activity和生命周期的联动(Activity结束时同时取消所有网络请求) 2.android-async-http 项目地址:https: