先慢慢来看.....这个Glide源码分析基于3.7.0的下来就开始看吧::::
一:概述
Glide是一款由Bump Technologies开发的图片加载框架,使得我们可以在Android平台上以极度简单的方式加载和展示图片。
一行代码
Glide.with(this).load(url).into(imageView);
图片加载用到了Glide,这是一个专门用来加载图片的第三方库,他操作简单,功能强大。支持加载各种各样的图片加载,包括网络图片,本地图片,二进制流还有url对象等;支持优先级处理;和Activity,Fragment的生命周期一致;默认使用urlConnection获取数据,但他还可以配合okhttp,volley使用;还有就是glide的内存友好。
With()
首先,调用Glide.with()方法用于创建一个加载图片的实例。with()方法可以接收Context、Activity或者Fragment类型的参数。也就是说我们选择的范围非常广,不管是在Activity还是Fragment中调用with()方法,都可以直接传this。那如果调用的地方既不在Activity中也不在Fragment中呢?也没关系,我们可以获取当前应用程序的ApplicationContext,传入到with()方法当中。注意with()方法中传入的实例会决定Glide加载图片的生命周期,如果传入的是Activity或者Fragment的实例,那么当这个Activity或Fragment被销毁的时候,图片加载也会停止。如果传入的是ApplicationContext,那么只有当应用程序被杀掉的时候,图片加载才会停止。
Load()
这个方法用于指定待加载的图片资源。Glide支持加载各种各样的图片资源,包括网络图片、本地图片、应用资源、二进制流、Uri对象等等。因此load()方法也有很多个方法重载,除了我们刚才使用的加载一个字符串网址之外,你还可以这样使用load()方法:
加载本地图片
File file = getImagePath();
Glide.with(this).load(file).into(imageView);
加载应用资源
int resource = R.drawable.image;
Glide.with(this).load(resource).into(imageView);
加载二进制流
byte[] image = getImageBytes();
Glide.with(this).load(image).into(imageView);
加载Uri对象
Uri imageUri = getImageUri();
Glide.with(this).load(imageUri).into(imageView);
into()方法,这个方法就很简单了,我们希望让图片显示在哪个ImageView上,把这个ImageView的实例传进去就可以了。
二:占位图
观察刚才加载网络图片的效果,你会发现,点击了Load Image按钮之后,要稍微等一会图片才会显示出来。这其实很容易理解,因为从网络上下载图片本来就是需要时间的。那么我们有没有办法再优化一下用户体验呢?当然可以,Glide提供了各种各样非常丰富的API支持,其中就包括了占位图功能。
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.into(imageView);
不过如果你现在重新运行一下代码并点击Load Image,很可能是根本看不到占位图效果的。因为Glide有非常强大的缓存机制,我们刚才加载那张必应美图的时候Glide自动就已经将它缓存下来了,下次加载的时候将会直接从缓存中读取,不会再去网络下载了,因而加载的速度非常快,所以占位图可能根本来不及显示。
优化:
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)占位图
.diskCacheStrategy(DiskCacheStrategy.NONE)解决站位图缓存
.into(imageView);
这里串接了一个diskCacheStrategy()方法,并传入DiskCacheStrategy.NONE参数,这样就可以禁用掉Glide的缓存功能。(strategy战略)
2.1异常占位图
当然,这只是占位图的一种,除了这种加载占位图之外,还有一种异常占位图。异常占位图就是指,如果因为某些异常情况导致图片加载失败,比如说手机网络信号不好,这个时候就显示这张异常占位图。
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)占位图
.error(R.drawable.error)异常占位图
.diskCacheStrategy(DiskCacheStrategy.NONE)不缓存图片
.into(imageView);
三:指定图片格式(GIF)
我们还需要再了解一下Glide另外一个强大的功能,那就是Glide是支持加载GIF图片的。这一点确实非常牛逼,因为相比之下Jake Warton曾经明确表示过,Picasso是不会支持3.1加载GIF图片的。
而使用Glide加载GIF图并不需要编写什么额外的代码,Glide内部会自动判断图片格式。比如这是一张GIF图片的URL地址:
http://p1.pstatp.com/large/166200019850062839d3
也就是说,不管我们传入的是一张普通图片,还是一张GIF图片,Glide都会自动进行判断,并且可以正确地把它解析并展示出来。
3.2指定图片格式(强制静态)
但是如果我想指定图片的格式该怎么办呢?就比如说,我希望加载的这张图必须是一张静态图片,我不需要Glide自动帮我判断它到底是静图还是GIF图。
想实现这个功能仍然非常简单,我们只需要再串接一个新的方法就可以了,如下所示:
自定义格式代码:
Glide.with(this)
.load(url)
.asBitmap()只允许加载静态图片
.placeholder(R.drawable.loading)占位图方法
.error(R.drawable.error)异常占位图
.diskCacheStrategy(DiskCacheStrategy.NONE)不缓存
.into(imageView);
可以看到,这里在load()方法的后面加入了一个asBitmap()方法,这个方法的意思就是说这里只允许加载静态图片,不需要Glide去帮我们自动进行图片格式的判断了
注意:由于调用了asBitmap()方法,现在GIF图就无法正常播放了,而是会在界面上显示第一帧的图片。
3.3强制加载动态图片
那么类似地,既然我们能强制指定加载静态图片,就也能强制指定加载动态图片。比如说我们想要实现必须加载动态图片的功能,就可以这样写:
Glide.with(this)
.load(url)
.asGif()强制动态图片
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.into(imageView);
这里调用了asGif()方法替代了asBitmap()方法
注意:如果使用强制动态,却传入静1态图片,会走异常图片的方法error();
四:指定图片大小
4.1概述:
实际上,使用Glide在绝大多数情况下我们都是不需要指定图片大小的。
在学习本节内容之前,你可能还需要先了解一个概念,就是我们平时在加载图片的时候很容易会造成内存浪费。什么叫内存浪费呢?比如说一张图片的尺寸是1000*1000像素,但是我们界面上的ImageView可能只有200*200像素,这个时候如果你不对图片进行任何压缩就直接读取到内存中,这就属于内存浪费了,因为程序中根本就用不到这么高像素的图片。
而使用Glide,我们就完全不用担心图片内存浪费,甚至是内存溢出的问题。因为Glide从来都不会直接将图片的完整尺寸全部加载到内存中,而是用多少加载多少。Glide会自动判断ImageView的大小,然后只将这么大的图片像素加载到内存当中,帮助我们节省内存开支。
也正是因为Glide是如此的智能,所以刚才在开始的时候我就说了,在绝大多数情况下我们都是不需要指定图片大小的,因为Glide会自动根据ImageView的大小来决定图片的大小。
必须给图片指定一个固定的大小,Glide仍然是支持这个功能的。
4.2代码:
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading)
.error(R.drawable.error)
.diskCacheStrategy(DiskCacheStrategy.NONE)
.override(100, 100)指定图片的大小
.into(imageView);
仍然非常简单,这里使用override()方法指定了一个图片的尺寸,也就是说,Glide现在只会将图片加载成100*100像素的尺寸,而不会管你的ImageView的大小是多少了。
加载圆形图片
Glide.with(this).load(URL).transform(new
CircleTransform(context)).into(imageView);
4.3缩略图支持
为了减少在同一个view组件里同时加载多张图片的时间,可以调用Glide.with(context).load(“图片路径“).thumbnail(“缩略比例“).into(“view组件“)方法加载一个缩略图,还可以控制thumbnail()中的参数的大小,以控制显示不同比例大小的缩略图
跳过第一级缓存:skipMemoryCache(ture)
自定义第二级缓存:diskCacheStrategy(DiskCacheStrategy.NONE)
DiskCacheStrategy.NONE 什么都不缓存,就像刚讨论的那样
· DiskCacheStrategy.SOURCE 仅仅只缓存原来的全分辨率的图像。在我们上面的例子中,将会只有一个 1000x1000 像素的图片
· DiskCacheStrategy.RESULT 仅仅缓存最终的图像,即,降低分辨率后的(或者是转换后的)
· DiskCacheStrategy.ALL 缓存所有版本的图像(默认行为)
五:源码分析
1.Glide的构造
1.1概述
Glide是通过GlideBuilder的createGlide方法生成的
如何从GlideBuilder转换为Glide对象?
在Glide里面new GlideBuilder Gldie 对象都是调用createGlide(Engine engine, MemoryCache memoryCache, BitmapPool bitmapPool, Context context, DecodeFormat decodeFormat)
Glide 构造参数是主要有五个,有createGlide生成的
Engine 引擎类
MemoryCache 内存缓存
BitmapPool 图片池
Context 上下文对象
DecodeFormat 图片格式
1.2参数解释
1.2.1MemoryCache 内存缓存LruResourceCache
在createBuilder方法中,MemoryCache的对象是由子类LruResourceCache new的,构造方法参数是参数是int型的,用MemorySizeCalculator(构造方法参数是上下文对象)的对象调方法getMemoryCaseSize(),方法里返回的是变量int型的memoryCaseSize,给memoryCaseSize赋值的是变量targetMemoryCaseSize,所以说内存缓存用的是targetMemoryCaseSize(官方解释的是屏幕的宽*高*4*2),并且targetMemoryCaseSize的由来也是有一定的选择,判断targetMemoryCaseSize+targetPoolSize(与图片池有关)<=maxSizez(等于getMaxSize)成立就是targeMemoryCaseSize赋值给memoryCaseSize,否则就是等比缩小(缩小的规则:引入一个int变量part=maxSize/4+2结果四舍五入 然后targetMemoryCaseSize=part*2)。这就是MemoryCache值的由来
解释一个方法getMaxSize:
One:有一个int变量memoryClassBytes(每个进程可用的最大内存)
Two:一个布尔变量isLowMemoryDevice(判断是否是低配手机)
Three:return的是三目运算符“判断是否是低配手机”(最大内存*0.33或者是*0.4)
最大内存:如果是低配手机,就每个进程可用的最大内存乘以0.33,否则就每个进程可用的最大内存乘以0.4
1.2.2BitmaPool图片池LruBitmapPool BitmapPoolAdapter
在GlideBuilder里。BitmapPool对象的赋值是set方法,在createBuilder里,bitmapPool须要判断,判断版本号是不是大于等于11,如果为true则new LruBitmapPool,参数是size,赋值是由MemoryCaseSizeCalculate类的方法getBitmapPoolSize(),返回的返回值是int变量bitmapPoolSize,给bitmapPoolSize赋值的变量是targetPoolSize(官方解释是屏幕的宽*高*4*4),并且targetPoolSize的由来也有一定的选择,判断targetMemoryCaseSize+targetPoolSize(与图片池有关)<=maxSizez(等于getMaxSize)成立就是直接targetPoolSize赋值给bitmapPoolSize,否则等比例缩小,(缩小的规则:引入一个int变量part=maxSize/4+2结果四舍五入 然后targetMemoryCaseSize=part*4).如果当初的版本判断小于11,就new BitmapPoolAdapter,无参。但是实现BitmapPool
这就是BitmapPoolSize的由来
1.2.3Engine引擎类
直接new 但是要四个参数(MemoryCache<内存缓存>memoryCache、DiskCache.Factory<本地缓存>diskCacheFactory、ExecutorService<处理源资源的线程池>sourceService、ExecutorService<处理本地缓存的线程池>diskCacheService)
参数解释:
One:MemoryCache和上述的一样内存缓存
Two:DiskCache.Factory本地缓存——InternalCacheDiskCacheFactory(继承DiskLruCacheFactory)
在InternalCacheDiskCacheFactory的构造方法有三个,用第一个,传参Context,在内部调用的是第三个,三个参数的(一个context,一个String,一个int)int DEFAULT_DISK_CACHE_SIZE = 250 * 1024 * 1024;默认是250MB
Three:ExecutorServic——diskCacheService处理本地缓存的线程池——FifoPriorityThreadPoolExecutor(继承ThreadPoolExecutor)传入的参数是1.线程池的核心线程数量是1.
Four:ExecutorServic——sourceService处理本地缓存的线程池——FifoPriorityThreadPoolExecutor(继承ThreadPoolExecutor)线程池的核心线程数量等于获得可用的处理器个数。处理器个数的个数请求:final int cores = Math.max(1, Runtime.getRuntime().availableProcessors());
1.2.4DecodeFormat图片格式
public static final DecodeFormat DEFAULT = PREFER_RGB_565;
默认是RGB-565
2.With()
可传对象(Activity、FragmentActivity、Context、Fragment)
With有很多重载方法,但是返回的是RequestManager
他调用了RequestManagerRetriever的方法获取了一个RequestManagerRetriever然后在调用get方法获取了一个RequestManager,到这里我们大概基本明白了with方法做的是将当前引用(Activity,Fragment)传递进去并获取一个和当前引用绑定的RequestManager,到这里大家应该能明白了为什么说Glide绑定了界面的生命周期了吧
1.1返回的是RequestManager
在Glide类里面,With有四个重载方法 参数分别是(Conteext,Activity,FragmentActivity,android.app.Fragment)。在with方法里有个RequestManagerRetriever类的get方法返回自己的对象,就是单例模式。对象的get方法也是有四个重载,参数和with一样,返回RequesManager。
分析RequestManager的get方法(和With方法一样四个重载)
1.1.1context
注意:就算你传入的是Context,也会根据context实际的类型,走不同的分支,如果你是在子线程调用with方法,或者传入的Context是Application的话,请求是跟你的Application的生命周期同步。那个方法是getApplicationManager
Util.isOnMainThread()。判断是否是在主线程
解析:这里会有判断,看你是否在主线程中和是否是Application类型。如果不是的话在跟你返回强转不同的context作为参数(有一个是ContextWrapper),调用get方法。否则会直接返回getApplicationManager(context)。
1.1.2FragmentActivity
最后会创建一个无界面的Fragment,即SupportRequestManagerFragment ,让请求和你的activity的生命周期同步
用FragmentActivity的对象调用getSupportFragmentManager()方法,得到FragmentManager对象,return的是supportFragmentGet(FragmentActivity,FragmentManager)(即SupportRequestManagerFragment ,让请求和你的activity的生命周期同步)
1.1.3Fragment
如果在子线程直接返回getApplicationContext。
否则:用Fragment对象调用getChildFragmentManager方法,返回的是FragmentManager。supportFragmentGet(fragment.getActivity(),FragmentManager)。
1.1.4Activity
如果咋子线程直接返回getApplicationContext
否则:用Activity对象调用getFragmentManager方法,返回的是FragmentManager。
fragmentGet(Activity,FragmentManager)
1.1.5android.app.Fragment
如果在子线程直接返回getApplicationContext。
否则:用Fragment对象调用getChildFragmentManager方法,返回的是FragmentManager。supportFragmentGet(fragment.getActivity(),FragmentManager)。
总结:With方法是将Glide的生命周期与Activity的生命周期同步
感悟With方法的(绑定生命周期)
在With方法中你会见到一个类RequestManagerRetriever,然后是get单例到自己的对象,在对象的get方法,里面有很多重载get,有不同的参数,(Context、FragmentActivity、Fragment、Activity、app.Fragment).其实参数就是两种,既是Application类型的和不是Application的。我们先说Application类型的参数是如何绑定生命周期的。会在参数为context的重载中调用getApplicationManager(context)方法来获取一个RequestManager的对象。其实这是最简单的一种的方式,因为Application的生命周期是全局的,既应用程序的生命周期,所以Glide不用做什么特殊的处理。直接就和应用程序生命周期同步,程序停止Glide也就直接停止。接下来是非Application的类型,无论是FragmentActivity、Activity、Fragment、app.Fragment的那一种,最终流程都是一样的,向activity中添加一个隐藏的Fragment(getSupportRequestManagerFragment(supperFragmentGet方法,FragemntActivty、Fragment)、getRequestManagerFragment(app.Fragemnt,Activty在FragmentGet中)方法)。那为什么要添加一个隐藏的Fragment那?毫无疑问,因为Glide要加载生命周期《答案是:如果你在打开一个Activity时候,开始加载图片,图片还没加载出来,你就直接关闭 这个Activity,那么图片还应该继续加载吗,当然不应该,加载给谁看那。这时候Glide需要知道Activity的生命周期,更智能的加载或停止。所以Glide就使用啦添加隐藏Fragment的方法来解决,因为Fragment的和Activity的生命周是同步的,但Activity的生命周期停止啦,Fragment是可以监听的,Glide也就可以停止加载啦》。
一句话总结:With就是为了让Glide的生命周期和Actiivty的生命周期同步。
3. Load():
目的:返回DrawableTypeRequest(继承DrawableRequestBuilder《可以把这个类理解为链式调用的类》辅助类)因为With方法返回的是RequestManager类,所以很容易就知道,load是这个类方法,因为load里的参数很多,所以毫无疑问重载方法也很多,但是每个重载方法里面都是返回的DrawableTypeRequest,并且调用不同的from**,在调用load方法。就拿String来举例把,fromString(),在里面有调用loadGeneric方法,传入String.Class。在里面分别调用Glide.buildStreamModelLoader()方法和Glide.buildFileDescriptorModelLoader()方法来获得ModelLoader对象,ModelLoader对象是用来加载图片的,load方法会传入不同的的参数类型,所以也会得到不同的ModelLoader对象。
这个RequestManager.class这个类主要用来处理低内存的时候处理;管理请求,比如请求的开始、暂停、结束。还有通过加载方法获得一个DrawableTypeRequest对象。
4.into():
这个方法真的是,说简单也简单,说难也难...
主要的网络操作也在这个类里面,简单就是一个target承接Image,简单理解是这样。
深层理解的话请看goulin大神的博客:http://blog.csdn.net/guolin_blog/article/details/53939176/
这块我就简单理解了一下,没有深层研究,看到大半夜4点都没研究太透,谢谢各位,喜欢的话可以订阅,还会更新一些其他的...