每日一題:Glide源碼

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

glide圖片緩存
glide源碼分享

面試率: ★★★☆☆

面試技巧與建議

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

面試建議

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

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

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

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

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

  • 在什么地方用過
    項目中哪個模塊使用過這個庫呢,是全局使用,還是局部使用?

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

面試技巧

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

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

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

面試題

下面是我從源碼中提取出來的一些問題,簡單而實用.

你為什么使用Glide?

Glide特點

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

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

總共有兩點:

通過控制圖片大小.

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

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

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

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

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

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

 builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

Glide為何要使用額外的無界面的Fragment/activity?

根據(jù)傳入Context的類型有不同的實現(xiàn),這里以FragmentActivity為例(現(xiàn)在常用的MD樣式Activity類AppCompatActivity是FragmentActivity的子類)。方法get(FragmentActivity activity)調(diào)用了方法supportFragmentGet(activity, fm),后者返回的對象類型是SupportRequestManagerFragment 。SupportRequestManagerFragment 是一個無界面的Fragment類,起到把請求和Activity生命周期同步的作用。

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

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

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

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

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

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


    img-w400
  • 可以通過實現(xiàn)GlideModule接口來自定義一個Glide圖庫,可以通過他改變Glide的行為和基礎(chǔ)配置.

在自定義GlideModule時需要注意什么?

要全局的去聲明這個類,讓 Glide 知道它應(yīng)該在哪里被加載和使用。Glide 會掃描 AndroidManifest.xml 為 Glide module 的 meta 聲明。具體可以查看源碼在Glide.get(Context),通過ManifestParser對象獲取GlideModule集合.
因此,你必須在 AndroidManifest.xml 的 < application> 標簽內(nèi)去聲明這個剛剛創(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,如果配置了多個會出現(xiàn)沖突,無法編譯運行,解決方式可在AndroidManifest.xml移除
<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”remove” />

實際開發(fā)中的一些問題

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

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

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ù)用有問題的bug,如果在listview中加載CircleImageView,請不要使用該方法。

方案四:不使用Glide的默認動畫:

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

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

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

提示:這個問題主要是context對應(yīng)的生命周期引起的.當 Glide 檢測到 Activity 被銷毀時,會自動取消等待中的請求.如果你傳遞的是一個
getApplicationContext,Glide就不能對其進行優(yōu)化,所以適當選擇。

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

首先Glide的全局tag只是為了保證你可以正常的使用view.setTag方法,和錯位沒關(guān)系的。因為Glide內(nèi)部就是通過view.setTag來保證不錯位的.
進一步說明:
當圖片從ListView 中移出屏幕時,Glide 也會取消其對應(yīng)的請求。
由于大多數(shù)開發(fā)者在 adapter 中重用 View,Glide 會給在請求數(shù)據(jù)時給對應(yīng)的ImageView 附加一個 tag,然后再載入其他圖片時檢查這個 tag,如果存在的話取消第一個請求,進而形成的優(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();
            }
        });

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

方案二:從Glide的3.6.0之后,新添加了全局設(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" />

方案三:寫一個繼承自ImageViewTaget的類,復(fù)寫它的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常見錯誤

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 都要寫成getApplicationContext ,這個主要針對于在子線程使用Glide.

Glide的已經(jīng)寫的不錯了****,****有沒對他做些進階的優(yōu)化/使用?

  1. Glide.with(context).resumeRequests()和 Glide.with(context).pauseRequests()
    當列表在滑動的時候,調(diào)用pauseRequests()取消請求,滑動停止時,調(diào)用resumeRequests()恢復(fù)請求。這樣是不是會好些呢?

  2. Glide.clear()
    當你想清除掉所有的圖片加載請求時,這個方法可以幫助到你。

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

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

demo代碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容