Glide4.9圖片框架源碼(二)之如何綁定Activity的生命周期

上一節(jié)我們簡單的介紹了Glide的常規(guī)使用方法若锁,有需要的話可以看看上一節(jié):

Glide框架之加載圖片的常規(guī)使用方式

Glide.with(this).load(url).into(imageView)

標題這一句代碼赞季,囊括了整個Glide的核心功能,可以說Glide在這一行代碼中做了成噸的工作,最繁重的任務是在into方法中略贮,但是我們根據(jù)順序掉房,首先看一下with方法。說到Glide源碼颤芬,面試中大家可能都知道Glide為何能監(jiān)聽頁面或者application的生命周期悲幅,從而及時的取消請求和回收對象套鹅,是通過綁定一個空的fragment。那么我們就來看一下汰具,with方法中卓鹿,是如何實現(xiàn)這個操作的。

Glide.with(this)方法

    @NonNull
    public static RequestManager with(@NonNull Context context) {
        return getRetriever(context).get(context);
    }

    @NonNull
    public static RequestManager with(@NonNull Activity activity) {
        return getRetriever(activity).get(activity);
    }

    @NonNull
    public static RequestManager with(@NonNull FragmentActivity activity) {
        return getRetriever(activity).get(activity);
    }

    @NonNull
    public static RequestManager with(@NonNull Fragment fragment) {
        return getRetriever(fragment.getActivity()).get(fragment);
    }

    /** @deprecated */
    @Deprecated
    @NonNull
    public static RequestManager with(@NonNull android.app.Fragment fragment) {
        return getRetriever(fragment.getActivity()).get(fragment);
    }

    @NonNull
    public static RequestManager with(@NonNull View view) {
        return getRetriever(view.getContext()).get(view);
    }

glide提供的with方法比較多留荔,其實這里看的出來吟孙,不管是傳入context還是activity亦或是fragment,其實還是拿到當前頁面所屬的context聚蝶,那么這里是情況其實只有兩種杰妓,一種是普通的context,另一種這是applicationcontext碘勉。我們以傳入context為例巷挥,調(diào)用的是return getRetriever(context).get(context),返回值則是一個RequestManager验靡,我們跟進去看看getRetriever(context)方法:

    @NonNull
    private static RequestManagerRetriever getRetriever(@Nullable Context context) {
        Preconditions.checkNotNull(context, "You cannot start a load on a not yet attached View or a Fragment 
          where getActivity() returns null (which usually occurs when getActivity() is called 
          before the Fragment is attached or after the Fragment is destroyed).");
        return get(context).getRequestManagerRetriever();
    }

checkNotNull方法執(zhí)行的是context的空檢查倍宾,我們繼續(xù)看一下get(context).getRequestManagerRetriever()中的get(context)

 @NonNull
    public static Glide get(@NonNull Context context) {
        if (glide == null) {
            Class var1 = Glide.class;
            synchronized(Glide.class) {
                if (glide == null) {
                    checkAndInitializeGlide(context);
                }
            }
        }
        return glide;
    }

這里get方法是獲取glide實例,實現(xiàn)的一個單例方法胜嗓,其中checkAndInitializeGlide對glide進行初始化高职,這里我們不去細究回到上一步繼續(xù)看看get(context).getRequestManagerRetriever()的getRequestManagerRetriever()方法

    @NonNull
    public RequestManagerRetriever getRequestManagerRetriever() {
        return this.requestManagerRetriever;
    }

這里直接返回的是RequestManagerRetriever,那么這個變量是什么時候初始化的呢兼蕊,我們看下Glide的build方法發(fā)現(xiàn)這里初始化了RequestManagerRetriever初厚。到這里with方法中的getRetriever(context).get(context)的getRetriever結(jié)束,我們繼續(xù)看看get(context)孙技,這里應該返回了一個RequestManagerRetriever里面的RequestManager产禾,來看下源碼:

 @NonNull
    public RequestManager get(@NonNull Context context) {
        if (context == null) {
            throw new IllegalArgumentException("You cannot start a load on a null Context");
        } else {
            if (Util.isOnMainThread() && !(context instanceof Application)) {
                if (context instanceof FragmentActivity) {
                    return this.get((FragmentActivity)context);
                }
                if (context instanceof Activity) {
                    return this.get((Activity)context);
                }
                if (context instanceof ContextWrapper) {
                    return this.get(((ContextWrapper)context).getBaseContext());
                }
            }
            return this.getApplicationManager(context);
        }
    }

這里看出將context分成了兩種類型,一種是context instanceof Application牵啦,另一種則是普通context亚情。先看看如果是普通的context,這里FragmentActivity哈雏、Activity其實差不多楞件,內(nèi)部創(chuàng)建的fragment支持的類型不同。如果是ContextWrapper類型則繼續(xù)取到baseContext裳瘪,遞歸調(diào)用get(context)土浸。那么這里我們看看Activity的場景的源碼this.get((Activity)context):

@NonNull
    public RequestManager get(@NonNull Activity activity) {
        if (Util.isOnBackgroundThread()) {
            return this.get(activity.getApplicationContext());
        } else {
            assertNotDestroyed(activity);
            FragmentManager fm = activity.getFragmentManager();
            return this.fragmentGet(activity, fm, (android.app.Fragment)null, isActivityVisible(activity));
        }
    }

這里的if判斷表示如果當前程序是在后臺運行,那么傳入getApplicationContext去get RequestManager 彭羹,這里ApplicationContext的情況我們等會兒單獨再講黄伊,繼續(xù)看下面的代碼,我們看到activity.getFragmentManager()派殷,獲取當前activity的FragmentManager还最,然后調(diào)用了fragmentGet方法墓阀,那么繼續(xù)看看這個方法的源碼:

   /** @deprecated */
    @Deprecated
    @NonNull
    private RequestManager fragmentGet(@NonNull Context context, @NonNull FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) {
        RequestManagerFragment current = this.getRequestManagerFragment(fm, parentHint, isParentVisible);
        RequestManager requestManager = current.getRequestManager();
        if (requestManager == null) {
            Glide glide = Glide.get(context);
            requestManager = this.factory.build(glide, current.getGlideLifecycle(), current.getRequestManagerTreeNode(), context);
            current.setRequestManager(requestManager);
        }
        return requestManager;
    }

重點來了,我們看到第一行返回了一個RequestManagerFragment 拓轻,我們跟進去看看這個fragment是怎么創(chuàng)建的:

  @NonNull
    private RequestManagerFragment getRequestManagerFragment(@NonNull FragmentManager fm, @Nullable android.app.Fragment parentHint, boolean isParentVisible) {
        RequestManagerFragment current = (RequestManagerFragment)fm.findFragmentByTag("com.bumptech.glide.manager");
        if (current == null) {
            current = (RequestManagerFragment)this.pendingRequestManagerFragments.get(fm);
            if (current == null) {
                current = new RequestManagerFragment();
                current.setParentFragmentHint(parentHint);
                if (isParentVisible) {
                    current.getGlideLifecycle().onStart();
                }
                this.pendingRequestManagerFragments.put(fm, current);
                fm.beginTransaction().add(current, "com.bumptech.glide.manager").commitAllowingStateLoss();
                this.handler.obtainMessage(1, fm).sendToTarget();
            }
        }
        return current;
    }

這里我們先通過pendingRequestManagerFragments從緩存中去拿RequestManagerFragment 斯撮,這里的pendingRequestManagerFragment就是一個hashmap,Map<FragmentManager, RequestManagerFragment>扶叉,如果緩存中沒有勿锅,那么去new一個fragment并且將其添加到緩存中,重點來了枣氧, fm.beginTransaction().add(current, "com.bumptech.glide.manager").commitAllowingStateLoss();這里便將一個沒有ui的fragment添加到了context對應的activity上粱甫。回到前面我們提到的applicationcontext作瞄,看看這種情況茶宵,調(diào)用的是getApplicationManager:

 @NonNull
    private RequestManager getApplicationManager(@NonNull Context context) {
        if (this.applicationManager == null) {
            synchronized(this) {
                if (this.applicationManager == null) {
                    Glide glide = Glide.get(context.getApplicationContext());
                    this.applicationManager = this.factory.build(glide, new ApplicationLifecycle(), new EmptyRequestManagerTreeNode(), context.getApplicationContext());
                }
            }
        }
        return this.applicationManager;
    }

這里我們直接看factory的build方法,跟進去看一源碼:

private static final RequestManagerFactory DEFAULT_FACTORY = new RequestManagerFactory() {
    @NonNull
    @Override
    public RequestManager build(@NonNull Glide glide, @NonNull Lifecycle lifecycle,
        @NonNull RequestManagerTreeNode requestManagerTreeNode, @NonNull Context context) {
      return new RequestManager(glide, lifecycle, requestManagerTreeNode, context);
    }
  };

這里根據(jù)傳入applicationContext去創(chuàng)建一個RequestManager并返回宗挥,到這里整個with方法就結(jié)束了乌庶,我們再看看fragment對應的生命周期方法中做了什么:

@Override
  public void onDetach() {
    super.onDetach();
    unregisterFragmentWithRoot();
  }

  @Override
  public void onStart() {
    super.onStart();
    lifecycle.onStart();
  }

  @Override
  public void onStop() {
    super.onStop();
    lifecycle.onStop();
  }

  @Override
  public void onDestroy() {
    super.onDestroy();
    lifecycle.onDestroy();
    unregisterFragmentWithRoot();
  }

這里可以看出,當activity觸發(fā)生命周期的時候契耿,當前無UI的fragment也會觸發(fā)相應的生命周期方法瞒大,那么這里的lifecycle調(diào)用到了哪里呢,跟進去發(fā)現(xiàn)調(diào)用的是ActivityFragmentLifecycle實現(xiàn)的幾個方法:

void onStart() {
    isStarted = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStart();
    }
  }

  void onStop() {
    isStarted = false;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onStop();
    }
  }

  void onDestroy() {
    isDestroyed = true;
    for (LifecycleListener lifecycleListener : Util.getSnapshot(lifecycleListeners)) {
      lifecycleListener.onDestroy();
    }
  }

這里的lifecycleListeners存儲了前面添加的多個生命周期的監(jiān)聽搪桂,在這里全部觸發(fā)透敌,那么這里我們之前添加的
lifecycleListener 包括了RequestManager中的,我們在RequestManager創(chuàng)建的時候就已經(jīng)添加了一個listener到lifecycleListeners中踢械,所以這里的onStart酗电、onStop、onDestroy會調(diào)用RequestManager里面的對應方法内列,RequestManager作為一個管理類撵术,管理了兩個重要的對象,一個是target话瞧,另一個是request嫩与,因此RequestManager通過監(jiān)聽生命周期方法,同時控制了target和request的加載情況交排,我們來看下代碼:

  @Override
  public synchronized void onStart() {
    resumeRequests();
    targetTracker.onStart();
  }

  @Override
  public synchronized void onStop() {
    pauseRequests();
    targetTracker.onStop();
  }

  @Override
  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);
  }

可以看到划滋,在對應的生命周期方法中控制了targetTracker和requestTracker,這兩個對象則分別控制這target和request的生命周期埃篓。到這里我們的width方法源碼流程就結(jié)束了处坪。

RequestManager的load(url)方法

上面我們分析的是Glide.with(this).load(url).into(imageView)中的with方法,那么我們繼續(xù)看load,width返回的是RequestManager稻薇,那么自然load方法在RequestManager中,我們看下源碼:

  @Override
public RequestBuilder<Drawable> load(@Nullable Bitmap bitmap) {
    return asDrawable().load(bitmap);
  }

  @Override
  public RequestBuilder<Drawable> load(@Nullable Drawable drawable) {
    return asDrawable().load(drawable);
  }

  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

  @Override
  public RequestBuilder<Drawable> load(@Nullable Uri uri) {
    return asDrawable().load(uri);
  }

  @Override
  public RequestBuilder<Drawable> load(@Nullable File file) {
    return asDrawable().load(file);
  }

  @Override
  public RequestBuilder<Drawable> load(@RawRes @DrawableRes @Nullable Integer resourceId) {
    return asDrawable().load(resourceId);
  }

  @Override
  public RequestBuilder<Drawable> load(@Nullable URL url) {
    return asDrawable().load(url);
  }

  @Override
  public RequestBuilder<Drawable> load(@Nullable byte[] model) {
    return asDrawable().load(model);
  }

  @Override
  public RequestBuilder<Drawable> load(@Nullable Object model) {
    return asDrawable().load(model);
  }

glide提供的load方法極多胶征,涵蓋了大多數(shù)的圖片加載資源塞椎,例如字節(jié)碼,URL睛低,Drawable案狠,文件,bitmap钱雷,字符串的圖片地址等等骂铁,這里我們就以常用的字符串的圖片地址為例看下代碼:

  @Override
  public RequestBuilder<Drawable> load(@Nullable String string) {
    return asDrawable().load(string);
  }

  @NonNull
  public RequestBuilder<Drawable> asDrawable() {
    return as(Drawable.class);
  }

  @NonNull
  public <ResourceType> RequestBuilder<ResourceType> as(
      @NonNull Class<ResourceType> resourceClass) {
    return new RequestBuilder<>(glide, this, resourceClass, context);
  }

  @Override
  @CheckResult
  public RequestBuilder<TranscodeType> load(@Nullable String string) {
    return loadGeneric(string);
  }

  @NonNull
  private RequestBuilder<TranscodeType> loadGeneric(@Nullable Object model) {
    this.model = model;
    isModelSet = true;
    return this;
  }

我們先看asDrawable方法,其實就是設置了圖片資源的類型罩抗,然后創(chuàng)建了一個RequestBuilder對象拉庵,然后傳入一個String類型并且調(diào)用load方法,這里是將我們的String URL設置到了model 對象中套蒂,并沒有開始請求钞支,所以我們的重點任務就放在了into方法中,它包括了獲取內(nèi)存緩存操刀,獲取磁盤緩存烁挟,請求,寫入內(nèi)存和磁盤緩存等許多操作骨坑,那么我們下一節(jié)再繼續(xù)分析最重要的一步into方法吧~

總結(jié)

首先通過width方法中的getRetriever方法撼嗓,完成Glide的初始化并且獲取到RequestManagerRetriever,RequestManagerRetriever主要用于管理和生成RequestManager欢唾,然后通過RequestManagerRetriever的get方法為activity創(chuàng)建一個無UI的fragment且警,并且綁定到當前activity,然后生成一個RequestManager并且與之關(guān)聯(lián)生命周期礁遣,當activity的生命周期發(fā)生改變時振湾,通知綁定的fragment,繼而通知到RequestManager的監(jiān)聽方法亡脸,從而控制對target和request的加載押搪、暫停和銷毀。

下一節(jié)我們講講into(imageview)中的內(nèi)存緩存:

Glide源碼之into方法后續(xù)讀取內(nèi)存緩存

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浅碾,一起剝皮案震驚了整個濱河市大州,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌垂谢,老刑警劉巖厦画,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡根暑,警方通過查閱死者的電腦和手機力试,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來排嫌,“玉大人畸裳,你說我怎么就攤上這事〈镜兀” “怎么了怖糊?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長颇象。 經(jīng)常有香客問我伍伤,道長,這世上最難降的妖魔是什么遣钳? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任扰魂,我火速辦了婚禮,結(jié)果婚禮上蕴茴,老公的妹妹穿的比我還像新娘阅爽。我一直安慰自己,他們只是感情好荐开,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布付翁。 她就那樣靜靜地躺著,像睡著了一般晃听。 火紅的嫁衣襯著肌膚如雪百侧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天能扒,我揣著相機與錄音佣渴,去河邊找鬼。 笑死初斑,一個胖子當著我的面吹牛辛润,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播见秤,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼砂竖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了鹃答?” 一聲冷哼從身側(cè)響起乎澄,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎测摔,沒想到半個月后置济,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體解恰,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年浙于,在試婚紗的時候發(fā)現(xiàn)自己被綠了护盈。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡羞酗,死狀恐怖腐宋,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情整慎,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布围苫,位于F島的核電站裤园,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏剂府。R本人自食惡果不足惜拧揽,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望腺占。 院中可真熱鬧淤袜,春花似錦、人聲如沸衰伯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽意鲸。三九已至烦周,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間怎顾,已是汗流浹背读慎。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留槐雾,地道東北人夭委。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像募强,于是被迫代替她去往敵國和親株灸。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345