Universal-Image-Loader源码分析,及常用的缓存策略

讲到图片请求,主要涉及到网络请求,内存缓存,硬盘缓存等原理和4大引用的问题,概括起来主要有以下几个内容:

原理示意图

主体有三个,分别是UI,缓存模块和数据源(网络)。它们之间的关系如下:

① UI:请求数据,使用唯一的Key值索引Memory Cache中的Bitmap。

 内存缓存:缓存搜索,如果能找到Key值对应的Bitmap,则返回数据。否则执行第三步。

 硬盘存储:使用唯一Key值对应的文件名,检索SDCard上的文件。

 如果有对应文件,使用BitmapFactory.decode*方法,解码Bitmap并返回数据,同时将数据写入缓存。如果没有对应文件,执行第五步。

 下载图片:启动异步线程,从数据源下载数据(Web)。

⑥ 若下载成功,将数据同时写入硬盘和缓存,并将Bitmap显示在UI中。

UIL中的内存缓存策略

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

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

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

UsingFreqLimitedMemoryCache(如果缓存的图片总量超过限定值,先删除使用频率最小的bitmap)

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

3.只使用弱引用缓存

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

我们直接选择UIL中的默认配置缓存策略进行分析。

view
source
print?

1.ImageLoaderConfiguration
config = ImageLoaderConfiguration.createDefault(context);

ImageLoaderConfiguration.createDefault(…)这个方法最后是调用Builder.build()方法创建默认的配置参数的。默认的内存缓存实现是LruMemoryCache,磁盘缓存是UnlimitedDiscCache。

所以android在以后的版本中建议使用LruMemoryCache进行管理。

LruMemoryCache:一种使用强引用来保存有数量限制的Bitmap的cache(在空间有限的情况,保留最近使用过的Bitmap)。每次Bitmap被访问时,它就被移动到一个队列的头部。当Bitmap被添加到一个空间已满的cache时,在队列末尾的Bitmap会被挤出去并变成适合被GC回收的状态。

注意:这个cache只使用强引用来保存Bitmap。

LruMemoryCache实现MemoryCache,而MemoryCache继承自MemoryCacheAware。

view
source
print?

1.public interface MemoryCache extends MemoryCacheAware<String,
Bitmap>

下面给出继承关系图

通过跟踪LruMemoryCache.get()的代码我们发现,LruMemoryCache声称保留在空间有限的情况下保留最近使用过的Bitmap,这是一个LinkedHashMap<String,
Bitmap>,Icache的源码缓存的代码如下:

@Override

public ICache<String, Bitmap> getBitmapCache() {

if (bitmapCache == null) {

bitmapCache = new ICache<String, Bitmap>() {

@Override

public void remove(String key) {

ImageLoader.getInstance().getMemoryCache().remove(key);

}

@Override

public boolean put(String key, Bitmap value) {

if (key == null || value == null) return false;

return ImageLoader.getInstance().getMemoryCache().put(key, value);

}

@Override

public Collection<String> keys() {

return null;

}

@Override

public Bitmap get(String key) {

return ImageLoader.getInstance().getMemoryCache().get(key);

}

@Override

public void clear() {

ImageLoader.getInstance().getMemoryCache().clear();

}

};

}

return bitmapCache;

}

看到这里我们就清楚LruMemoryCache使用LinkedHashMap来缓存数据,在LinkedHashMap.get()方法执行后,LinkedHashMap中entry的顺序会得到调整。那么我们怎么保证最近使用的项不会被剔除呢?接下去,让我们看看LruMemoryCache.put(...)。

注意到代码第8行中的size+=
sizeOf(key, value),这个size是什么呢?我们注意到在第19行有一个trimToSize(maxSize),trimToSize(...)这个函数就是用来限定LruMemoryCache的大小不要超过用户限定的大小,cache的大小由用户在LruMemoryCache刚开始初始化的时候限定。

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

        this.trimToSize(this.maxSize);
        return true;
    } else {
        throw new NullPointerException("key == null || value == null");
    }
}

所以不难理解我们在用Universal-Image-Loader做图片加载的时候,有时候图片缓存超过阈值的时候,会去重新重服务器加载了

当Bitmap缓存的大小超过原来设定的maxSize时应该是在trimToSize(...)这个函数中做到的。这个函数做的事情也简单,遍历map,将多余的项(代码中对应toEvict)剔除掉,直到当前cache的大小等于或小于限定的大小。

private void trimToSize(int maxSize) {
    while(true) {
        synchronized(this) {
            if(this.size < 0 || this.map.isEmpty() && this.size != 0) {
                throw new IllegalStateException(this.getClass().getName() + ".sizeOf() is reporting inconsistent results!");
            }

            if(this.size > maxSize && !this.map.isEmpty()) {
                Entry toEvict = (Entry)this.map.entrySet().iterator().next();
                if(toEvict != null) {
                    String key = (String)toEvict.getKey();
                    Bitmap value = (Bitmap)toEvict.getValue();
                    this.map.remove(key);
                    this.size -= this.sizeOf(key, value);
                    continue;
                }
            }

            return;
        }
    }
}

这时候我们会有一个以为,为什么遍历一下就可以将使用最少的bitmap缓存给剔除,不会误删到最近使用的bitmap缓存吗?首先,我们要清楚,LruMemoryCache定义的最近使用是指最近用get或put方式操作到的bitmap缓存。其次,之前我们直到LruMemoryCache的get操作其实是通过其内部字段LinkedHashMap.get(...)实现的,当LinkedHashMap的accessOrder==true时,每一次get或put操作都会将所操作项(图中第3项)移动到链表的尾部(见下图,链表头被认为是最少使用的,链表尾被认为是最常使用的。),每一次操作到的项我们都认为它是最近使用过的,当内存不够的时候被剔除的优先级最低。需要注意的是一开始的LinkedHashMap链表是按插入的顺序构成的,也就是第一个插入的项就在链表头,最后一个插入的就在链表尾。假设只要剔除图中的1,2项就能让LruMemoryCache小于原先限定的大小,那么我们只要从链表头遍历下去(从1→最后一项)那么就可以剔除使用最少的项了。

           

至此,我们就知道了LruMemoryCache缓存的整个原理,包括他怎么put、get、剔除一个元素的的策略。接下去,我们要开始分析默认的磁盘缓存策略了。

UIL中的磁盘缓存策略

幸好UIL提供了几种常见的磁盘缓存策略,你也可以自己去扩展,可以根据他提供的几种缓存策略做进一步的缓存值的限制,

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

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

在UIL中有着比较完整的存储策略,根据预先指定的空间大小,使用频率(生命周期),文件个数的约束条件,都有着对应的实现策略。最基础的接口DiscCacheAware和抽象类BaseDiscCache

接下来我们看一些硬盘缓存的一些策略:

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

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

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

[java] view
plain
 copy

  1. /*******************************************************************************
  2. * Copyright 2011-2013 Sergey Tarasevich
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *******************************************************************************/
  16. package com.nostra13.universalimageloader.cache.disc.impl;
  17. import com.nostra13.universalimageloader.cache.disc.LimitedDiscCache;
  18. import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  19. import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
  20. import com.nostra13.universalimageloader.utils.L;
  21. import java.io.File;
  22. /**
  23. * Disc cache limited by total cache size. If cache size exceeds specified limit then file with the most oldest last
  24. * usage date will be deleted.
  25. *
  26. * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  27. * @see LimitedDiscCache
  28. * @since 1.0.0
  29. */
  30. public class TotalSizeLimitedDiscCache extends LimitedDiscCache {
  31. private static final int MIN_NORMAL_CACHE_SIZE_IN_MB = 2;
  32. private static final int MIN_NORMAL_CACHE_SIZE = MIN_NORMAL_CACHE_SIZE_IN_MB * 1024 * 1024;
  33. /**
  34. * @param cacheDir     Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
  35. *                     needed for right cache limit work.
  36. * @param maxCacheSize Maximum cache directory size (in bytes). If cache size exceeds this limit then file with the
  37. *                     most oldest last usage date will be deleted.
  38. */
  39. public TotalSizeLimitedDiscCache(File cacheDir, int maxCacheSize) {
  40. this(cacheDir, DefaultConfigurationFactory.createFileNameGenerator(), maxCacheSize);
  41. }
  42. /**
  43. * @param cacheDir          Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
  44. *                          needed for right cache limit work.
  45. * @param fileNameGenerator Name generator for cached files
  46. * @param maxCacheSize      Maximum cache directory size (in bytes). If cache size exceeds this limit then file with the
  47. *                          most oldest last usage date will be deleted.
  48. */
  49. public TotalSizeLimitedDiscCache(File cacheDir, FileNameGenerator fileNameGenerator, int maxCacheSize) {
  50. super(cacheDir, fileNameGenerator, maxCacheSize);
  51. if (maxCacheSize < MIN_NORMAL_CACHE_SIZE) {
  52. L.w("You set too small disc cache size (less than %1$d Mb)", MIN_NORMAL_CACHE_SIZE_IN_MB);
  53. }
  54. }
  55. @Override
  56. protected int getSize(File file) {
  57. return (int) file.length();
  58. }
  59. }

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

[java] view
plain
 copy

  1. /*******************************************************************************
  2. * Copyright 2011-2013 Sergey Tarasevich
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *******************************************************************************/
  16. package com.nostra13.universalimageloader.cache.disc;
  17. import com.nostra13.universalimageloader.cache.disc.naming.FileNameGenerator;
  18. import com.nostra13.universalimageloader.core.DefaultConfigurationFactory;
  19. import java.io.File;
  20. import java.util.Collections;
  21. import java.util.HashMap;
  22. import java.util.Map;
  23. import java.util.Map.Entry;
  24. import java.util.Set;
  25. import java.util.concurrent.atomic.AtomicInteger;
  26. /**
  27. * Abstract disc cache limited by some parameter. If cache exceeds specified limit then file with the most oldest last
  28. * usage date will be deleted.
  29. *
  30. * @author Sergey Tarasevich (nostra13[at]gmail[dot]com)
  31. * @see BaseDiscCache
  32. * @see FileNameGenerator
  33. * @since 1.0.0
  34. */
  35. public abstract class LimitedDiscCache extends BaseDiscCache {
  36. private static final int INVALID_SIZE = -1;
  37. //记录缓存文件的大小
  38. private final AtomicInteger cacheSize;
  39. //缓存文件的最大值
  40. private final int sizeLimit;
  41. private final Map<File, Long> lastUsageDates = Collections.synchronizedMap(new HashMap<File, Long>());
  42. /**
  43. * @param cacheDir  Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
  44. *                  needed for right cache limit work.
  45. * @param sizeLimit Cache limit value. If cache exceeds this limit then file with the most oldest last usage date
  46. *                  will be deleted.
  47. */
  48. public LimitedDiscCache(File cacheDir, int sizeLimit) {
  49. this(cacheDir, DefaultConfigurationFactory.createFileNameGenerator(), sizeLimit);
  50. }
  51. /**
  52. * @param cacheDir          Directory for file caching. <b>Important:</b> Specify separate folder for cached files. It‘s
  53. *                          needed for right cache limit work.
  54. * @param fileNameGenerator Name generator for cached files
  55. * @param sizeLimit         Cache limit value. If cache exceeds this limit then file with the most oldest last usage date
  56. *                          will be deleted.
  57. */
  58. public LimitedDiscCache(File cacheDir, FileNameGenerator fileNameGenerator, int sizeLimit) {
  59. super(cacheDir, fileNameGenerator);
  60. this.sizeLimit = sizeLimit;
  61. cacheSize = new AtomicInteger();
  62. calculateCacheSizeAndFillUsageMap();
  63. }
  64. /**
  65. * 另开线程计算cacheDir里面文件的大小,并将文件和最后修改的毫秒数加入到Map中
  66. */
  67. private void calculateCacheSizeAndFillUsageMap() {
  68. new Thread(new Runnable() {
  69. @Override
  70. public void run() {
  71. int size = 0;
  72. File[] cachedFiles = cacheDir.listFiles();
  73. if (cachedFiles != null) { // rarely but it can happen, don‘t know why
  74. for (File cachedFile : cachedFiles) {
  75. //getSize()是一个抽象方法,子类自行实现getSize()的逻辑
  76. size += getSize(cachedFile);
  77. //将文件的最后修改时间加入到map中
  78. lastUsageDates.put(cachedFile, cachedFile.lastModified());
  79. }
  80. cacheSize.set(size);
  81. }
  82. }
  83. }).start();
  84. }
  85. /**
  86. * 将文件添加到Map中,并计算缓存文件的大小是否超过了我们设置的最大缓存数
  87. * 超过了就删除最先加入的那个文件
  88. */
  89. @Override
  90. public void put(String key, File file) {
  91. //要加入文件的大小
  92. int valueSize = getSize(file);
  93. //获取当前缓存文件大小总数
  94. int curCacheSize = cacheSize.get();
  95. //判断是否超过设定的最大缓存值
  96. while (curCacheSize + valueSize > sizeLimit) {
  97. int freedSize = removeNext();
  98. if (freedSize == INVALID_SIZE) break; // cache is empty (have nothing to delete)
  99. curCacheSize = cacheSize.addAndGet(-freedSize);
  100. }
  101. cacheSize.addAndGet(valueSize);
  102. Long currentTime = System.currentTimeMillis();
  103. file.setLastModified(currentTime);
  104. lastUsageDates.put(file, currentTime);
  105. }
  106. /**
  107. * 根据key生成文件
  108. */
  109. @Override
  110. public File get(String key) {
  111. File file = super.get(key);
  112. Long currentTime = System.currentTimeMillis();
  113. file.setLastModified(currentTime);
  114. lastUsageDates.put(file, currentTime);
  115. return file;
  116. }
  117. /**
  118. * 硬盘缓存的清理
  119. */
  120. @Override
  121. public void clear() {
  122. lastUsageDates.clear();
  123. cacheSize.set(0);
  124. super.clear();
  125. }
  126. /**
  127. * 获取最早加入的缓存文件,并将其删除
  128. */
  129. private int removeNext() {
  130. if (lastUsageDates.isEmpty()) {
  131. return INVALID_SIZE;
  132. }
  133. Long oldestUsage = null;
  134. File mostLongUsedFile = null;
  135. Set<Entry<File, Long>> entries = lastUsageDates.entrySet();
  136. synchronized (lastUsageDates) {
  137. for (Entry<File, Long> entry : entries) {
  138. if (mostLongUsedFile == null) {
  139. mostLongUsedFile = entry.getKey();
  140. oldestUsage = entry.getValue();
  141. } else {
  142. Long lastValueUsage = entry.getValue();
  143. if (lastValueUsage < oldestUsage) {
  144. oldestUsage = lastValueUsage;
  145. mostLongUsedFile = entry.getKey();
  146. }
  147. }
  148. }
  149. }
  150. int fileSize = 0;
  151. if (mostLongUsedFile != null) {
  152. if (mostLongUsedFile.exists()) {
  153. fileSize = getSize(mostLongUsedFile);
  154. if (mostLongUsedFile.delete()) {
  155. lastUsageDates.remove(mostLongUsedFile);
  156. }
  157. } else {
  158. lastUsageDates.remove(mostLongUsedFile);
  159. }
  160. }
  161. return fileSize;
  162. }
  163. /**
  164. * 抽象方法,获取文件大小
  165. * @param file
  166. * @return
  167. */
  168. protected abstract int getSize(File file);
  169. }

最后看一些我们需要怎么使用

配置:

ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(YmatouApplication.instance())
        .threadPriority(Thread.NORM_PRIORITY - 1)
        .memoryCache(new LruMemoryCache(getMemoryCacheSize()))
        .diskCacheSize(50 * ByteConstants.MB)
        .diskCacheFileNameGenerator(new Md5FileNameGenerator())
        .diskCacheFileCount(200)
        .memoryCacheExtraOptions(480, 800)
        .threadPoolSize(3)
        .memoryCache(new WeakMemoryCache())
        .tasksProcessingOrder(QueueProcessingType.FIFO)
        .defaultDisplayImageOptions(getSimpleOptions().build())
        .build();
ImageLoader.getInstance().init(config);

在做网络请求的时候,我们也做些配置,如网络请求之前默认什么背景,请求完成后,请求失败后怎么显示我们都可以在

defaultDisplayImageOptions(DisplayImageOptions.Builder builed)进行配置
private static DisplayImageOptions.Builder getSimpleOptions() {
    DisplayImageOptions.Builder options = new DisplayImageOptions.Builder()
            .cacheInMemory(true)
            .cacheOnDisk(true)
            .considerExifParams(true)
            .imageScaleType(ImageScaleType.EXACTLY)
            .showImageOnLoading(R.drawable.image_loading)
            .showImageForEmptyUri(R.drawable.image_default)
            .showImageOnFail(R.drawable.image_failed)
            .resetViewBeforeLoading(true)
            .bitmapConfig(Bitmap.Config.ARGB_8888);
    return options;
}

加载图片:

public static void imageloader(String url, ImageView imageView) {
    displayImage(url, imageView, null);
}

时间: 2024-11-05 12:05:48

Universal-Image-Loader源码分析,及常用的缓存策略的相关文章

cglib源码分析2 ----- Class name 生成策略

一.如何获取动态生成的class 字节码 结合生成的class文件是一个学习cglib的比较好的方法.在cglib中,生成的class文件默认只存储在内存中,我们可以在代码中加入下面语句来获取class file. System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\code "); 这里涉及到了一个比较关键的类DebuggingClassWriter,接下来我们简单的分析一下这个类.Debuggi

Mybatis源码分析之Cache二级缓存原理 (五)

一:Cache类的介绍 讲解缓存之前我们需要先了解一下Cache接口以及实现MyBatis定义了一个org.apache.ibatis.cache.Cache接口作为其Cache提供者的SPI(ServiceProvider Interface) ,所有的MyBatis内部的Cache缓存,都应该实现这一接口 Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现. 看下cache的实现类,如下图: 1.FIFOCache:先进

mybatis源码分析之05一级缓存

首先需要明白,mybatis的一级缓存就是指SqlSession缓存,Map缓步! 通过前面的源码分析知道mybatis框架默认使用的是DefaultSqlSession,它是由DefaultSqlSessionFactory创建的,下面是源码 private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Tran

mybatis缓存源码分析之浅谈缓存设计

一般我们用到缓存的时候,只知道他很快,很强,还能持久,但是为什么他可以做到这些呢,有人会说这是天赋,遗传的,是的,你想的没错,确实是大佬们在构造这些的时候,赋予他这些能力,那今天我们就来剖析一下,大佬们干了啥,区区缓存就能这么厉害. 去大厂面试的时候,面试官总会喜欢问为什么,一开始,完全搞不懂我就去拧个螺丝,你问我造火箭怎么造我咋知道,后来在工作中遇到各种各样的问题,解决不了的时候,看着身边大佬们一层层点进去看源码分析问题的时候,瞬间觉得这多牛逼啊,或许一开始有动力看源码,了解为什么就是因为这个

Spring源码分析(十三)缓存中获取单例bean

摘要:本文结合<Spring源码深度解析>来分析Spring 5.0.6版本的源代码.若有描述错误之处,欢迎指正. 介绍过FactoryBean的用法后,我们就可以了解bean加载的过程了.前面已经提到过,单 例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里也只是尝试加载,首先尝试从缓存中加载,然后再尝试从singletonFactories中加载. 因为在创建单例bean的时候会存在依赖注人的情况,而在创建依赖的时候为了避免循环依赖, Sprin

CI源码分析(四)—DB查询缓存

(一) 使用 创建缓存文件目录, 保证目录可写 在config/database.php中配置缓存路径 : db['default′]['cachedir′]='path/to/cache/dir′启用||关闭缓存:可以使用代码控制,只对部分代码生效–this->db->cache_on(), this?>db?>cacheoff();也可以通过配置文件配置,对所有代码生效-db['default']['cache_on'] = true; 建议在配置文件中关闭,然后在使用的地方自

Android 开源项目源码分析第一期正式发布

由 Trinea 发起.几十名 Android 开发者参与的Android 开源项目源码分析第一期正式发布. 从简介.总体设计.流程图.详细设计全方面分析开源库源码,第一期包括 10 个著名开源库及 5 个公共技术点的全面介绍. 分析文档 作者 Volley 源码解析 grumoon Universal Image Loader 源码分析 huxian99 Dagger 源码解析 扔物线 EventBus 源码解析 Trinea xUtils 源码解析 Caij ViewPagerindicat

Android应用Activity、Dialog、PopWindow、Toast窗口添加机制及源码分析

1  背景 之所以写这一篇博客的原因是因为之前有写过一篇<Android应用setContentView与LayoutInflater加载解析机制源码分析>, 然后有人在文章下面评论和微博私信中问我关于Android应用Activity.Dialog.PopWindow加载显示机制是咋回事,所以我就写一 篇文章来分析分析吧(本文以Android5.1.1 (API 22)源码为基础分析),以便大家在应用层开发时不再迷糊. PS一句:不仅有人微博私信我这个问题,还有人问博客插图这些是用啥画的,这

ASimpleCache(ACache)源码分析(android轻量级开源缓存框架)

转载请注明出处:http://blog.csdn.net/zhoubin1992/article/details/46379055 ASimpleCache框架源码链接 https://github.com/yangfuhai/ASimpleCache 杨神作品,大家最熟悉他的应该是afinal框架吧 官方介绍 ASimpleCache 是一个为android制定的 轻量级的 开源缓存框架.轻量到只有一个java文件(由十几个类精简而来). 1.它可以缓存什么东西? 普通的字符串.JsonObj

jquery2源码分析系列目录

学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中可以学到很多w3c新的标准( 如html5,css3,ECMAScript).原文地址是:http://www.cnblogs.com/aaronjs/p/3279314.html 关于1.x.x版的jquery源码分析系列,本博客也转载了一个地址http://www.cnblogs.com/jav