Glide源码分析Two

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

内容简介: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源码分析流程思维导图

©爱穿衬衫的程序员


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

查看所有标签

猜你喜欢:

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

来自圣经的证明

来自圣经的证明

M.Aigner、G.M.Ziegler / 世界图书出版公司 / 2006-7 / 39.00元

作为一门历史悠久的学问,数学有她自身的文化和美学,就像文学和艺术一样。一方面,数学家们在努力开拓新领域、解决老问题;另一方面他们也在不断地从不同的角度反复学习、理解和欣赏前辈们的工作。的确,数学中有许多不仅值得反复推敲理解,更值得细心品味和欣赏的杰作。有些定理的证明不仅想法奇特、构思精巧,作为一个整体更是天衣无缝。难怪,西方有些虔诚的数学家将这类杰作比喻为上帝的创造。 本书已被译成8种文字。......一起来看看 《来自圣经的证明》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码