Glide 系列五:InputStream 是怎么转成Bitmap 的?

472 阅读3分钟

Glide 系列五:InputStream 是怎么转成Bitmap 的?

本文概述:

  • 上接系列四文章,分析了Glide 框架是怎么将字节流转成Bitmap 的;在文章末尾简单罗列了分析过程中的相关问题;

回退至HttpUrlFetcher.loadData()

  • 从HttpUrlFetcher.loadDataWithRedirects () 回退至HttpUrlFetcher.loadData()

  • 关键代码:

     try {
         InputStream result = loadDataWithRedirects(glideUrl.toURL(), 0, null, glideUrl.getHeaders());
         //从这个地方就开始往回走了
         callback.onDataReady(result);
     } 
    
  • 从callback.onDataReady(result); 通过onDataReady 进入DataFetcher 接口

在DataFetcher 接口中,

  • 由前文可知,我们是从SourceGenerator 过来的,因为走的是默认的缓存策略,所以应该这样回退

    image-20220820185117715

  • 此时,进入SourceGenerator.onDataReadyInternal()

在SourceGenerator.onDataReadyInternal()

  • 关键代码:

     //继续向前回调,此时传入sourceKey,data 等参数
     this.cb.onDataFetcherReady(loadData.sourceKey, data, loadData.fetcher,            loadData.fetcher.getDataSource(), this.originalKey);        
    
  • 通过cb.onDataFetcherReady 回退至onDataFetcherGenerator 接口中的onDataFetcherReady 接口

在onDataFetcherReady 接口中

  • 回退至DecodeJob.onDataFetcherReady()

    image-20220820185609038

  • 进入decodeFromRetrievedData

  • 进入decodeFromData

  • 进入decodeFromFetcher

  • 进入runLoadPath

    • 关键代码:传入了图片宽高,选项(控件的scaleType),监听
     return path.load(
         rewinder, options, width, height, new DecodeCallback<ResourceType>(dataSource));
    
    • 注意,此时的数据仍未InputStream ,只是被不断封装了
  • 通过path.load() 进入LoadPath 再进入loadWithExceptionList

在LoadPath.loadWithExceptionList ()

  • 关键代码:

     //此泛型会装载Bitmap 
     Resource<Transcode> result = null;
     //通过decode 将InputStream 变成Bitmap 
     result = path.decode(rewinder, width, height, options, decodeCallback);
    
  • 进入decode

    • 进入decodeResource

    • 进入decodeResourceWithList :传入图片宽高

      • 关键代码

         //这个result 就是最终拿到的Bitmap 
         result = decoder.decode(data, width, height, options);
        

此时进入decode接口

  • 进入其实现类:

    image-20220820191152268

  • 凭什么说StreamBitmapDecoder 就是它的实现类?

    • 在使用是,我们传入的URL 是String 字符串 ,最终我们需要去得到Bitmap,那么我们就要去解码Decode ,所以断定decode 接口的实现类应该选择StreamBitmapDecoder

在StreamBitmapDecoder.decode()

  • 关键代码:这里就将InputStream 转成了Bitmap(看返回值)

    image-20220820191701620

  • 继续向前回退,注意此时携带的参数是Bitmap ;

    • 往后就只截取出重要流程

是怎么将Bitmap 加载出来的?

在EngineJob.notifyCallbacksOfResult

  • 关键代码:将成功拿到的资源(Bitmap) 加入活动缓存

     engineJobListener.onEngineJobComplete(this, localKey, localResource);
     ​
     //补充代码:
     public synchronized void onEngineJobComplete(xxx
         ……
         activeResources.activate(key, resource);   
    
    • 第一次,从Http 服务器去拿;
    • 第二次,从活动缓存中拿

在SingleRequest.onResourceReady ()

  • 关键代码:设置图片加载位置(ImageViewTarget )

     private final Target<R> target;
     //这个target 就是ImageViewTarget 
     target.onResourceReady(result, animation);
    
  • 通过onResourceReady 进入 Target 接口中的onResourceReady 接口

    • 选择实现类:ImageViewTarget

    image-20220820193326977

在ImageViewTarget.onResourceReady()

  • 关键代码:设置资源

     setResourceInternal(resource);
    
  • 此时进入ImageViewTarget.setResourceInternal()

    • 关键代码:设置资源

       setResource(resource);
      
    • 这个setResource 是一个抽象方法:

       protected abstract void setResource(@Nullable Z resource);
      
      • 进入其实现类

        image-20220820193654645

在BitmapImageViewTarget.setResource

  • 这里就将回调过程中携带的Bitmap 设置上了

     @Override
     protected void setResource(Bitmap resource) {
         view.setImageBitmap(resource);
     }
    

问题汇总:

使用Glide时,with函数在子线程中,会有什么问题?

  • 子线程,不会去添加 生命周期机制, 主线程才会添加 空白的Fragment 去监听 Activity Fragment 的变化。
  • 子线程搞into 会报错的;
  • 真的不建议,在子线程中用Glide ,很麻烦的;

使用Glide时,with函数传入Application后,Glide内部会怎么处理?

  • 在MainActivity中,MainActivity销毁了,并会让Glide生命周期机制处理回收,只有在整个APP应用都没有的时候,跟随着销毁(上节课 ApplicationLIfecycle add onStart onDestroy 什么事情都没有做)。

  • 跟子线程流程一样;这相当于跳过了缓存;

    • 这个很不好,因为那个空白Fragment 是可以在内存紧张的时候,帮助释放资源,避免崩溃;

Glide 线程是怎么切换的?

  • 需要展示的,就直接用Handler + MyLooper 去玩的;

Glide 会不会多次创建?

  • Glide 是单例的;