跟着源码学设计:Glide框架及源码解析(二)

前言
近期研究了一下Glide的图片加载框架,在这里和大家分享一下。由于代码研读有限,难免有错误的地方,了解的童鞋还望指正。

本篇是Glide框架及源码解析的第二篇,更多文章敬请关注后续文章。如果这篇文章对大家学习Glide有帮助,还望大家多多转载。

版权归作者所有,如有转发,请注明文章出处:http://www.jianshu.com/u/d43d948bef39

相关文章:

跟着源码学设计:Glide框架及源码解析(一)
跟着源码学设计:Glide框架及源码解析(二)
跟着源码学设计:Glide框架及源码解析(三)
跟着源码学设计:Glide框架及源码解析(四)
跟着源码学设计:Glide框架及源码解析(五)

1. Request管理机制

在上一篇中我们剖析了Glide的生命周期绑定机制,这一篇我们紧接着Glide的处理流程来学习一下Glide的请求管理机制。
我们先来看一下Glide的最简单的使用代码示例:

ImageView ivImage = (ImageView) findViewById(R.id.ivImage);
Glide.with(ivImage.getContext()) //获取RequestManager对象
     .load(url); //设置Request对象需要的资源链接
     .into(ivImage); //获取Request对象并绑定viewTarget -> 发起网络请求

该段代码十分简洁,但是内部实现的功能却十分的强大,比如:

  • request的生命周期管理(如:退出或者隐藏了界面,需求就取消或暂停了)
  • viewTarget的生命周期管理
  • 资源的复用和释放
  • 灵活的配置(request的builder模式)

针对这些问题,后面将会一一展开剖析。

2. request及其生命周期管理

  • 通过上一篇的学习,我们知道了Glide内部生命周期接口为LifecycleListener
  • RequestManager具有生命周期(实现了LifecycleListener接口)
  • request由RequestManager的into()方法族获得
  • request的生命周期由RequestManager统一管理

3. Glide请求管理机制类图

RequestManager是如何生成request并管理request队列的?
老规矩,先上图:


Glide请求管理机制类图

如图:

  1. RequestManager持有一个RequestTracker对象requestTracker。
  2. requestTracker对象维护request的队列集合
  3. RequestManager的load()函数用于获取GenericRequestBuilder对象(其实是子类对象)
  • load()内部调用loadGeneric()方法,将requestTracker对象引用传递给GenericRequestBuilder类
  • load()实际调用GenericRequestBuilder.load()方法完成request的URL设置
  1. GenericRequestBuilder的into()方法是实际产生request和消费request的地方。
  • GenericRequestBuilder的into(target)方法调用obtainReauest()获取到GenericRequest对象request,request与target相互绑定并被requestTracker维护。

3.1 GenericRequestBuilder的into(target)方法

public <Y extends Target<TranscodeType>> Y into(Y target) {
    Util.assertMainThread();
    if (target == null) {
      throw new IllegalArgumentException("You must pass in a non null Target");
    }
    if (!isModelSet) {
      throw new IllegalArgumentException("You must first set a model (try #load())");
    }
  
    //因为target和request是相互绑定的,所以考虑到复用的情景时,可以先获取一下request
    Request previous = target.getRequest();
   
    //previous != null说明target有复用,需要释放之前绑定的资源
    //注意:request内部是绑定了资源的,这里还没有讲到,先知道这回事,后面会讲
    if (previous != null) {
        //释放资源,防内存泄漏
        //这段代码是精华,需要好好体会:
        //Glidek肯定支持view的复用(对吧?),那么复用的view资源是如何绑定和释放的?
        //这里就是资源释放的地方(入口),资源在何时绑定会在后续的课程讲到。
        previous.clear();
        requestTracker.removeRequest(previous);
        previous.recycle();
    }
   
    //获取需求对象(对象绑定了target)
    Request request = buildRequest(target);
    //target绑定需求
    target.setRequest(request);
    //因为target具有生命周期,即实现了LifecycleListener方法,所以将其注册给ActivityFragmentLifecycle统一管理(不知道是啥的去看上一篇文章)
    lifecycle.addListener(target);
    //将需求加入队列并执行需求
    //注意是单线程
    requestTracker.runRequest(request);
  
    return target;
}

3.2 request的生命周期管理

  • 根据上文得知,request都被加入到requestTracker中来管理
  • requestTracker由RequestManager创建和管理
  • RequestManager具有生命周期

3.2.1 RequestManager

下面让我们看看RequestManager在各个生命周期回调里都做了什么

@Override
public void onStart() {
    resumeRequests();
}
 
@Override
public void onStop() {
    pauseRequests();
}
 
@Override
public void onDestroy() {
    requestTracker.clearRequests();
}
 
public void pauseRequests() {
    Util.assertMainThread();
    requestTracker.pauseRequests();
}
 
public void resumeRequests() {
    Util.assertMainThread();
    requestTracker.resumeRequests();
}
//下面的两个回调其实和request的关系没有那么直接,先放在这里留个印象    
public void onTrimMemory(int level) {
    glide.trimMemory(level);
}
 
public void onLowMemory() {
    glide.clearMemory();
}

代码很清楚了吧。细心的同学可能注意到了onTrimMemory(int level)和onLowMemory(),这俩货是系统在资源不足时调用的,说白了就是释放内存,具体怎么搞得,后续文章会专门讲到Glide的内存管理机制(也是精华)

3.2.2 requestTracker

最后让我们看看requestTracker都干啥了吧

public class RequestTracker {
    private final Set<Request> requests = Collections.newSetFromMap(new WeakHashMap<Request, Boolean>());
  
    @SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
    private final List<Request> pendingRequests = new ArrayList<Request>();
  
    private boolean isPaused;
  
    /**
    * Starts tracking the given request.
    */
    public void runRequest(Request request) {
        requests.add(request);
        if (!isPaused) {
            request.begin();
        } else {
            pendingRequests.add(request);
        }
    }
  
    // Visible for testing.
    void addRequest(Request request) {
        requests.add(request);
    }
  
    /**
    * Stops tracking the given request.
    */
    public void removeRequest(Request request) {
        requests.remove(request);
        pendingRequests.remove(request);
    }
   
    /**
    * Returns {@code true} if requests are currently paused, and {@code false} otherwise.
    */
    public boolean isPaused() {
        return isPaused;
    }
   
    /**
    * Stops any in progress requests.
    */
    public void pauseRequests() {
        isPaused = true;
        for (Request request : Util.getSnapshot(requests)) {
            if (request.isRunning()) {
                request.pause();
                pendingRequests.add(request);
            }
        }
    }
  
    /**
    * Starts any not yet completed or failed requests.
    */
    public void resumeRequests() {
        isPaused = false;
        for (Request request : Util.getSnapshot(requests)) {
            if (!request.isComplete() && !request.isCancelled() && !request.isRunning()) {
            request.begin();
            }
        }
        pendingRequests.clear();
    }
  
    /**
    * Cancels all requests and clears their resources.
    */
    public void clearRequests() {
        for (Request request : Util.getSnapshot(requests)) {
            request.clear();
        }
        pendingRequests.clear();
    }
  
    /**
    * Restarts failed requests and cancels and restarts in progress requests.
    */
    public void restartRequests() {
      for (Request request : Util.getSnapshot(requests)) {
          if (!request.isComplete() && !request.isCancelled()) {
              // Ensure the request will be restarted in onResume.
              request.pause();
              if (!isPaused) {
                  request.begin();
              } else {
                  pendingRequests.add(request);
              }
           }
        }
    }
}

(本篇是Glide框架及源码解析的第二篇,更多文章敬请关注后续文章。版权归作者所有,如有转发,请注明文章出处:原文链接

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

推荐阅读更多精彩内容