Glide源码分析Two

栏目: IOS · Android · 发布时间: 5年前

内容简介:Glide4.0之后,GlideModule推荐使用注解(@GlideModule)形式实现

Glide官方文档地址

1.1、 Example

Glide.with(imageView.context)
    .load(roundRectUrl)
    .apply(RequestOptions().priority(Priority.HIGH))
    .apply(RequestOptions().diskCacheStrategy(DiskCacheStrategy.DATA))
    .apply(RequestOptions.centerCropTransform())
    .apply(
        RequestOptions.bitmapTransform(
            RoundedCornersTransformation(
                radius, 0, RoundedCornersTransformation.CornerType.ALL
            )
        )
    )
    .into(imageView)
复制代码

GlideDemo GitHub Address

1.2、自定义AppGlideModule

Glide4.0之后,GlideModule推荐使用注解(@GlideModule)形式实现

@GlideModule
class GlideConfigModule : AppGlideModule() {

    override fun applyOptions(context: Context, builder: GlideBuilder) {
        //设置内存大小
        builder.setMemoryCache(LruResourceCache(1024 * 1024 * 100)) // 100M

        //设置图片缓存大小
        builder.setBitmapPool(LruBitmapPool(1024 * 1024 * 50))

        /**
         * 设置磁盘缓存大小
         */
        // 内部缓存目录  data/data/packageName/DiskCacheName
        builder.setDiskCache(
            InternalCacheDiskCacheFactory(context, "GlideDemo", 1024 * 1024 * 100)
        )
        // 外部磁盘SD卡
        /*builder.setDiskCache(
            ExternalPreferredCacheDiskCacheFactory(context, "GlideDemo", 1024 * 1024 * 10)
        )*/
    }

    override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
        // 替换网络请求组件,使用OkHttp
        val builder = OkHttpClient.Builder()
        builder.addInterceptor(ProgressInterceptor)
        val okHttpClient = builder.build()

        registry.replace(GlideUrl::class.java, InputStream::class.java, OkHttpUrlLoader.Factory(okHttpClient))
    }

    /**
     * 禁用清单解析
     */
    override fun isManifestParsingEnabled(): Boolean {
        return false
    }
}
复制代码

二、前言

2.1 一个好的图片加载框架必备的功能:

当阅读Glide源码之前,我们应该考虑一个问题,假设让我们自己去实现一个图片加载框架,会需要哪些功能?

1、网络组件,下载图片 HttpUrlConnection OkHttp

2、请求队列,线程池 优先级处理,请求的终止

3、缓存: 内存缓存,本地文件缓存,服务器缓存等等

4、内存资源回收机制: 先进先出,或者生命周期

5、更加基础的组件就是各种图片资源的转码解码了

2.2 Android图片加载框架对比

假设你去面试,面试官问你使用什么图片框架,你说你用Glide。面试官并不是想让你说Glide,而是你选择Glide图片加载框架的原因和理由。它与其他图片加载框架的优缺点。

这里引用前辈的文章,就不在单独再写一篇了: Android图片加载框架Fresco,Glide,Picasso对比分析

三、Glide流程分析

3.1 流程图

这是从网上找的流程图:

Glide源码分析Two

你心中会想:这么简单??

我回答你,当然不是这么简单,但这确实是最主要最底层的架构,所有的图片加载框架都一样,区别在于在这个基础上延伸的各种小细节,比如多少级缓存、缓存的位置(是缓存在应用的内存中,还是缓存在系统的共享匿名内存中)、生命周期管理等等。

3.2 Glide使用三步曲源码分析

这里就需要去阅读我的上一篇文章了:Glide源码分析One

3.3 类关系图

OK,当你看完了3.2后,再看Glide的类关系图你会相当的清晰,这个图也是我从网上找的:
Glide源码分析Two

接下来就是一些细节问题了

四、生命周期管理

生命周期管理是在: 第一步Glide.with()中创建的,具体逻辑在RequestManagerRetriever.get()中实现:

4.1 RequestManagerRetriever.get():

public RequestManager get(@NonNull Context context) {
  、、、
  if (Util.isOnMainThread() && !(context instanceof Application)) {
    if (context instanceof FragmentActivity) {
      return get((FragmentActivity) context);
    } else if (context instanceof Activity) {
      return get((Activity) context);
    } else if (context instanceof ContextWrapper) {
      return get(((ContextWrapper) context).getBaseContext());
    }
  }
  return getApplicationManager(context);
}
复制代码

get()方法重载了很多,参数对应与Glide.with()方法的参数,分别有Context、Activity、FragmentActivity、Fragment、android.support.v4.app.Fragment、View.

参数很重要,参数很重要,参数很重要!!!接下来给出解释: 1、RequestManagerRetriever.get(Activity): 参数为Activity的get()方法
public RequestManager get(@NonNull Activity activity) {
    if (Util.isOnBackgroundThread()) {
      return get(activity.getApplicationContext());
    } else {
      assertNotDestroyed(activity);
      android.app.FragmentManager fm = activity.getFragmentManager();  // FragmentManager直接来自Activity
      return fragmentGet(
          activity, fm, /*parentHint=*/ null, isActivityVisible(activity));
    }
  }
复制代码

2、RequestManagerRetriever.get(Fragment): 参数为Fragment的get()方法

public RequestManager get(@NonNull Fragment fragment) {
    if (Util.isOnBackgroundThread()) {
      return get(fragment.getActivity().getApplicationContext());
    } else {
      FragmentManager fm = fragment.getChildFragmentManager();  // FragmentManager来自Fragment
      return supportFragmentGet(fragment.getActivity(), fm, fragment, fragment.isVisible());
    }
  }
复制代码

3、RequestManagerRetriever.get(View): 参数为View的get()方法

public RequestManager get(@NonNull View view) {
    ...
    Activity activity = findActivity(view.getContext());
    if (activity == null) {
      return get(view.getContext().getApplicationContext());
    }

    // Support Fragments.
    // Although the user might have non-support Fragments attached to FragmentActivity, searching
    // for non-support Fragments is so expensive pre O and that should be rare enough that we
    // prefer to just fall back to the Activity directly.
    if (activity instanceof FragmentActivity) {
      Fragment fragment = findSupportFragment(view, (FragmentActivity) activity);
      return fragment != null ? get(fragment) : get(activity);
    }

    // Standard Fragments.
    android.app.Fragment fragment = findFragment(view, activity);
    if (fragment == null) {
      return get(activity);
    }
    return get(fragment);
  }
复制代码

通过View去寻找其所在的Fragment,如果该View在Fragment中,直接走参数为Fragment的get(Fragment)方法;如果在Activity中,则直接走参数为Activity的get(Activity)方法

重载的所有get()方法,最终都只有三种情况:

1)、 getApplicationManager()

2)、 fragmentGet()

3)、 supportFragmentGet()

  • 1、 getApplicationManager() 当参数为ApplicationContext或者运行在非主线程时,会执行到getApplicationManager()方法:
private RequestManager getApplicationManager(@NonNull Context context) {
  // Either an application context or we are on a background thread.
  if (applicationManager == null) {
    synchronized (this) {
      if (applicationManager == null) {
        // Normally pause/resume is taken care of by the fragment we add to the fragment or
        // activity. However, in this case since the manager attached to the application will not
        // receive lifecycle events, we must force the manager to start resumed using
        // ApplicationLifecycle.

        // TODO(b/27524013): Factor out this Glide.get() call.
        Glide glide = Glide.get(context.getApplicationContext());
        applicationManager =
            factory.build(
                glide,
                new ApplicationLifecycle(),
                new EmptyRequestManagerTreeNode(),
                context.getApplicationContext());
      }
    }
  }
  return applicationManager;
}
复制代码

看该方法的注释,Request(也就是缓存)的生命周期是跟随Application

  • 2、fragmentGet(FragmentManager)
private RequestManager fragmentGet(@NonNull Context context,
      @NonNull android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    RequestManagerFragment current = getRequestManagerFragment(fm, parentHint, isParentVisible); 
    RequestManager requestManager = current.getRequestManager();
    if (requestManager == null) {
      Glide glide = Glide.get(context);
      // build RequestManager时,将RequestManagerFragment的生命周期(current.getGlideLifecycle())传入
      requestManager =
          factory.build(
              glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
      current.setRequestManager(requestManager);
    }
    return requestManager;
  }

private RequestManagerFragment getRequestManagerFragment(
      @NonNull final android.app.FragmentManager fm,
      @Nullable android.app.Fragment parentHint,
      boolean isParentVisible) {
    // 通过FragmentManager寻找是否已经添加过RequestManagerFragment,避免重复添加
    RequestManagerFragment current = (RequestManagerFragment) fm.findFragmentByTag(FRAGMENT_TAG); 
    if (current == null) {
      current = pendingRequestManagerFragments.get(fm);
      if (current == null) {
        current = new RequestManagerFragment();  // 新建一个空布局的Fragment
        current.setParentFragmentHint(parentHint);
        if (isParentVisible) {
          current.getGlideLifecycle().onStart();
        }
        // 在原有的界面上添加一个空布局的Fragment,用于生命周期管理
        pendingRequestManagerFragments.put(fm, current); 
        fm.beginTransaction().add(current, FRAGMENT_TAG).commitAllowingStateLoss();
        handler.obtainMessage(ID_REMOVE_FRAGMENT_MANAGER, fm).sendToTarget();
      }
    }
    return current;
  }
复制代码

Glide的请求的生命周期管理是通过向页面添加一个空布局的Fragment来实现的,上面说参数很重要,这里给大家解惑。
1、当参数为Activity时,空布局的Fragment直接添加在Activity上,此时的Request的生命周期跟随Activity;
2、当参数为Fragment时,空布局的Fragment直接添加在Fragment上,此时的Request的生命周期跟随父Fragment;
3、单参数为View时,这时会通过View去找寻该View是在Fragment上还是在Activity中,分别走以上两种情况;

4.2 RequestManager

RequestManager(
    Glide glide,
    Lifecycle lifecycle,
    RequestManagerTreeNode treeNode,
    RequestTracker requestTracker,
    ConnectivityMonitorFactory factory,
    Context context) {
  ···
  // If we are the application level request manager, we may be created on a background thread.
  // In that case we cannot risk synchronously pausing or resuming requests, so we hack around the
  // issue by delaying adding ourselves as a lifecycle listener by posting to the main thread.
  // This should be entirely safe.
  if (Util.isOnBackgroundThread()) {
    mainHandler.post(addSelfToLifecycle);
  } else {
    lifecycle.addListener(this);
  }
  ···
}

private final Runnable addSelfToLifecycle = new Runnable() {
  @Override
  public void run() {
    lifecycle.addListener(RequestManager.this);
  }
};
复制代码
1、mainHandler.post(addSelfToLifecycle);

当前线程为后台进程、或者是application等级的主线程时,通过mainHandler切回主线程,因为得刷新UI,主线程安全

2、lifecycle.addListener(this);

直接监听上一步生成的布局为空的fragment的生命周期

五、优先级Priority

5.1 Priority 类

public enum Priority {
  IMMEDIATE,
  HIGH,
  NORMAL,
  LOW,
}
复制代码

5.2 DecodeJob

前面分析Glide执行流程时发现,DecodeJob是真正干活的类,其实现了Runnable接口,但是它还实现了Comparable<DecodeJob<?>>接口.

Engine实例化DecodeJob时,会将优先级属性传入DecodeJob中,具体使用在其实现的比较方法中:

/**
 * @return  a negative integer, zero, or a positive integer as  
 * this object is less than, equal to, or greater than the specified        
 **/
@Override
public int compareTo(@NonNull DecodeJob<?> other) {
  int result = getPriority() - other.getPriority();
  if (result == 0) {
    result = order - other.order;
  }
  return result;
}
复制代码
ThreadPoolExecutor:

execute()

PriorityBlockingQueue: // 线程池的任务队列,选择高优先级的任务优先执行

offer()

siftUpComparable()

同时实现Runnable和Comparable<DecodeJob<?>>接口,线程池自动实现优先级的自动排序。

六、缓存

6.1 LRU缓存算法

在Glide初始化时,会指定一个默认的缓存算法,在GlideBuilder.build()方法中

Glide build(@NonNull Context context) {
  ···
  if (memoryCache == null) {
      memoryCache = new LruResourceCache(memorySizeCalculator.getMemoryCacheSize());
  }
  ···
}
复制代码

使用了LRU(Least Recently Used)算法,核心就是最近最少使用。在算法的内部维护了一个LinkHashMap的链表(双向链表),通过put数据的时候判断是否内存已经满了,如果满了,则将最近最少使用的数据给剔除掉,从而达到内存不会爆满的状态。

Glide源码分析Two

更重要的一点是,当我们通过get()方法获取数据的时候,这个获取的数据会从队列中跑到队列尾来,从而很好的满足我们LruCache的算法设计思想。

LinkedHashMap.get():

public V get(Object key) {
    Node<K,V> e;
    if ((e = getNode(hash(key), key)) == null)
        return null;
    if (accessOrder)
        afterNodeAccess(e);
    return e.value;
}

void afterNodeAccess(Node<K,V> e) { // move node to last
    LinkedHashMapEntry<K,V> last;
    if (accessOrder && (last = tail) != e) {
        LinkedHashMapEntry<K,V> p =
            (LinkedHashMapEntry<K,V>)e, b = p.before, a = p.after;
        p.after = null;
        if (b == null)
            head = a;
        else
            b.after = a;
        if (a != null)
            a.before = b;
        else
            last = b;
        if (last == null)
            head = p;
        else {
            p.before = last;
            last.after = p;
        }
        tail = p;
        ++modCount;
    }
}
复制代码

6.2 Glide缓存逻辑

缓存逻辑关键类Engine

6.2.1 缓存Key

  • Enginr.load():
public synchronized <R> LoadStatus load(...) {
  EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);
  ...
}
复制代码

model: Url、Uri、FilePath,图片来源地址

这里有个前提知识必须得知道:

DiskCacheStrategy.NONE 什么都不缓存

DiskCacheStrategy.DATA 只缓存原来的全分辨率的图像。

DiskCacheStrategy.RESOURCE 只缓存最终的图像,即降低分辨率后的(或者是 转换后的)

DiskCacheStrategy.ALL 缓存所有版本的图像

DiskCacheStrategy.AUTOMATIC 让Glide根据图片资源智能地选择使用哪一种缓存策略 (默认选项)

width,height:从缓存的key中包含宽高能得出,同一张图片,假设设置的不是缓存原始图片,不同宽高将缓存不同的图片。

优点: 加载更快,少了图片宽高处理的过程。

缺点: 更费内存

另一Android图片加载框架Picasso,只缓存一张全尺寸的图片,优点是占用内存小,缺点是再次显时示,需要重新调整大小。

6.2 缓存读取逻辑

Enginr.load():

public synchronized <R> LoadStatus load() {
 ... 
 EngineKey key = keyFactory.buildKey(model, signature, width, height, transformations, resourceClass, transcodeClass, options);

EngineResource<?> active = loadFromActiveResources(key, isMemoryCacheable);
if (active != null) {
 cb.onResourceReady(active, DataSource.MEMORY_CACHE);
 if (VERBOSE_IS_LOGGABLE) {
   logWithTimeAndKey("Loaded resource from active resources", startTime, key);
 }
 return null;
}

EngineResource<?> cached = loadFromCache(key, isMemoryCacheable);
if (cached != null) {
 cb.onResourceReady(cached, DataSource.MEMORY_CACHE);
 if (VERBOSE_IS_LOGGABLE) {
   logWithTimeAndKey("Loaded resource from cache", startTime, key);
 }
 return null;
}

EngineJob<?> current = jobs.get(key, onlyRetrieveFromCache);
if (current != null) {
 current.addCallback(cb, callbackExecutor);
 if (VERBOSE_IS_LOGGABLE) {
   logWithTimeAndKey("Added to existing load", startTime, key);
 }
 return new LoadStatus(cb, current);
}

// 生成网络请求逻辑
...
}

@Nullable
private EngineResource<?> loadFromActiveResources(Key key, boolean isMemoryCacheable) {
 if (!isMemoryCacheable) {
   return null;
 }
 EngineResource<?> active = activeResources.get(key);
 if (active != null) {
   active.acquire();  // 引用计数器加1
 }
 return active;
}

private EngineResource<?> loadFromCache(Key key, boolean isMemoryCacheable) {
 if (!isMemoryCacheable) {
   return null;
 }

 EngineResource<?> cached = getEngineResourceFromCache(key);  // 将缓存数据从缓存队列中移出
 if (cached != null) {
   cached.acquire();  // 引用计数器加1
   activeResources.activate(key, cached);  // 将数据放入正在活动的缓存列表中
 }
 return cached;
}
复制代码

EngineResource.acquire():

synchronized void acquire() {
 if (isRecycled) {
   throw new IllegalStateException("Cannot acquire a recycled resource");
 }
 ++acquired; // 资源被引用的计数器
}
复制代码

6.3 缓存生成

其实就是在图片数据加载成功后,放入缓存列表。回调的最终方法在Engine的onEngineJobComplete()方法。

public synchronized void onEngineJobComplete(
   EngineJob<?> engineJob, Key key, EngineResource<?> resource) {
 // A null resource indicates that the load failed, usually due to an exception.
 if (resource != null) {
   resource.setResourceListener(key, this);

   if (resource.isCacheable()) { // 判断该资源是否需要缓存
     activeResources.activate(key, resource);   // 缓存
   }
 }
 jobs.removeIfCurrent(key, engineJob);
}
复制代码

6.4 缓存的移除

Engine.onResourceReleased():

public synchronized void onResourceReleased(Key cacheKey, EngineResource<?> resource) {
 activeResources.deactivate(cacheKey); // 从活动列表移除
 if (resource.isCacheable()) {
   cache.put(cacheKey, resource);  // 放入缓存列表
 } else {
   resourceRecycler.recycle(resource);
 }
}
复制代码

移除缓存的具体调用流程:

这里就得扯到前面的生命周期管理了,RequestManager中实现了LifecycleListener,接收Fragment或Activity的生命周期,这里主要分析onDestory()方法:

RequestManager.onDestory():

public synchronized void onDestroy() {
  targetTracker.onDestroy();
  for (Target<?> target : targetTracker.getAll()) {
    clear(target);
  }
  targetTracker.clear();
  requestTracker.clearRequests();
  lifecycle.removeListener(this);
  lifecycle.removeListener(connectivityMonitor);
  mainHandler.removeCallbacks(addSelfToLifecycle);
  glide.unregisterRequestManager(this);
}
复制代码

RequestTracker.clearRequests() -> SingleRequest.clear() -> Engine.release() -> EngineResource.release()

void release() {
  // To avoid deadlock, always acquire the listener lock before our lock so that the locking
  // scheme is consistent (Engine -> EngineResource). Violating this order leads to deadlock
  // (b/123646037).
  synchronized (listener) {
    synchronized (this) {
      if (acquired <= 0) {
        throw new IllegalStateException("Cannot release a recycled or not yet acquired resource");
      }
      if (--acquired == 0) { // 引用数值标准acquire减1
        listener.onResourceReleased(key, this);
      }
    }
  }
}
复制代码

参考了下面的优秀文章:

Glide源码分析

Glide源码分析流程思维导图

©爱穿衬衫的程序员


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Developing Large Web Applications

Developing Large Web Applications

Kyle Loudon / Yahoo Press / 2010-3-15 / USD 34.99

As web applications grow, so do the challenges. These applications need to live up to demanding performance requirements, and be reliable around the clock every day of the year. And they need to withs......一起来看看 《Developing Large Web Applications》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具