每日一題:Glide源碼

****每日一題****: Glide

glide圖片緩存
glide源碼分享

面試率: ★★★☆☆

面試技巧與建議

圖庫(kù)在Android實(shí)際項(xiàng)目中基本上都會(huì)使用到,這種概率跟網(wǎng)絡(luò)庫(kù)幾乎一樣的,因此出去面試前準(zhǔn)備好一套圖庫(kù)面試技巧是不可避免的.

面試建議

在最近的面試中很多應(yīng)聘者都存在如下幾個(gè)問(wèn)題:

  • 基本都使用/了解過(guò)
    你去面試一家公司,別人問(wèn)這個(gè)問(wèn)題你說(shuō)使用過(guò),但是面試官一天面試7-8個(gè)人,其他應(yīng)聘者也會(huì)使用,那么你們區(qū)別在哪里?

  • 不知道其底層網(wǎng)絡(luò)協(xié)議
    一個(gè)網(wǎng)路底層使用socket還是http這個(gè)是最基本的了解.

  • 不知道為什么使用它
    圖庫(kù)有很多種如Fresco,Picasso,imgLoader等,然而為什么使用Glide呢,你是否知道他的特點(diǎn)?

  • 不知道如何封裝
    使用過(guò)那如何使用?做了什么封裝?如何自定義Glide?

  • 在什么地方用過(guò)
    項(xiàng)目中哪個(gè)模塊使用過(guò)這個(gè)庫(kù)呢,是全局使用,還是局部使用?

對(duì)于上述問(wèn)題可以了解并答出,才能證明你有這方面相關(guān)開(kāi)發(fā)經(jīng)驗(yàn),否則只能說(shuō)你只是看過(guò)Glide.

面試技巧

一般開(kāi)發(fā)中迭代速度比較快的情況下,不會(huì)有很多時(shí)間去深入的探討開(kāi)源庫(kù)中的全部源碼,但是如果跟業(yè)務(wù)有關(guān)聯(lián)的話,我們才會(huì)去做研究,而研究的也是全部源碼中的某塊功能或者模塊.

  • Glide Module自定義緩存
    圖片框架中很多自定義的實(shí)現(xiàn),而緩存也是框架中最為常見(jiàn)的行為之一。因此在開(kāi)發(fā)使用中有些項(xiàng)目需求不凡試下自定義Glide的緩存.

下面是一篇自定義Glide的緩的文章:
Glide Module

面試題

下面是我從源碼中提取出來(lái)的一些問(wèn)題,簡(jiǎn)單而實(shí)用.

你為什么使用Glide?

Glide特點(diǎn)

  • 使用簡(jiǎn)單
  • 可配置度高呆盖,自適應(yīng)程度高
  • 支持常見(jiàn)圖片格式Jpg png gif webp
  • 支持多種數(shù)據(jù)源網(wǎng)絡(luò)拖云、本地、資源应又、Assets 等
  • 高效緩存策略支持Memory和Disk圖片緩存 默認(rèn)Bitmap格式采用RGB_565內(nèi)存使用至少減少一半
  • 生命周期集成根據(jù)Activity/Fragment生命周期自動(dòng)管理請(qǐng)求
  • 高效處理Bitmap使用Bitmap Pool使Bitmap復(fù)用宙项,主動(dòng)調(diào)用recycle回收需要回收的Bitmap,減小系統(tǒng)回收壓力

Glide是如何提高加載圖片的性能株扛?

總共有兩點(diǎn):

通過(guò)控制圖片大小.

  • .override(200,200)可以設(shè)置加載圖片大小尤筐,但是實(shí)際大小不一定是200x200,通過(guò)源碼分析下:
    在BitmapRequestBuilder中的private Downsampler downsampler = Downsampler.AT_LEAST默認(rèn)就是設(shè)置了尺寸優(yōu)化,超過(guò)最大比例的就會(huì)對(duì)圖片進(jìn)行等比例縮放,如何縮放見(jiàn)下面;

  • 在Downsampler中的decode方法中汇荐,獲取的Bitmap大小變成1/sampleSize,倍數(shù)通過(guò)getSampleSize計(jì)算所得盆繁,

  • inSampleSize 是 BitmapFactory.Options的屬性掀淘,應(yīng)該大家都知道。然后再看看怎么生成.override(200,200)油昂,如果沒(méi)有設(shè)置Glide默認(rèn)是FitCenter革娄,查看FitCenter可以看到圖片截取方式。

  • 舉個(gè)例子:
    加載的圖片大小為1080x540冕碟,如果使用了.override(200,200)默認(rèn)緩存一張200x100的圖片,也就是默認(rèn)存儲(chǔ)結(jié)果RESULT.

通過(guò)設(shè)置Bitmap Format的圖片類型

為了降低內(nèi)存消耗拦惋,Glide默認(rèn)配置的Bitmap Format 為 RGB_565,修改GlideBuilder's setDecodeFormat設(shè)置.

 builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

Glide為何要使用額外的無(wú)界面的Fragment/activity鸣哀?

根據(jù)傳入Context的類型有不同的實(shí)現(xiàn)架忌,這里以FragmentActivity為例(現(xiàn)在常用的MD樣式Activity類AppCompatActivity是FragmentActivity的子類)。方法get(FragmentActivity activity)調(diào)用了方法supportFragmentGet(activity, fm)我衬,后者返回的對(duì)象類型是SupportRequestManagerFragment 叹放。SupportRequestManagerFragment 是一個(gè)無(wú)界面的Fragment類,起到把請(qǐng)求和Activity生命周期同步的作用挠羔。

Glide.with() 不僅僅只是Context還可以是Activity,Fragment等井仰,傳入后自動(dòng)適配,Glide加載圖片是會(huì)隨著Activity,Fragment 的生命周期破加,具體可以參考LifecycleListener俱恶,所以推薦使用Activity,Fragment.

Glide的緩存策略是怎么做的?

通過(guò)該方法設(shè)置策略.diskCacheStrategy(DiskCacheStrategy.ALL)
DiskCacheStrategy 分別有以下幾種選擇,ALL緩存原圖和截取后的圖范舀,NONE 不緩存合是,SOURCE 只緩存原圖,RESULT緩存截取后的圖.

因此如果圖片需要分享或需要原圖的建議緩存ALL锭环,否則只緩存RESULT.

Glide的核心GlideModule的作用是什么?

  • GlideModule是對(duì)glide全局配置相關(guān)的類.
  • 如可以設(shè)置緩存策略.
  • 更多的配置如下


    img-w400
  • 可以通過(guò)實(shí)現(xiàn)GlideModule接口來(lái)自定義一個(gè)Glide圖庫(kù),可以通過(guò)他改變Glide的行為和基礎(chǔ)配置.

在自定義GlideModule時(shí)需要注意什么?

要全局的去聲明這個(gè)類聪全,讓 Glide 知道它應(yīng)該在哪里被加載和使用。Glide 會(huì)掃描 AndroidManifest.xml 為 Glide module 的 meta 聲明辅辩。具體可以查看源碼在Glide.get(Context),通過(guò)ManifestParser對(duì)象獲取GlideModule集合.
因此难礼,你必須在 AndroidManifest.xml 的 < application> 標(biāo)簽內(nèi)去聲明這個(gè)剛剛創(chuàng)建的 Glide module。

  1. 創(chuàng)建GlideModel
public class MyGlideModule implements GlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {

  }
  @Override
  public void registerComponents(Context context, Glide glide) {

  }
}
  1. 在AndroidManifest.xml的meta-data配置GlideModule
<meta-data android:name="com.branch.glidedemo.MyGlideModule"
           android:value="GlideModule"/>
  1. 解決GlideModel沖突有可能加入的library中也同樣配置了GlideModule,如果配置了多個(gè)會(huì)出現(xiàn)沖突玫锋,無(wú)法編譯運(yùn)行蛾茉,解決方式可在AndroidManifest.xml移除
<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”remove” />

實(shí)際開(kāi)發(fā)中的一些問(wèn)題

為什么 有的圖片第一次加載的時(shí)候只顯示占位圖,第二次才顯示正常的圖片呢撩鹿?

如果你剛好使用了這個(gè)圓形Imageview庫(kù)或者其他的一些自定義的圓形Imageview谦炬,而你又剛好設(shè)置了占位的話,那么节沦,你就會(huì)遇到第一個(gè)問(wèn)題吧寺。如何解決呢窜管?方案一: 不設(shè)置占位;方案二:使用Glide的Transformation API自定義圓形Bitmap的轉(zhuǎn)換稚机。這里是一個(gè)已有的例子幕帆;方案三:使用下面的代碼加載圖片:

Glide.with(mContext)
    .load(url) 
    .placeholder(R.drawable.loading_spinner)
    .into(new SimpleTarget<Bitmap>(width, height) {
        @Override 
        public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
            // setImageBitmap(bitmap) on CircleImageView 
        } 
    });

該方法在listview上復(fù)用有問(wèn)題的bug,如果在listview中加載CircleImageView,請(qǐng)不要使用該方法赖条。

方案四:不使用Glide的默認(rèn)動(dòng)畫(huà):

Glide.with(mContext)
    .load(url) 
    .dontAnimate()
    .placeholder(R.drawable.loading_spinner)
    .into(circleImageview);

為什么 我總會(huì)得到類似You cannot start a load for a destroyed activity這樣的異常呢失乾?

請(qǐng)記住一句話:不要再非主線程里面使用Glide加載圖片,如果真的使用了纬乍,請(qǐng)把context參數(shù)換成getApplicationContext碱茁。

提示:這個(gè)問(wèn)題主要是context對(duì)應(yīng)的生命周期引起的.當(dāng) Glide 檢測(cè)到 Activity 被銷毀時(shí),會(huì)自動(dòng)取消等待中的請(qǐng)求.如果你傳遞的是一個(gè)
getApplicationContext,Glide就不能對(duì)其進(jìn)行優(yōu)化,所以適當(dāng)選擇仿贬。

為什么 我不能給加載的圖片setTag()呢纽竣?

首先Glide的全局tag只是為了保證你可以正常的使用view.setTag方法,和錯(cuò)位沒(méi)關(guān)系的茧泪。因?yàn)镚lide內(nèi)部就是通過(guò)view.setTag來(lái)保證不錯(cuò)位的.
進(jìn)一步說(shuō)明:
當(dāng)圖片從ListView 中移出屏幕時(shí)蜓氨,Glide 也會(huì)取消其對(duì)應(yīng)的請(qǐng)求。
由于大多數(shù)開(kāi)發(fā)者在 adapter 中重用 View队伟,Glide 會(huì)給在請(qǐng)求數(shù)據(jù)時(shí)給對(duì)應(yīng)的ImageView 附加一個(gè) tag穴吹,然后再載入其他圖片時(shí)檢查這個(gè) tag,如果存在的話取消第一個(gè)請(qǐng)求,進(jìn)而形成的優(yōu)化嗜侮。

使用setTag(int,object)方法設(shè)置tag,具體用法如下:Java代碼是醬紫的:

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(imageViewHolder.image);
        imageViewHolder.image.setTag(R.id.image_tag, i);
        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
                int position = (int) v.getTag(R.id.image_tag);
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });

同時(shí)在values文件夾下新建ids.xml港令,添加
<item name="image_tag" type="id"/>

方案二:從Glide的3.6.0之后,新添加了全局設(shè)置的方法锈颗。具體方法如下:先實(shí)現(xiàn)GlideMoudle接口顷霹,全局設(shè)置ViewTaget的tagId:

public class MyGlideMoudle implements GlideModule{
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        ViewTarget.setTagId(R.id.glide_tag_id);
    }

    @Override
    public void registerComponents(Context context, Glide glide) {

    }
}

同樣,也需要在ids.xml下添加id
<item name="glide_tag_id" type="id"/>
最后在AndroidManifest.xml文件里面添加

<meta-data
    android:name="com.yourpackagename.MyGlideMoudle"
    android:value="GlideModule" />

方案三:寫(xiě)一個(gè)繼承自ImageViewTaget的類击吱,復(fù)寫(xiě)它的get/setRequest方法

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(new ImageViewTarget<GlideDrawable>(imageViewHolder.image) {
            @Override
            protected void setResource(GlideDrawable resource) {
                imageViewHolder.image.setImageDrawable(resource);
            }

            @Override
            public void setRequest(Request request) {
                imageViewHolder.image.setTag(i);
                imageViewHolder.image.setTag(R.id.glide_tag_id,request);
            }

            @Override
            public Request getRequest() {
                return (Request) imageViewHolder.image.getTag(R.id.glide_tag_id);
            }
        });

        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = (int) v.getTag();
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });

Glide常見(jiàn)錯(cuò)誤

java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
解決辦法 在使用Glide的那段代碼加是否在主線程判斷

if(Util.isOnMainThread()) {
Glide.with(MyActivity.this).load(strURL).into(imageView);
}

在onDestory加

@Override
protected void onDestroy() {
   super.onDestroy();
   if(Util.isOnMainThread()) {
       Glide.with(this).pauseRequest();
   }

并且所有的this 都要寫(xiě)成getApplicationContext ,這個(gè)主要針對(duì)于在子線程使用Glide.

Glide的已經(jīng)寫(xiě)的不錯(cuò)了****,****有沒(méi)對(duì)他做些進(jìn)階的優(yōu)化/使用?

  1. Glide.with(context).resumeRequests()和 Glide.with(context).pauseRequests()
    當(dāng)列表在滑動(dòng)的時(shí)候淋淀,調(diào)用pauseRequests()取消請(qǐng)求,滑動(dòng)停止時(shí)姨拥,調(diào)用resumeRequests()恢復(fù)請(qǐng)求绅喉。這樣是不是會(huì)好些呢渠鸽?

  2. Glide.clear()
    當(dāng)你想清除掉所有的圖片加載請(qǐng)求時(shí)叫乌,這個(gè)方法可以幫助到你。

  3. ListPreloader
    如果你想讓列表預(yù)加載的話徽缚,不妨試一下ListPreloader這個(gè)類憨奸。

可以提升圖片加載速度。首先是在圖片展示前預(yù)讀取數(shù)據(jù)凿试,
它提供了一個(gè) ListPreloader排宰,通過(guò)預(yù)加載 item 的數(shù)量初始化似芝。接著通過(guò)
setOnScrollListener(OnScrollListener) 把 ListPreloader 設(shè)置給 ListView。如果你想在 ListView 之外預(yù)載圖片板甘,只要調(diào)用上面 DrawableRequestBuilder 對(duì)象的 downloadOnly() 方法就好党瓮,像這樣 builder.downloadOnly();

demo代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市盐类,隨后出現(xiàn)的幾起案子寞奸,更是在濱河造成了極大的恐慌,老刑警劉巖在跳,帶你破解...
    沈念sama閱讀 219,039評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件枪萄,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡猫妙,警方通過(guò)查閱死者的電腦和手機(jī)瓷翻,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)割坠,“玉大人齐帚,你說(shuō)我怎么就攤上這事【录梗” “怎么了童谒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,417評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)沪羔。 經(jīng)常有香客問(wèn)我饥伊,道長(zhǎng),這世上最難降的妖魔是什么蔫饰? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,868評(píng)論 1 295
  • 正文 為了忘掉前任琅豆,我火速辦了婚禮,結(jié)果婚禮上篓吁,老公的妹妹穿的比我還像新娘茫因。我一直安慰自己,他們只是感情好杖剪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布冻押。 她就那樣靜靜地躺著,像睡著了一般盛嘿。 火紅的嫁衣襯著肌膚如雪洛巢。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,692評(píng)論 1 305
  • 那天次兆,我揣著相機(jī)與錄音稿茉,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛漓库,可吹牛的內(nèi)容都是我干的恃慧。 我是一名探鬼主播,決...
    沈念sama閱讀 40,416評(píng)論 3 419
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼渺蒿,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼痢士!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起茂装,我...
    開(kāi)封第一講書(shū)人閱讀 39,326評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤良瞧,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后训唱,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體褥蚯,經(jīng)...
    沈念sama閱讀 45,782評(píng)論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評(píng)論 3 337
  • 正文 我和宋清朗相戀三年况增,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了赞庶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡澳骤,死狀恐怖歧强,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情为肮,我是刑警寧澤摊册,帶...
    沈念sama閱讀 35,790評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站颊艳,受9級(jí)特大地震影響茅特,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜棋枕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評(píng)論 3 331
  • 文/蒙蒙 一白修、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧重斑,春花似錦兵睛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,996評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至漾脂,卻和暖如春假颇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背符相。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,113評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工拆融, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人啊终。 一個(gè)月前我還...
    沈念sama閱讀 48,332評(píng)論 3 373
  • 正文 我出身青樓镜豹,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蓝牲。 傳聞我的和親對(duì)象是個(gè)殘疾皇子趟脂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容