Glide使用简介及流程分析

Glide

Glide简介

Glide是Google推荐的一套快速高效的图片加载框架,作者是bumptech,功能强大且使用方便,实际的android应用开发中,有不少的开发者在使用它,今天,老衲就带大家来讲解下Glide的使用及实现的逻辑流程。

Glide的使用

Glide的使用与前一篇的Picasso类似,都是链式调用,极其方便。但是,与其他的图片加载框架不同的是,Glide支持GIF的加载与解码。这是该框架的一个亮点,以下为常用API

//设置默认和出错时的图片  
Glide.with(this).load(url).placeholder(resId).error(resId).into(mImageView)
//普通的图片加载
Glide.with(this).load(url).into(mImageView);
//可理解为加载动态图的第一帧的Bitmap,比如Gif
Glide.with(this).load(url).asBitmap().into(imageView);
//GIF加载,URL指向的资源必须是gif,如果是普通图片则不显示。
//相反,如果指向正确但没有执行asGif方法,则只是作为普通图片展示
Glide.with(this).asGif().load(url).into(mImageView);
//缩略图的加载
Glide.with(yourFragment).load(yourUrl).thumbnail(0.1f).into(yourView)

Glide的核心思想:

第一条是多数人认可的观点,其他则是老衲自己在分析源码时对该框架的一些感悟。如有不对请指出

  1. 对象池:
    Glide原理的核心是为bitmap维护一个对象池。对象池的主要目的是通过减少大对象内存的分配以重用来提高性能。对象池的概念参见对象池的使用
  2. 生命周期绑定:
    图片的加载任务会与activity或者Fragment的生命周期绑定,当界面执行onStop的使用自动暂定,而当执行onStart的时候又会自动重新开启,同样的,动态Gif图的加载也是如此,以用来节省电量,同时Glide会对网络状态做监听,当网络状态发生改变时,会重启失败的任务,以减少任务因网络连接问题而失败的概率。
  3. 预览图的使用
    为加快加载速度,提高体验,优先加载预览图
  4. AbsListView内图片的预加载:

Glide的代码流程分析

按照惯例,首先介绍一下业务逻辑中需要用到的类。有印象即可

RequestManager

Glide用来管理和开始请求的类,实现了LifecycleListener接口并重写了如下方法,可以使用Activity和Fragment的生命周期事件机制的开启,停止及重启请求任务。

/** 
  * 开始图片加载请求,一般在Activity或者Fragment的onStart方法内执行,用来重启失败或暂停的任务。
  */
@Override
public void onStart() {    
  resumeRequests();
}
/** 
  * 暂停图片加载请求,一般在Activity或Fragment的onStop方法内执行,用来暂停任务。
*/
@Override
public void onStop() {    
  pauseRequests();
}
/** 
* 取消正在执行的请求,以及释放已完成请求的资源。
*/
@Override
public void onDestroy() {    
  requestTracker.clearRequests();
}

RequestManagerFragment

没有视图的fragment,简单的来讲,就是在每一个Activity或者Fragment上又添加了一个Fragment,该Fragment没有View,仅仅用来存储RequestManager并管理Glide请求

RequestManagerRetriever

用来创建并从Activity或者Fragment检索已存在的RequestManager

Engine

负责开始加载任务,以及管理活跃的,已缓存的资源

BitmapPool(bitmap对象的缓存池)

Bitmap内存池,用来复用对象

LruBitmapPool//基于LruPoolStrategy策略的BitmapPool
BitmapPoolAdapter//该实现类拒绝了对象的复用,get方法总是返回null

LruPoolStrategy

对象池内对象的匹配策略,根据不同的标准,有如下三种匹配策略

//校验bitmap内存大小和图片格式,内部的实现基于数组
1. SizeConfigStrategy
//仅要求bitmap尺寸完全匹配,内部的实现基于HashMap
2. AttributeStrategy
//校验bitmap尺寸和图片格式,内部实现基于TreeMap
3. SizeStrategy

RequestTracker

用来跟踪,取消和重启正在进行中,或者已完成,失败的请求

ConnectivityMonitor

网络状态改变的监听,本质是一个BroadcastReceiver,值得一提的是,该类也是LifecycleListener的实现类,所以,也会有onStart,onStop的方法,而内部的逻辑,就是对网络状态监听广播的注册于反注册

//注册网络状态改变的监听广播
private void register() {    
  if (isRegistered) {        
    return;    
  }    
  isConnected = isConnected(context);
  context.registerReceiver(connectivityReceiver, 
   
    new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));    
  isRegistered = true;
}
//取消注册
private void unregister() {    
  if (!isRegistered) {        
  return;    
  }    
  context.unregisterReceiver(connectivityReceiver);    
  isRegistered = false;
}
//是否有网络链接
private boolean isConnected(Context context) {    
  ConnectivityManager connectivityManager = (ConnectivityManager)  
  context.getSystemService(Context.CONNECTIVITY_SERVICE);    
  NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();
  return networkInfo != null && networkInfo.isConnected();
}
@Override
public void onStart() {    
  register();
}
@Override
public void onStop() {    
  unregister();
}

Resource

支持复用及池存储功能的特殊类型的接口,以下是其部分实现类

EngineResource //支持引用计数的Resource
BitmapResource //Bitmap的包装类
DrawableResource //抽象类,根据ConstantState返回一个依赖于自身ConstantState的drawable的副本
  BitmapDrawableResource //BitmapDrawable的包装类
  GifDrawableResource //GifDrawable的包装类

Target:

LifecycleListener接口的子类,Glide用来加载资源并在加载时通知相关声明周期事件的接口。ViewTarget是它的抽象实现类。典型的生命周期是onLoadStarted -> onResourceReady or onLoadFailed -> onLoadCleared,然而并不保证一定按照此顺序执行,比如:如果资源已经在内存中,则onLoadStarted就不会被调用,同样的,如果Target如果永远不被清除,则onLoadCleared永远不会被调用。

//加载开始时调用
void onLoadStarted(@Nullable Drawable placeholder);
//加载失败是调用
void onLoadFailed(@Nullable Drawable errorDrawable);
//加载结束时调用
void onResourceReady(R resource, Transition<? super R> transition);
//加载任务取消并且资源被释放时调用
void onLoadCleared(@Nullable Drawable placeholder);
//取回目标大小,Callback的实现类为SizeDeterminer,在ViewTarget.class中
void getSize(SizeReadyCallback cb);

ViewTarget

加载资源的基类。Target的部分实现类。根据参数的类型,有不同的实现方法,并能通过ViewTreeObserver.OnDrawListener来决定View的大小。
在需要检测任意涉及到复用View的ViewGroup时(比如listview),该类用setTag方法来存储一些标志,当检测到复用时,之前的加载任务和对应的资源文件会被取消或复用。

ImageViewTarget:在ImageView中展示图片的基类,有如下两个子类
  DrawableImageViewTarget:当参数是drawable的使用使用
    //核心方法
    @Override
    protected void setResource(Bitmap resource) { 
       view.setImageBitmap(resource);
    }
  BitmapImageViewTarget:当参数是bitmap 的时候使用
    //核心方法
    @Override
    protected void setResource(Bitmap resource) { 
       view.setImageBitmap(resource);
    }

LifecycleListener

Fragment和Activity生命周期方法的监听类,主要用来监听onStart,onStop,onDestroy三个方法。实现类如下RequestTracker

RequestManager:负责监听Fragment和Activity中对应的方法
      @Override
      public void onStart() {  
        resumeRequests();  //重启暂停或者失败的任务
        targetTracker.onStart();
      }
      @Override
      public void onStop() {  
        pauseRequests();  //暂停正在执行的任务
        targetTracker.onStop();
      }
DefaultConnectivityMonitor:负责网络状态监听广播的注册于反注册
      @Override
      public void onStart() {  
        register();
      }
      @Override
      public void onStop() {  
        unregister();
      }
BaseTarget:空实现,真正的实现者是其子类ImageViewTarget,用来开始与暂停动画
      @Override
      public void onStart() {  
        if (animatable != null) {    
          animatable.start();  
        }
      }
      @Override
      public void onStop() {  
        if (animatable != null) {    
          animatable.stop();  
        }
      }
TargetTracker:该类调用的,其实是Target类中对应的方法
      @Override
      public void onStart() {  
        for (Target<?> target : Util.getSnapshot(targets)) {    
          target.onStart();  
        }
      }
      @Override
      public void onStop() {  
        for (Target<?> target : Util.getSnapshot(targets)) {   
         target.onStop();  
        }
      }

RequestFutureTarget:空实现,忽略
NullConnectivityMonitor:空实现,忽略

DataFetcher

数据提取的抽象接口,根据资源的来源有不同的实现,例如

HttpUrlFetcher //加载网络图片数据
AssetPathFetcher //加载Asset图片数据
LocalUriFetcher //加载本地图片数据
ThumbFetcher //加载MediaStore中的缩略图数据

DiskCacheStrategy

缓存策略的抽象类,只有Glide提供的固定的几个对象,分别对应不同的策略

ALL:远程数据同时缓存Data和Resource,本地数据仅缓存Resource
NONE:不缓存任何数据
DATA:解码之前直接将数据写入硬盘
RESOURCE:解码之后写入硬盘
AUTOMATIC:默认策略,根据DataFetcher以及ResourceEncoder的编码策略(EncodeStrategy)智能选择

Glide图片加载的业务流程

1. Glide的初始化

public static Glide get(Context context) {  
  if (glide == null) {    
    synchronized (Glide.class) {      
    if (glide == null) {        
      Context applicationContext = context.getApplicationContext(); 
      //查找manifest文件中注册的懒加载的配置信息,下面会介绍到   
      List<GlideModule> modules = new ManifestParser(applicationContext).parse();        
      GlideBuilder builder = new GlideBuilder(applicationContext);
      for (GlideModule module : modules) {
        module.applyOptions(applicationContext, builder);        
       }        
      //创建Glide实例
      glide = builder.createGlide();        
      for (GlideModule module : modules) {   
        module.registerComponents(applicationContext, glide.registry);      
       }      
      }    
    }  
  }  
  return glide;
 }
创建Glide实例对象
Glide createGlide() {  
//依赖于优先级的线程池,用来执行Glide的加载,解码和转换任务(当从缓存中没有找到对应的对象时)
//线程数量取决于当前唤醒的CPU核数,而不是CPU的总数
if (sourceExecutor == null) {    
  sourceExecutor = GlideExecutor.newSourceExecutor();  
} 
//依赖于优先级的线程池,用来执行Glide的加载,解码和转换任务(当从缓存中有对应的对象时)
//线程数量为1
if (diskCacheExecutor == null) {    
   diskCacheExecutor = GlideExecutor.newDiskCacheExecutor();  
}
//内存大小的计算器,计算结果取决于一些常量和当前设备的信息(宽,高,像素密度),最后会与介绍   
if (memorySizeCalculator == null) {    
    memorySizeCalculator = new MemorySizeCalculator.Builder(context).build();  
}  
//网络活动监视器,用来检测网络状态
if (connectivityMonitorFactory == null) {    
  connectivityMonitorFactory = new DefaultConnectivityMonitorFactory();  
}  
//bitmap对象池,用来存储bitmap对象
if (bitmapPool == null) {    
  //3.0以上使用基于LRU算法的bitmap对象池
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 
     int size = memorySizeCalculator.getBitmapPoolSize();   
     bitmapPool = new LruBitmapPool(size);    
  } else {      
    bitmapPool = new BitmapPoolAdapter();    
  }  
}  
//基于LRU算法的数组缓存池
if (arrayPool == null) {    
  arrayPool = new LruArrayPool(memorySizeCalculator.getArrayPoolSizeInBytes()); 
}    
if (memoryCache == null) {    
  memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());  
} 
//硬盘缓存 
if (diskCacheFactory == null) {    
  diskCacheFactory = new InternalCacheDiskCacheFactory(context);  
}  
//负责开启加载任务以及管理活跃的或者缓存的图片资源
if (engine == null) {    
  engine = new Engine(memoryCache, diskCacheFactory, diskCacheExecutor, sourceExecutor);  
}  
return new Glide(context,      
                 engine,      
                 memoryCache,      
                 bitmapPool,      
                 arrayPool,      
                 connectivityMonitorFactory,      
                 logLevel,      
                 defaultRequestOptions.lock());
}

Glide真正的构造方法

Glide(Context context,    
        Engine engine,    
        MemoryCache memoryCache,    
        BitmapPool bitmapPool,    
        ArrayPool arrayPool,    
        ConnectivityMonitorFactory connectivityMonitorFactory,    
        int logLevel,    
        RequestOptions defaultRequestOptions) {  
    this.engine = engine;  
    this.bitmapPool = bitmapPool;  
    this.arrayPool = arrayPool;  
    this.memoryCache = memoryCache;  
    this.connectivityMonitorFactory = connectivityMonitorFactory;  
    //图片的加载格式(ARGB_8888或RGB_565),默认ARGB_8888,判断规则如下
    //如果支持透明或者使用了透明则使用ARGB_8888
    //如果不支持透明则使用ARGB_565
    DecodeFormat decodeFormat = defaultRequestOptions.getOptions().get(Downsampler.DECODE_FORMAT);  
    //BitmapPool的预填充器,最后面有介绍
    bitmapPreFiller = new BitmapPreFiller(memoryCache, bitmapPool, decodeFormat);  
    Resources resources = context.getResources(); 
    //从给定的inputstream中解码图片
    Downsampler downsampler = new Downsampler(resources.getDisplayMetrics(), bitmapPool, arrayPool); 
    //Gif图片资源的解码器
    ByteBufferGifDecoder byteBufferGifDecoder = new ByteBufferGifDecoder(context, bitmapPool, arrayPool);  
    ...
    //包含了加载图片资源所需要的类,比如Registry,Engine,等等
    glideContext = new GlideContext(context, registry, imageViewTargetFactory, defaultRequestOptions, engine, this, logLevel);
  }

2. 创建请求管理器RequestManager

首先根据context的类型,拿到FragmentManager

public RequestManager get(Context context) {  
  if (context == null) {    
    ... 
  } else if (Util.isOnMainThread() && !(context instanceof Application)) {    
    if (context instanceof FragmentActivity) {    
      //FragmentActivity
      return get((FragmentActivity) context);    
    } else if (context instanceof Activity) {   
      //Activity   
      return get((Activity) context);    
    } else if (context instanceof ContextWrapper) {     
      //如果不属于以上两种,则递归查找父类Context 
      return get(((ContextWrapper) context).getBaseContext());    
    } 
  }  
  //返回ApplicationManager
  return getApplicationManager(context);
}

...//get()中会调用fragmentGet方法

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
RequestManager fragmentGet(Context context, 
        android.app.FragmentManager fm, android.app.Fragment parentHint) {  
  //获取RequestManagerFragment
  RequestManagerFragment current = getRequestManagerFragment(fm, parentHint);  
  //获取RequestManagerFragment对应的RequestManager(请求管理器)
  RequestManager requestManager = current.getRequestManager();  
  if (requestManager == null) {    
    requestManager = new RequestManager(context, current.getLifecycle(), 
          current.getRequestManagerTreeNode());   
    current.setRequestManager(requestManager);  
    }  
    return requestManager;
}

RequestManagerRetriever中使用两个map用来分别存放RequestManagerFragment和SupportRequestManagerFragment,Key都是FragmentManager。说白了就是每一个Activity或者FragmentActivity都有一个唯一的FragmentManager,通过这个FragmentManager作为key就可以找到该Activity对应的RequestManagerFragment,Glide就是通过这个Fragment用来实现在生命周期中图片加载的控制,比如Paused状态在暂停加载,在Resumed的时候又自动重新加载

@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
RequestManagerFragment getRequestManagerFragment(final android.app.FragmentManager fm
      , android.app.Fragment parentHint) {  
RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG);  
if (current == null) {   
  //pendingRequestManagerFragments是一个 
   current = pendingRequestManagerFragments.get(fm);    
  if (current == null) {      
    current = new RequestManagerFragment(); 
    current.setParentFragmentHint(parentHint);  
    pendingRequestManagerFragments.put(fm, current);   
    fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
    handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();    
    }  
  }  
  return current;
}

2. 填充资源路径(网络路径或本地路径)

/** 
  * 利用默认的请求参数创建RequestBuilder
  * 默认的参数包括,是否启动硬盘缓存,优先级,错误及加载中的默认图片等
  * 详情请看BaseRequestOptions.java类
  * @return A new request builder for loading a { Drawable} using the given model. 
  */
public RequestBuilder<Drawable> load(@Nullable Object model) { 
 return asDrawable().load(model);
}

3. 设置要加载图片的Target(ImageView)

public Target<TranscodeType> into(ImageView view) {  
  Util.assertMainThread();  
  Preconditions.checkNotNull(view);  
  if (!requestOptions.isTransformationSet()
        && requestOptions.isTransformationAllowed()  
        && view.getScaleType() != null) {    
    ...
  return into(context.buildImageViewTarget(view, transcodeClass));
}


public <Y extends Target<TranscodeType>> Y into(@NonNull Y target) {  
  Util.assertMainThread();  
  Preconditions.checkNotNull(target);  
  if (!isModelSet) {   //是否设置了url 
    ...
  }  
  //获取该Target之前的请求任务(如果有的话)
  Request previous = target.getRequest();  
  //如果有,取消该Target之前所有的任务并释放资源(例如bitmap)以备复用
  if (previous != null) {    
    requestManager.clear(target);  
  }  
  requestOptions.lock(); 
  //创建请求 
  Request request = buildRequest(target);  
  target.setRequest(request);  
  // TODO:下载图片的任务
  requestManager.track(target, request);  
  return target;
}

Glide的缩略图加载思想,为了更快的展示图片,一般来说,一张图片的加载任务分为全尺寸图片和缩略图两部分,因为缩略图更小,所以一般来说相对于全尺寸图片会加载更快,但不绝对,如果缩略图先加载完则先展示缩略图,然后等全尺寸图片加载完成后再加载全尺寸图片,但是,如果全尺寸图片先于缩略图下载完成,则缩略图则不会展示。

private Request buildRequestRecursive(Target<TranscodeType> target, 
    @Nullable ThumbnailRequestCoordinator parentCoordinator,  
    TransitionOptions<?, ? super TranscodeType> transitionOptions,  
    Priority priority, 
    int overrideWidth, 
    int overrideHeight) {  
      //默认的该thumbnailBuilder对象为null,除非手动调用RequestBuilder类的thumbnail方法,
      //否则该if代码永远不会执行
      if (thumbnailBuilder != null) {    
        // Recursive case: contains a potentially recursive thumbnail request builder.    
        if (isThumbnailBuilt) {      
          throw new IllegalStateException("You cannot use a request as both the main request and a thumbnail, consider using clone() on the request(s) passed to thumbnail()");    
        }    
        TransitionOptions<?, ? super TranscodeType> thumbTransitionOptions = thumbnailBuilder.transitionOptions;    
        if (DEFAULT_ANIMATION_OPTIONS.equals(thumbTransitionOptions)) { 
         thumbTransitionOptions = transitionOptions;    
        }    
        //缩略图权限
        Priority thumbPriority = thumbnailBuilder.requestOptions.isPrioritySet() ? thumbnailBuilder.requestOptions.getPriority() : getThumbnailPriority(priority);    
        //缩略图宽高
        int thumbOverrideWidth = thumbnailBuilder.requestOptions.getOverrideWidth();    
        int thumbOverrideHeight = thumbnailBuilder.requestOptions.getOverrideHeight();   
        //宽高校验 
        if (Util.isValidDimensions(overrideWidth, overrideHeight) && !thumbnailBuilder.requestOptions.isValidOverride()) {   
         thumbOverrideWidth = requestOptions.getOverrideWidth();      
         thumbOverrideHeight = requestOptions.getOverrideHeight();    
        }    
        //缩略图请求协调器,用来同时协调缩略图和原始图片的请求
        ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);    
        //原始图片请求
        Request fullRequest = obtainRequest(target, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight);   
        isThumbnailBuilt = true;    
        // Recursively generate thumbnail requests
        //递归生成缩略图请求  
        Request thumbRequest = thumbnailBuilder.buildRequestRecursive(target, coordinator,        thumbTransitionOptions, thumbPriority, thumbOverrideWidth, thumbOverrideHeight);    
        isThumbnailBuilt = false;    
        coordinator.setRequests(fullRequest, thumbRequest);    
        return coordinator;  
  } else if (thumbSizeMultiplier != null) { //根据指定缩放系数加载缩略图   
        ThumbnailRequestCoordinator coordinator = new ThumbnailRequestCoordinator(parentCoordinator);    
        Request fullRequest = obtainRequest(target, requestOptions, coordinator, transitionOptions, priority, overrideWidth, overrideHeight);
        BaseRequestOptions<?> thumbnailOptions = requestOptions.clone().sizeMultiplier(thumbSizeMultiplier);    
        Request thumbnailRequest = obtainRequest(target, thumbnailOptions, coordinator,  transitionOptions, getThumbnailPriority(priority), overrideWidth, overrideHeight);    coordinator.setRequests(fullRequest, thumbnailRequest);    
        return coordinator;  
  } else {    
      // 只加载原始图片 
      return obtainRequest(target, requestOptions, parentCoordinator, transitionOptions, priority, overrideWidth, overrideHeight);  
  }
}

至此,Glide的流程就分析完了,但是!!!和我们之前了解到的Picasso或者Imageloader等图片加载框架不同,我们完全没有看到他的网络请求已经缓存查找的业务逻辑,那这段逻辑究竟在哪呢。我们继续往下看

图片的加载

在上面的最后一步,buildRequestRecursive方法,不管是否有缩略图,我们都会返回一个Request,这个Request会和Target一起被RequestManager接收

void track(Target<?> target, Request request) {  
    targetTracker.track(target);  
    //runRequest最终执行的是Request的begin方法,下面以SingleRequest为例讲解下流程
    requestTracker.runRequest(request);
}


@Override
public void begin() {  
  stateVerifier.throwIfRecycled();  
  startTime = LogTime.getLogTime();  
  //如果图片的来源没有设置,则加载失败,来源是指网络,本地硬盘或者资源文件等。
  if (model == null) {    
    if (Util.isValidDimensions(overrideWidth, overrideHeight)) {      
      width = overrideWidth;      
      height = overrideHeight;    
    }    
    ...   
    onLoadFailed(new GlideException("Received null model"), logLevel);    
    return;  
}  
status = Status.WAITING_FOR_SIZE;  
//如果Target的宽高已经获取并且合法,则开始进行下一步
if (Util.isValidDimensions(overrideWidth, overrideHeight)) {    
  onSizeReady(overrideWidth, overrideHeight);  
} else {    //手动获取Target的宽高
  target.getSize(this);  
}  
if ((status == Status.RUNNING 
  || status == Status.WAITING_FOR_SIZE) 
  && canNotifyStatusChanged()) {    
    target.onLoadStarted(getPlaceholderDrawable());  
  }  
if (Log.isLoggable(TAG, Log.VERBOSE)) {    
  ...
  }
}

@Override
public void onSizeReady(int width, int height) {  
  stateVerifier.throwIfRecycled();  
  ...
  status = Status.RUNNING;  
  //计算缩略图的尺寸
  float sizeMultiplier = requestOptions.getSizeMultiplier();  
  this.width = Math.round(sizeMultiplier * width);  
  this.height = Math.round(sizeMultiplier * height);  
  ...
  //加载任务
  loadStatus = engine.load(glideContext,      
        model,      
        requestOptions.getSignature(),      
        this.width,      
        this.height,      
        requestOptions.getResourceClass(),      
        transcodeClass,      
        priority,      
        requestOptions.getDiskCacheStrategy(),   
        requestOptions.getTransformations(),   
        requestOptions.isTransformationRequired(),    
        requestOptions.getOptions(),     
        requestOptions.isMemoryCacheable(),      
        this);  
      ...
}
加载资源

Glide图片的资源加载与其他图片加载框架的加载逻辑类似,都是按照内存,硬盘及网络的顺序来加载图片,但是Glide得加载又稍显不同,他是的逻辑如下:

/** 
 *  1. 检查内存缓存
 *  2. 检查最近的活跃资源
 *  3. 检查最近的加载任务
 *  活跃资源指的是那些不止一次被加载并没有进行过资源释放的图片,一旦被释放,
 *  那么该资源则会从近期活跃资源中删除并进入到内存缓存中,
 *  但是如果该资源再次从内存缓存中读取,则会重新添加到活跃资源中
 */
public <R> LoadStatus load( 
    GlideContext glideContext, 
    Object model,    
    Key signature,
    int width,
    int height,
    Class<?> resourceClass,
    Class<R> transcodeClass,
    Priority priority,
    DiskCacheStrategy diskCacheStrategy,    
    Map<Class<?>, Transformation<?>> transformations,    
    boolean isTransformationRequired,    
    Options options,    
    boolean isMemoryCacheable,    
    ResourceCallback cb) {  

Util.assertMainThread();  
long startTime = LogTime.getLogTime();  
//内存缓存的唯一键值
EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options); 
//首先从缓存中查找
EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);  
if (cached != null) {    
   cb.onResourceReady(cached, DataSource.MEMORY_CACHE);    
    ...  
   return null;  
}  
//如果缓存中没有找到,则去活跃资源中加载
//memCache中该bitmap则会被remove掉bitmap并进入activeResource中
EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);  
if (active != null) {    
 cb.onResourceReady(active, DataSource.MEMORY_CACHE);    
 ...   
}    
 return null;  
}  
//如果该任务之前已经在队列中,则添加新的callback,然后返回
EngineJob current = jobs.get(key);  
if (current != null) {    
  current.addCallback(cb);    
  ...   
return new LoadStatus(cb, current);  
}  
//如果是新的加载任务,先创建EngineJob和DecodeJob,然后开始任务
EngineJob<R> engineJob = engineJobFactory.build(key, isMemoryCacheable);  
DecodeJob<R> decodeJob = decodeJobFactory.build(glideContext,model,key,signature,width,height,resourceClass,transcodeClass,      priority,diskCacheStrategy,transformations,      isTransformationRequired,options,engineJob); 
jobs.put(key, engineJob);  
engineJob.addCallback(cb);  
//开始任务
engineJob.start(decodeJob);  
...
return new LoadStatus(cb, engineJob);
}


/** 
 *   DecodeJob 类中run方法的实现,DecodeJob是一个Runnable的实现类
 *   该方法的作用是,
 *   1.确定数据的加载来源(Resource,Data,Source)
 *   2.创建对应来源的DataFetcherGenerator
 *   3.执行DataFetcherGenerator
 */
private void runWrapped() {   
  switch (runReason) {    
    case INITIALIZE://首次提交
      stage = getNextStage(Stage.INITIALIZE);  //确定资源的加载来源    
      currentGenerator = getNextGenerator();      
      runGenerators();      
      break;    
    case SWITCH_TO_SOURCE_SERVICE://从硬盘获取资源失败 ,尝试重新获取
      runGenerators();      
      break;    
    case DECODE_DATA://获取数据成功,但不在同一线程  
      decodeFromRetrievedData();      
      break;    
    default:      
      throw new IllegalStateException("Unrecognized run reason: " + runReason);  
    }
  }

/** 
 *   根据当前阶段获取下一阶段
 *   Data和Resource的区别:
 *   Data:原始的图片(或gif)数据
 *   Resource:经过处理(旋转,缩放)后的数据
 *   该方法的大致逻辑如下
 *   1.如果是初始状态,则判断是否解码已缓存的Resource,true是解码Resource。
 *     false的话则会通过递归进入第二个判断分支
 *   2.判断是否解码已缓存的Data,true是解码Data
 *     false的话则会通过递归进入第三个判断分支
 *   3.该阶段则需要从数据源去解码。
 *   简单的来说,就是根据Resource--->Data--->source的顺序去解码加载数据
 *   该阶段Stage的确定,影响着下一阶段DataFetcherGenerator相应子类的实例创建
 */
private Stage getNextStage(Stage current) {  
  switch (current) {    
    case INITIALIZE: 
    return diskCacheStrategy.decodeCachedResource() ? Stage.RESOURCE_CACHE : getNextStage(Stage.RESOURCE_CACHE);   
   case RESOURCE_CACHE:
    return diskCacheStrategy.decodeCachedData() ? Stage.DATA_CACHE : getNextStage(Stage.DATA_CACHE);    
  case DATA_CACHE:  
    return Stage.SOURCE;    
  case SOURCE:    
  case FINISHED:      
    return Stage.FINISHED;    
  default:      
    throw new IllegalArgumentException("Unrecognized stage: " + current);  
  }
}

/** 
 *   根据不同的阶段创建不同的DataFetcherGenerator,该类使用已注册的ModelLoaders和Model
 *   来生成一系列的DataFetcher。有如下实现类
 *   DataFetcherGenerator:经过处理的资源数据缓存文件(采样转换等处理)
 *   ResourceCacheGenerator:未经处理的资源数据缓存文件
 *   SourceGenerator:源数据的生成器,包含了根据来源创建的ModelLoader和Model(文件路径,URL...)
 */
private DataFetcherGenerator getNextGenerator() {  
  switch (stage) {    
    case RESOURCE_CACHE:      
      return new ResourceCacheGenerator(decodeHelper, this);    
    case DATA_CACHE:      
      return new DataCacheGenerator(decodeHelper, this);    
    case SOURCE:      
      return new SourceGenerator(decodeHelper, this);    
    case FINISHED:      
      return null;    
    default:      
      throw new IllegalStateException("Unrecognized stage: " + stage); 
    }
}

/** 
 *   根据不同的状态来选择并执行生成器
 *  从当前Generator 获取数据,如果获取成功则直接回调onDataFetcherReady,
 *  如果失败则通过reschedule重新调度
 */
private void runGenerators() {  
  ... 
  boolean isStarted = false;  
  while (!isCancelled && currentGenerator != null 
          && !(isStarted = currentGenerator.startNext())) {    
    stage = getNextStage(stage);    
    currentGenerator = getNextGenerator();    
    if (stage == Stage.SOURCE) {      
      reschedule();      
      return;    
    }  
  }  
  // 加载失败 
  if ((stage == Stage.FINISHED || isCancelled) && !isStarted) {
    notifyFailed();  
  }  
}

如果是首次加载一张图片资源,最终会来到SourceGenerator的startNext来执行。

/** 
 *  SourceGenerator
 *  DataFetcher的简介:Fetcher的意思是抓取,所以该类可以称为数据抓取器
 *      作用就是根据不同的数据来源(本地,网络,Asset等) 
 *      以及读取方式(Stream,ByteBuffer等)来提取并解码数据资源,实现类如下
 *  AssetPathFetcher:加载Asset数据
 *  HttpUrlFetcher:加载网络数据
 *  LocalUriFetcher:加载本地数据
 *  其他实现类...
 * 
 */
@Override
public boolean startNext() {  
  ...
  if (sourceCacheGenerator != null && sourceCacheGenerator.startNext()) {    
    return true;  
  }  
sourceCacheGenerator = null;  
loadData = null;  
boolean started = false;  
//是否有更多的ModelLoader
while (!started && hasNextModelLoader()) {    
  loadData = helper.getLoadData().get(loadDataListIndex++);    
  if (loadData != null&& (helper.getDiskCacheStrategy()
    .isDataCacheable(loadData.fetcher.getDataSource())
    || helper.hasLoadPath(loadData.fetcher.getDataClass()))) {      
      started = true;      
      //选择合适的LoadData,并使用LoadData中的fetcher来抓取数据
      loadData.fetcher.loadData(helper.getPriority(), this);    
      }  
   }  
  return started;
}

当走完上面的流程,接下来就是我们最熟悉的网络数据请求的模块了,因为该模块在整个的图片加载流程中并不是很重要,所以就简单介绍一下,不过有几个知识点还是比较有意思的。

/** 
 *  HttpUrlFetcher
 *  HttpUrlFetcher的简介:网络数据抓取器,通俗的来讲就是去服务器上下载图片,支持地址重定向(最多5次)
 * 
 */
@Override
public void loadData(Priority priority, DataCallback<? super InputStream> callback) {  
  long startTime = LogTime.getLogTime();  
  final InputStream result;  
  try {    
    result = loadDataWithRedirects(glideUrl.toURL(), 0 /*redirects*/, null /*lastUrl*/, glideUrl.getHeaders());  
    } catch (IOException e) {    
    ...  
    callback.onLoadFailed(e);    
    return;  
    }  
  ...  
  callback.onDataReady(result);
}


private InputStream loadDataWithRedirects(URL url, int redirects, URL lastUrl,
   Map<String, String> headers) throws IOException { 
  //重定向次数过多
  if (redirects >= MAXIMUM_REDIRECTS) {    
    throw new HttpException("Too many (> " + MAXIMUM_REDIRECTS + ") redirects!");  
   } else {     
    //通过URL的equals方法来比较会导致NetworkI/O开销,一般会有问题,
    //有兴趣的同学可以看下下面的链接或者直接阅读URL里equals方法的源码注释,一目了然
    //http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html.   
   try {      
    if (lastUrl != null && url.toURI().equals(lastUrl.toURI())) {        
        throw new HttpException("In re-direct loop");      
      }    
     } catch (URISyntaxException e) {      
      // Do nothing, this is best effort.    
     }  
  }  
  //HttpUrlConnection下载图片
  ...
}

Glide的知识点

接下来,老衲带大家看下Glide中有什么我们在日常开发的时候能用得上的技术

1.线程池内线程的个数的计算方式

/** 
 * 根据/sys/devices/system/cpu/下的文件来决定线程池内线程的数量
 * 决定线程数量的不是一共得CPU核数,而是唤醒的CPU核数
 * 
 * See http://goo.gl/8H670N. 
 */
public static int calculateBestThreadCount() {  
  File[] cpus = null;  
  try {    
    File cpuInfo = new File(CPU_LOCATION);    
    final Pattern cpuNamePattern = Pattern.compile(CPU_NAME_REGEX);    
      cpus = cpuInfo.listFiles(new FilenameFilter() {      
        @Override      
        public boolean accept(File file, String s) {        
          return cpuNamePattern.matcher(s).matches();      
          }    
      });  
  } catch (Throwable t) {    
    ... 
  }  
  int cpuCount = cpus != null ? cpus.length : 0;  
  int availableProcessors = Math.max(1, Runtime.getRuntime().availableProcessors());  
  return Math.min(MAXIMUM_AUTOMATIC_THREAD_COUNT, Math.max(availableProcessors, cpuCount));
}

2. 权限判断

以网络请求的权限为例

final int res = context
        .checkCallingOrSelfPermission("android.permission.ACCESS_NETWORK_STATE");
final boolean hasPermission = res == PackageManager.PERMISSION_GRANTED;

Glide的懒加载配置

解析Manifest文件中注册的懒加载配置信息
public List<GlideModule> parse() {  
  List<GlideModule> modules = new ArrayList<>();  
  try {    
    ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);    
    if (appInfo.metaData != null) {      
      for (String key : appInfo.metaData.keySet()) {        
      if (GLIDE_MODULE_VALUE.equals(appInfo.metaData.get(key))) {
        modules.add(parseModule(key));        
        }      
      }    
    }  
  } catch (PackageManager.NameNotFoundException e) {    
    ...
  }  
  return modules;
}
3. Glide给出的懒加载示例(混淆代码的话请读者自己查看GlideModule类的注释)
public class FlickrGlideModule implements GlideModule {
    Override
   public void applyOptions(Context context, GlideBuilder builder) {
     builder.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888);
    }
    public void registerComponents(Context context, Glide glide) {
      glide.register(Model.class, Data.class, new MyModelLoader());
    }
  }

4. Glide的预填充机制

Glide最核心的部分,就是Bitmap池的使用,
Glide类中有一个preFillBitmapPool方法,用来预填充bitmap对象池,他的注释如下

 /**
   * 作用:根据给定的尺寸预填充Bitmap对象池
   * 缺陷:太多的资源释放会导致GC频繁执行,这样就失去了Glide本身存在的意义。此方法要慎重使用。
   * 若太多的Bitmap被添加到对象池使其完全被填满,会导致大多数甚至全部最近被添加的bitmap被驱逐(释放)
   * bitmap会根据给定的尺寸的权重来分配,每一种尺寸只会填充对象池所占内存的一定比例。
   * 比例的计算公式为weight / prefillWeightSum
   */
public void preFillBitmapPool(PreFillType.Builder... bitmapAttributeBuilders) {          
  bitmapPreFiller.preFill(bitmapAttributeBuilders);
}

public void preFill(PreFillType.Builder... bitmapAttributeBuilders) {    
 if (current != null) {        
    current.cancel();    
 }    
//PreFillType中保存着图片的宽高,Config以及weight等信息
PreFillType[] bitmapAttributes = new PreFillType[bitmapAttributeBuilders.length];    
for (int i = 0; i < bitmapAttributeBuilders.length; i++) {    
  PreFillType.Builder builder = bitmapAttributeBuilders[i];       
   if (builder.getConfig() == null) {            
    builder.setConfig(defaultFormat == DecodeFormat.ALWAYS_ARGB_8888   
    || defaultFormat == DecodeFormat.PREFER_ARGB_8888? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565);        
  }        
  bitmapAttributes[i] = builder.build();    
}    
  //根据PreFillType中保存的图片信息创建预填充队列
  PreFillQueue allocationOrder = generateAllocationOrder(bitmapAttributes);    
  //创建用来填充对象池的线程对象,
  //该类通过Handler发布到主线程中尽量避免GC因高比例的Bitmap触发垃圾回收所导致的ANR,
  //通过延时减少GC线程的垃圾回收的次数
  current = new BitmapPreFillRunner(bitmapPool, memoryCache, allocationOrder);    
  handler.post(current);
}

// Visible for testing.
PreFillQueue generateAllocationOrder(PreFillType[] preFillSizes) {    
//剩余内存
 final int maxSize = memoryCache.getMaxSize() - memoryCache.getCurrentSize() + bitmapPool.getMaxSize();    
 int totalWeight = 0;    
//计算图片总权重
 for (PreFillType size : preFillSizes) {        
   totalWeight += size.getWeight();    
 }    
  //计算每一份权重占用的字节
 final float bytesPerWeight = maxSize / (float) totalWeight;   
 Map<PreFillType, Integer> attributeToCount = new HashMap<PreFillType, Integer>();    
 for (PreFillType size : preFillSizes) {        
   //根据权重计算出的图片的内存占用量
   int bytesForSize = Math.round(bytesPerWeight * size.getWeight());  
   //根据宽高和Config计算出的图片的内存占用量
   int bytesPerBitmap = getSizeInBytes(size);     
   int bitmapsForSize = bytesForSize / bytesPerBitmap;
   attributeToCount.put(size, bitmapsForSize);    
  }   
 return new PreFillQueue(attributeToCount);
}

5. DefaultConnectivityMonitor 网络状态监视器

平常的开发中使用频率还是比较高的,可以直接拿来用,完整代码请参考源码

private final BroadcastReceiver connectivityReceiver = new BroadcastReceiver() {    
@Override    
public void onReceive(Context context, Intent intent) {        
  boolean wasConnected = isConnected;        
  isConnected = isConnected(context);        
  if (wasConnected != isConnected) {   
     listener.onConnectivityChanged(isConnected);        
    }    
  }
};
public DefaultConnectivityMonitor(Context context, ConnectivityListener listener) {    
  this.context = context.getApplicationContext();    
  this.listener = listener;
}
private void register() {    
 if (isRegistered) {        
  return;    
 }    
isConnected = isConnected(context);    
context.registerReceiver(connectivityReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));   
isRegistered = true;}private void unregister() {    
if (!isRegistered) {        
  return;    
  }    
context.unregisterReceiver(connectivityReceiver);    
isRegistered = false;
}
private boolean isConnected(Context context) {    
  ConnectivityManager connectivityManager = (ConnectivityManager) context
    .getSystemService(Context.CONNECTIVITY_SERVICE);    
  NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();    
  return networkInfo != null && networkInfo.isConnected();
}
@Override
public void onStart() {    
  register();
}
@Override
public void onStop() {    
  unregister();
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容