Android | 用Kotlin仿写Glide图片加载框架

2021-07-06  本文已影响0人  太白余风

一、前言

Glide是一个极其著名的Android的快速高效的开源媒体管理和图像加载框架,它将媒体解码、内存和磁盘缓存以及资源池打包成一个简单易用的界面。源码地址 https://github.com/bumptech/glide

Glide的设计十分巧妙,古人云:“纸上得来终觉浅,绝知此事要躬行”。不动手写只看代码进行分析,印象始终不深刻,不能更进一步了解Glide框架设计精髓.

所以打算使用Kotlin语言进行仿写,GitHub地址https://github.com/jiangpana/KGlide

二、关键类&作用

核心类
Registry
ModelLoader 相关
缓存相关

三、流程&原理

处理生命周期&封装参数

RequestManagerRetriever#supportFragmentGet方法中 , 构建SupportRequestManagerFragment然后设置RequestManager,并添加到activity中用于监听生命周期

 private fun supportFragmentGet(
        context: Context,
        fm: FragmentManager,
        parentHint: Fragment?,
        isParentVisible: Boolean
    ): RequestManager {
       //构建SupportRequestManagerFragment用于监听生命周期
        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()
}
构建请求

SingleRequest#obtain方法中构建request , 泛型R 默认为Drawable. 如果是asBitmap()则为Bitmap

  //callbackExecutor为在主线程执行的Executor
  //target 默认为 DrawableImageViewTarget
  //model 为 string ,图片请求地址
  //transcodeClass 为 Drawable.class
  //overrideWidth 解码时候需要的图片宽
  //overrideHeight 解码时候需要的图片高
  //priority 加载的优先级
    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,如果当前任务还在执行则添加回调
        val current: EngineJob<*>? = jobs.get(key, onlyRetrieveFromCache)
        current?.let {
            current.addCallback(cb, callbackExecutor)
            return LoadStatus(current, cb)
        }
        //构建engineJob
        val engineJob = engineJobFactory!!.build<R>(
            key,
            isMemoryCacheable,
            useUnlimitedSourceExecutorPool,
            useAnimationPool,
            onlyRetrieveFromCache
        )
        //构建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)
    }
从源获取数据 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;
        //请求成功则返回数据
        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 方式减少内存占用然后

  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方式优化bitmap占用内存大小
        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
    }
转码

bitmap 转为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 , 先调用 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)
                        //移除监听,防止内存泄漏
                        removeCallback(cb)
                    }
                }
            }
        }

    }
    
    // callCallbackOnResourceReady调用
      cb.onResourceReady(engineResource!!, dataSource)

SingleRequest实现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
            )
        ) {
            //构建用于缓存的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()
    }

四、总结

本文首先解读各主要类的功能以及方法执行流程,然后对框架进行解读.

用kotlin语言精简代码进行仿写, 希望能更加理解glide源码设计精髓,但glide的源码所能获取的营养远不止如此.

每看一遍又会有不一样的理解, 让人受益匪浅,在此对Glide开源工作者表示崇高的敬意和感谢。

上一篇下一篇

猜你喜欢

热点阅读