一椒功、前言
Glide是一個極其著名的Android的快速高效的開源媒體管理和圖像加載框架彭沼,它將媒體解碼廉侧、內(nèi)存和磁盤緩存以及資源池打包成一個簡單易用的界面肖方。源碼地址 https://github.com/bumptech/glide
Glide的設計十分巧妙,古人云:“紙上得來終覺淺祥绞,絕知此事要躬行”非洲。不動手寫只看代碼進行分析,印象始終不深刻,不能更進一步了解Glide框架設計精髓.
所以打算使用Kotlin語言進行仿寫,GitHub地址https://github.com/jiangpana/KGlide
二、關鍵類&作用
核心類
- KGlide : 框架的入口類
- Target: 圖片的請求的目標類,負責顯示圖片
- BaseRequestOptions : 負責請求的相關基礎配置
- Options : 負責請求過程配置的封裝,用map實現(xiàn)
- Lifecycle: 基于觀察者設計模式,負責管理LifecycleListener,實現(xiàn)類有ActivityFragmentLifecycle,ApplicationLifecycle
- FactoryPools : 對象池,復用對象
- GlideExecutor : 線程池,主要有SourceExecutor ,DiskCacheExecutor ,AnimationExecutor ,只有SourceExecutor可以用來網(wǎng)絡請求
- RequestManager: 負責管理圖片請求,實現(xiàn)LifecycleListener,在activity生命周期回調(diào)做相關處理
- SingleRequest : 負責圖片請求成功失敗取消等相關操作
- Engine : 負責啟動啟動圖片加載任務和內(nèi)存緩存
- EngineJob : 負責啟動DecodeJob ,并處理資源成功失敗相關回調(diào)
- DecodeJob : 負責從源獲取數(shù)據(jù),解碼,轉(zhuǎn)碼,變換,磁盤緩存相關
- DataCacheGenerator : 負則從磁盤緩存獲取未解碼的data數(shù)據(jù)
- ResourceCacheGenerator : 負則從磁盤緩存獲取已解碼可直接使用的resource數(shù)據(jù)
- SourceGenerator : 負責用來獲取解碼的源數(shù)據(jù)
Registry
- Registry:注冊表. 內(nèi)部主要有modelLoaderRegistry,decoderRegistry,resourceEncoderRegistry,encoderRegistry等相關注冊表,用來解碼轉(zhuǎn)碼編碼等相關功能
- ModelLoaderRegistry : 模型加載表,用于通過model獲取data
- ResourceDecoderRegistry : 資源解碼表,用于將獲取到的data 解碼成Resource
- TranscoderRegistry: 轉(zhuǎn)碼表,用于將Resource轉(zhuǎn)碼,比如bitmap -> bitmapDrawable
- DataRewinderRegistry: 數(shù)據(jù)回卷表,用于回卷流,便于inputStream重讀
- EncoderRegistry: 編碼表,用于data磁盤緩存相關
- ResourceEncoderRegistry: 資源編碼表,用于Resource磁盤緩存
- DecodeHelper : 負責提供解碼編碼相關操作幫助類
ModelLoader 相關
- ModelLoader : 接口,用于通過model獲取data
- ModelLoaderFactory : 用于生產(chǎn)modelLoader
- FileLoader : 處理model為file情況
- StringLoader : 處理model為string的情況
緩存相關
- Key : 負責磁盤緩存的key ,實現(xiàn)類有DataCacheKey,ResourceCacheKey 等
- DiskLruCache : 負責磁盤緩存
- LruArrayPool : 負責數(shù)組的緩存,防止內(nèi)存抖動
- LruBitmapPool : 用來緩存bitmap,復用bitmap ,防止內(nèi)存抖動
- LruResourceCache: 用來緩存resource ,大小根據(jù)cpu核心數(shù)計算
- BitmapEncoder : 負責將bitmap編碼成file
- StreamEncoder :負責將流編碼成file
- StreamBitmapDecoder : 將流解碼成bitmap
三蜕径、流程&原理
處理生命周期&封裝參數(shù)
RequestManagerRetriever#supportFragmentGet方法中 , 構(gòu)建SupportRequestManagerFragment然后設置RequestManager,并添加到activity中用于監(jiān)聽生命周期
private fun supportFragmentGet(
context: Context,
fm: FragmentManager,
parentHint: Fragment?,
isParentVisible: Boolean
): RequestManager {
//構(gòu)建SupportRequestManagerFragment用于監(jiān)聽生命周期
val current = getSupportRequestManagerFragment(fm, parentHint, isParentVisible);
var requestManager = current.getRequestManager()
if (requestManager == null) {
val glide = KGlide.get(context)
requestManager = factory.build(
glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context
)
//設置RequestManager
current.setRequestManager(requestManager)
}
return requestManager
}
BaseRequestOptions#apply中,應用其他BaseRequestOptions的配置
fun apply(o: BaseRequestOptions<*>): T {
val other = o
other.fields.apply {
if (isSet(SIZE_MULTIPLIER)) {
sizeMultiplier = other.sizeMultiplier
}
// ... 省略大量類似代碼
if (isSet(SIGNATURE)) {
signature = other.signature
}
if (isSet(ONLY_RETRIEVE_FROM_CACHE)) {
onlyRetrieveFromCache = other.onlyRetrieveFromCache
}
if (!isTransformationAllowed) {
transformations.clear()
fields.unSet(TRANSFORMATION)
fields.unSet(TRANSFORMATION_REQUIRED)
isTransformationRequired = false
isScaleOnlyOrNoTransform = true
}
}
fields = fields or other.fields
options.putAll(other.options)
return self()
}
Options 設置相關option
val CENTER_OUTSIDE: DownsampleStrategy = CenterOutside()
val FIT_CENTER: DownsampleStrategy = FitCenter()
val DEFAULT: DownsampleStrategy = CENTER_OUTSIDE
fun downsample(strategy: DownsampleStrategy): T {
return set(DownsampleStrategy.OPTION, strategy)
}
open operator fun <Y> set(option: Option<Y>, value: Y): T {
options[option] = value
return self()
}
構(gòu)建請求
SingleRequest#obtain方法中構(gòu)建request , 泛型R 默認為Drawable. 如果是asBitmap()則為Bitmap
//callbackExecutor為在主線程執(zhí)行的Executor
//target 默認為 DrawableImageViewTarget
//model 為 string ,圖片請求地址
//transcodeClass 為 Drawable.class
//overrideWidth 解碼時候需要的圖片寬
//overrideHeight 解碼時候需要的圖片高
//priority 加載的優(yōu)先級
fun <R> obtain(
context: Context,
glideContext: GlideContext,
model: Any,
transcodeClass: Class<R>,
requestOptions: BaseRequestOptions<*>,
overrideWidth: Int,
overrideHeight: Int,
priority: Priority,
target: Target<R>,
targetListener: RequestListener<R>? = null,
requestListeners: List<RequestListener<R>>? = null,
requestCoordinator: RequestCoordinator? = null,
engine: Engine,
animationFactory: TransitionFactory<R>? = null,
callbackExecutor: Executor
): SingleRequest<R> {
return SingleRequest()
...}
開始請求
Engine#waitForExistingOrStartNewJob 方法
private fun <R> waitForExistingOrStartNewJob(
glideContext: GlideContext,
model: Any,
signature: Key,
width: Int,
height: Int,
resourceClass: Class<*>,
transcodeClass: Class<R>,
priority: Priority,
diskCacheStrategy: DiskCacheStrategy,
transformations: Map<Class<*>, Transformation<*>>,
isTransformationRequired: Boolean,
isScaleOnlyOrNoTransform: Boolean,
options: Options,
isMemoryCacheable: Boolean,
useUnlimitedSourceExecutorPool: Boolean,
useAnimationPool: Boolean,
onlyRetrieveFromCache: Boolean,
cb: ResourceCallback,
callbackExecutor: Executor,
key: EngineKey,
startTime: Long
): LoadStatus? {
//先從緩存中獲取EngineJob,如果當前任務還在執(zhí)行則添加回調(diào)
val current: EngineJob<*>? = jobs.get(key, onlyRetrieveFromCache)
current?.let {
current.addCallback(cb, callbackExecutor)
return LoadStatus(current, cb)
}
//構(gòu)建engineJob
val engineJob = engineJobFactory!!.build<R>(
key,
isMemoryCacheable,
useUnlimitedSourceExecutorPool,
useAnimationPool,
onlyRetrieveFromCache
)
//構(gòu)建decodeJob
val decodeJob = decodeJobFactory!!.build(
glideContext,
model,
key,
signature,
width,
height,
resourceClass,
transcodeClass,
priority,
diskCacheStrategy,
transformations,
isTransformationRequired,
isScaleOnlyOrNoTransform,
onlyRetrieveFromCache,
options,
engineJob
)
//將engineJob緩存起來
jobs.put(key, engineJob)
engineJob.addCallback(cb, callbackExecutor)
//啟動decodeJob
engineJob.start(decodeJob)
return LoadStatus(engineJob, cb)
}
從源獲取數(shù)據(jù) data
SourceGenerator#startNext , 如果支持data緩存就處理緩存
override fun startNext(): Boolean {
printThis("startNext() " +Thread.currentThread().name)
//緩存data
if (dataToCache!=null){
val data: Any = dataToCache!!
dataToCache = null
cacheData(data)
}
//緩存成功,從DataCacheGenerator進行處理,如果處理成功返回true
if (sourceCacheGenerator != null && sourceCacheGenerator!!.startNext()) {
return true
}
sourceCacheGenerator = null
//從源獲取data
loadData = null
var started = false
//遍歷modelLoader獲取data
while (!started && hasNextModelLoader()) {
loadData = helper.getLoadData()[loadDataListIndex++]
loadData?.let {
if (helper.getDiskCacheStrategy().isDataCacheable(it.fetcher.getDataSource())
|| helper.hasLoadPath(it.fetcher.getDataClass())
) {
started = true
startNextLoad(it)
}
}
}
return started
}
HttpUrlFetcher#loadData ,通過HttpURLConnection 下載圖片
urlConnection.connectTimeout = DEFAULT_TIME_OUT
urlConnection.readTimeout = DEFAULT_TIME_OUT
urlConnection.useCaches = false
urlConnection.doInput = true
urlConnection.instanceFollowRedirects = false
urlConnection.connect()
stream = urlConnection.inputStream
//下載圖片過程中取消則返回null
if (isCancelled) {
return null
}
val statusCode = urlConnection.responseCode;
//請求成功則返回數(shù)據(jù)
if (isHttpOk(statusCode)) {
return getStreamForSuccessfulRequest(urlConnection)
}else if (isHttpRedirect(statusCode)){
println("$TAG statusCode =300 ")
//300 重定向
val redirectUrlString = urlConnection.getHeaderField("Location")
check(redirectUrlString.isNotBlank()){
"Received empty or null redirect url"
}
val redirectUrl = URL(url, redirectUrlString)
cleanup()
return loadDataWithRedirects(redirectUrl, redirects + 1, url, headers)
}else{
throw Exception(urlConnection.responseMessage + "statusCode =$statusCode")
}
對獲取到的data進行解碼
StreamBitmapDecoder#decode()
override fun decode(
source: InputStream,
width: Int,
height: Int,
options: Options
): Resource<Bitmap>? {
printThis(" decode -> width=$width , height=$height")
var callbacks: Downsampler.DecodeCallbacks?=null
return downsampler.decode(source ,width,height,options,callbacks)
}
Downsampler#decode() , 通過inTargetDensity和inDensity 方式減少內(nèi)存占用然后
fun decode(
ris: InputStream,
width: Int,
height: Int,
options: Options,
callbacks: DecodeCallbacks?
): Resource<Bitmap>? {
var bitmap: Bitmap
val options = BitmapFactory.Options()
ris.reset()
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(ris,null,options)
//獲取源圖片的寬高
options.inJustDecodeBounds = false;
val sourceHeight =options.outHeight
val sourceWidth =options.outWidth
printThis("sourceHeight =$sourceHeight sourceWidth =$sourceWidth")
//通過inTargetDensity,inDensity,inScaled方式優(yōu)化bitmap占用內(nèi)存大小
options.inTargetDensity=width
options.inDensity=sourceWidth
options.inScaled=true
//把流回到起點,重讀
ris.reset()
bitmap = BitmapFactory.decodeStream(ris,null,options)!!
printThis("bitmap size = ${Util.getBitmapByteSize(bitmap)}")
return BitmapResource.obtain(bitmap, bitmapPool);
}
變換
DecodeJob#onResourceDecoded
private fun <Z> onResourceDecoded(dataSource: DataSource, decoded: Resource<Z>?): Resource<Z>? {
/.../
var transformed = decoded
var appliedTransformation: Transformation<Z>? = null
if (dataSource != DataSource.RESOURCE_DISK_CACHE) {
//RESOURCE_DISK_CACHE ,不需要 transformed
appliedTransformation = decodeHelper.getTransformation(resourceSubClass as Class<Z>)
transformed =
appliedTransformation?.transform(glideContext!!, decoded, width, height) ?: decoded
}
/.../
return result
}
轉(zhuǎn)碼
bitmap 轉(zhuǎn)為bitmapDrawable
//BitmapDrawableTranscoder#transcode
override fun transcode(
toTranscode: Resource<Bitmap>?,
options: Options
): Resource<BitmapDrawable> {
printThis("transcode")
return LazyBitmapDrawableResource.obtain(resources, toTranscode)!!
}
//LazyBitmapDrawableResource#get
override fun get(): BitmapDrawable {
return BitmapDrawable(resources, bitmapResource.get())
}
顯示到imageview 中
EngineJob#CallResourceReady , 先調(diào)用 cb.onResourceReady(engineResource!!, dataSource)然后移除cb
inner class CallResourceReady(val cb: ResourceCallback) : Runnable {
override fun run() {
synchronized(cb.getLock()) {
synchronized(this@EngineJob) {
if (cbs.contains(cb)) {
// Acquire for this particular callback.
engineResource?.acquire()
callCallbackOnResourceReady(cb)
//移除監(jiān)聽,防止內(nèi)存泄漏
removeCallback(cb)
}
}
}
}
}
// callCallbackOnResourceReady調(diào)用
cb.onResourceReady(engineResource!!, dataSource)
SingleRequest實現(xiàn)ResourceCallback接口 onResourceReady方法中
override fun onResourceReady(resource: Resource<*>, dataSource: DataSource?) {
target.onResourceReady(resource.get() as R,null)
}
DrawableImageViewTarget#setResource
override fun setResource(resource: Drawable?) {
view.setImageDrawable(resource)
}
Resource緩存
將緩存策略設置為DiskCacheStrategy.RESOURCE
//如果dataSource 不等于RESOURCE_DISK_CACHE并且不等于MEMORY_CACHE則支持Resource緩存
override fun isResourceCacheable(
isFromAlternateCacheKey: Boolean,
dataSource: DataSource?,
encodeStrategy: EncodeStrategy?
): Boolean {
return dataSource!=DataSource.RESOURCE_DISK_CACHE && dataSource!=DataSource.MEMORY_CACHE
}
DecodeJob#onResourceDecoded
if (diskCacheStrategy.isResourceCacheable(
isFromAlternateCacheKey,
dataSource,
encodeStrategy
)
) {
//構(gòu)建用于緩存的key
val key: Key
when (encodeStrategy) {
EncodeStrategy.SOURCE -> key = DataCacheKey(currentSourceKey!!, signature!!);
EncodeStrategy.TRANSFORMED -> key = ResourceCacheKey(
decodeHelper.getArrayPool(),
currentSourceKey!!,
signature!!,
width,
height,
appliedTransformation,
resourceSubClass,
options!!
)
else -> throw IllegalArgumentException("Unknown strategy: $encodeStrategy")
}
val lockedResult = LockedResource.obtain(transformed)
//初始化deferredEncodeManager,用于待會緩存
deferredEncodeManager.init(key, encoder!!, lockedResult)
result = lockedResult
}
DecodeJob#notifyEncodeAndRelease
private fun notifyEncodeAndRelease(resource: Resource<R>, dataSource: DataSource) {
printThis("notifyEncodeAndRelease")
val result = resource
notifyComplete(result, dataSource)
stage = Stage.ENCODE
try {
if (deferredEncodeManager.hasResourceToEncode()) {
//緩存資源
deferredEncodeManager.encode(diskCacheProvider, options!!)
}
} catch (e: Exception) {
e.printStackTrace()
}
onEncodeComplete()
}
四两踏、總結(jié)
本文首先解讀各主要類的功能以及方法執(zhí)行流程,然后對框架進行解讀.
用kotlin語言精簡代碼進行仿寫, 希望能更加理解glide源碼設計精髓,但glide的源碼所能獲取的營養(yǎng)遠不止如此.
每看一遍又會有不一樣的理解, 讓人受益匪淺,在此對Glide開源工作者表示崇高的敬意和感謝。