三方框架原理分析总结
Glide的缓存机制
配上代码来说,统一的key,依据key去找;代码在Engine中
key是包括url,width,height等一系列参数计算出来的Key对象
- Hashmap<Key, ResourceWeakReference>,弱引用缓存,从弱引用中获取,直接内存缓存。系统内存不足时,允许gc
- Lrucache<Key, Resource>,Lrucache缓存,超过缓存额度时,将最久未使用的内存删除。使用内存缓存,强引用
3.Hashmap<Key, EngineJob>,从网络中获取图片,同时如果多个相同的图片请求触发,避免相同的网络重复请求 - 创建新的LoadStatus(cb, EngineJob),从网络中获取图片
缓存主要分为三个 activiteResources(弱引用活动缓存),MemoryCache(Lru内存缓存) DiskCache(磁盘缓存) 还有一个http网络缓存, 不多做介绍
Glide通过创建一个空的Fragment来管理生命周期 如果Activity被关闭把活动缓存的添加到LRU内存缓存中。
首先弱引用缓存代表的是当前正在使用的缓存, 请求网络图片之后 会分为两个步骤, 1.进入磁盘缓存,2进入弱引用缓存。 在弱引用缓存中使用一个int型计数器来维护当前资源是否被使用,如果计数器为0说明当前资源已经不被使用了,则进入Lru缓存,Lru缓存是glide使用LinkedHashMap来维护的,如果LRU满了则删除最长时间未使用的。
如果一个资源加载的时候在弱引用缓存中找不到,就会到LRU缓存中查找,如果没找到就去磁盘缓存找,不管在LRU缓存中找到还是在磁盘中找到, 都会把命中的缓存放在弱引用缓存中。
当图片资源的引用计数大于0时表示图片正在使用中,存储在弱引用活动缓存集合中,
当图片引用资源计数器为0时表示当前图片不再使用,就从弱引用活动缓存中删除,保存到LruCache中
这样就实现了使用中的图片保存在弱引用缓存HashMap集合中,不再使用的图片保存在LruCache内存缓存中
图片资源的引用计数器什么时候加1呢?
- 命中LruCache缓存时,会先从LruCache中删除再添加到活动缓存中,资源引用计数会+1
- 命中活动缓存时,图片资源引用计数会加1
3.若内存缓存没有命中,开启异步线程从硬盘缓存加载或者网络加载成功后,保存到活动缓存中,同时图片资源引用计数加1
图片资源引用计数什么时候减1呢?
在onStop和onDestroy时会调用release将图片引用计数减1,当引用计数为0时,从活动缓存中删除,即图片不再使用就添加到LruCache内存缓存中
glide设计的三级缓存模型, 其中内存缓存减轻了磁盘缓存的压力,避免io操作,而弱引用缓存则减轻了 LRU内存缓存的压力,避免LRU过满导致频繁gc,并且提高了查找效率,因为LRU内存缓存是基于LinkedHashMap实现的,HashMap的存储效率比LinkedHashMap高。
另外再说下 glide4 五种缓存策略 data source all no automicdata则是缓存住原始图片资源, source则是缓存住变换后的资源(glide 会针对imageview的大小对图片预先变换,避免小imageview加载大图片浪费内存)all 就是两种都缓存, no就是不缓存 ,automiic 则比较特殊,会缓存data 根据图片的编码 自行判断是否缓存resource
ButterKnife原理:
使用ButterKnife是为了简化代码 通过注解代替findViewById
ButterKnife是在编译时注解 不会影响APP的效率和性能
ButterKnife的实现原理是通过注解处理器真对每个Activity生成一个相对应的类 把原本需要findViewById和点击事件利用注解生成代码,在Activity的onCreate中调用了 Butterknife.bind(this),将当前Activity对象传递过去,内部封装了通过activity实例调用findviewById的逻辑,由于没有使用反射,所以注解修饰的View不能是private的。
Rxjava实现原理:
Rxjava响应式编程是一种基于异步数据流概念的编程模式。数据流就像一条河:它可以被观测,被过滤,被操作,或者为新的消费者与另外一条流合并为一条新的流。RxJava可以用非常简洁的代码逻辑来解决复杂的问题;随着业务逻辑的越来越复杂,它依然能够保持简洁。
其使用观察者模式,内部通过大量的将旧对象包装成新对象来扩展事件发送和事件接收处理能力
subscribleOn:事件订阅所在的线程,即被观察者事件生产所在的线程
observerOn:事件消费所在的线程,观察者观察到被观察者发送的事件时,事件消费所在的线程,是onNext,onComplete事件执行所在的线程
RX java线程切换的逻辑:
(1)创建执行的Scheduler,具体执行是它的多个子类比如IOScheduler,执行任务时schedule内部是通过线程池执行woker实现。
(2)发送事件时封装worker任务,然后通过观察者设置的Scheduler,执行线程池中的worker任务如onNext,onCompilete,onNext任务是存储在队列中的,从队列中不断循环执行onNext任务,当队列为空代表任务执行完成,然后调用onComplete
由于事件是从队列中不断读取消息执行的,在异步的情况下,当上游事件发送的速度大于下游处理事件的速度时,称之为背压,此时会生成一个无限大的缓冲池去缓存事件。在RxJava中,Flowable是支持背压策略的。
总共分为4种策略
1.BackpressureStrategy.ERROR:若上游发送事件速度超出下游处理事件能力,且事件缓存池已满,则抛出异常
2.BackpressureStrategy.BUFFER:若上游发送事件速度超出下游处理能力,则把事件存储起来等待下游处理
3.BackpressureStrategy.DROP:若上游发送事件速度超出下游处理能力,事件缓存池满了后将之后发送的事件丢弃
4.BackpressureStrategy.LATEST:若上有发送时间速度超出下游处理能力,则只存储最新的128个事件
Kotlin 使用栈帧管理要运行哪个函数以及所有局部变量。挂起协程时,系统会复制并保存当前的栈帧以供稍后使用。恢复时,会将栈帧从其保存位置复制回来,然后函数再次开始运行。即使代码可能看起来像普通的顺序阻塞请求,协程也能确保网络请求避免阻塞主线程。
Retrofit并不是网络请求框架,严格说只是对网络请求的一种封装,我们只需要定义一个接口类,在请求方法上加上相应的注解,甚至都不需要实现,就可以实现网络请求。
1、静态代理和动态代理
提到这个是因为,retrofit的精髓就在于内部的动态代理模式。
Java中的静态代理要求代理类(ProxySubject)和委托类(RealSubject)都实现同一个接口(Subject)。静态代理中代理类在编译期就已经确定,而动态代理则是JVM运行时动态生成,静态代理的效率相对动态代理来说相对高一些,但是静态代理代码冗余大,一旦需要修改接口,代理类和委托类都需要修改。
动态代理的两种实现方式:
JDK自带的java.lang.reflect.Proxy,只能代理接口类
CGLib,生产子类来实现方法增强,无需实现接口
Retrofit使用的就是JDK的动态代理。
2、简单介绍下retrofit实现原理
首先,通过Builder创建Retrofit对象,在create方法中,通过JDK动态代理的方式,生成实现类,在调用接口方法时,会触发InvocationHandler的invoke方法,将接口的空方法转换成ServiceMethod, 然后生成okhttp请求,通过callAdapterFactory找到对应的执行器,比如RxJava2CallAdapterFactory,最后通过ConverterFactory将返回数据解析成JavaBena,使用者只需要关心请求参数,内部实现由retrofit封装完成,底层请求还是基于okhttp实现的。
3、Retrofit关键类
Retrofit源码非常简练,关键类就看类成员变量,就只有下面7个。
public final class Retrofit {
private final Map<Method, ServiceMethod<?, ?>> serviceMethodCache = new ConcurrentHashMap<>();
final okhttp3.Call.Factory callFactory;
final HttpUrl baseUrl;
final List<Converter.Factory> converterFactories;
final List<CallAdapter.Factory> callAdapterFactories;
final @Nullable Executor callbackExecutor;
final boolean validateEagerly;
....
}
ServiceMethod
核心处理类,解析接口中定义的请求方法参数和注解,通过toCall方法将其转换成okhttp的call对象,有了Call对象,就可以发送请求了。
callFactory
字面意思就是生产Call的工厂,这里的Call是okhttp包下面的Call,CallFactory默认就是OkHttpClient
HttpUrl
就是将创建retrofit对象时传入的baseUrl转换成对象,进行格式校验等。
converterFactories
数据解析器Converter,将response通过converterFactory转换成对应的JavaBean数据形式,常见解析器有,GsonConverterFactory,FastJsonConverterFactory,当然也有xml的。
callAdapterFactories
通过calladapter将原始Call进行封装,找到对应的执行器。如rxjavaCallFactory对应的Observable,转换形式Call<T> --> Observable<T>
callbackExecutor
主线程执行器,返回结果在UI线程执行
validateEagerly
是否立即校验所以接口方法,也就是将接口类中的方法全部转换成ServiceMethod,默认为false
网友评论