1. 程式人生 > >圖片載入之Glide使用

圖片載入之Glide使用

一、簡介

在泰國舉行的谷歌開發者論壇上,谷歌為我們介紹了一個名叫Glide的圖片載入庫,作者是bumptech。這個庫被廣泛的運用在Google的開源專案中,包括2014Google I/O大會上釋出的官方App

Glide是一款由Bump Technologies開發的圖片載入框架,使得我們可以在Android平臺上以極度簡單的方式載入和展示圖片。Glide預設使用HttpUrlConnection進行網路請求,為了讓App保持一致的網路請求形式,可以讓Glide使用我們指定的網路請求形式請求網路資源。

二、依賴

1.jar

2.Gradle

dependencies {  
    compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'com.android.support:support-v4:23.3.0' }

三、許可權

<uses-permission android:name="android.permission.INTERNET" />

四、混淆

-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
    **[] $VALUES;
    public
*; }

五、使用

Glide.with(this).load(url).into(view);

Glide.with()方法用於建立一個載入圖片的例項。with()方法可以接收ContextActivityFragment或者FragmentActivity型別的引數,因此可供我們選擇的範圍非常廣。在ActivityFragment或者FragmentActivity中呼叫with()方法時都可以直接傳this,不在這些類中時可獲取當前應用程式的ApplicationContext傳入with()方法中。特別需要注意的是with()方法中傳入的例項會決定Glide載入圖片的生命週期,如果傳入的是Activity

Fragment或者FragmentActivity的例項,那麼當其被銷燬時圖片載入也會停止,如果傳入的是ApplicationContext時只有當應用程式被殺掉的時候圖片載入才會停止。

 Glide.with(Context context);// 繫結Context
 Glide.with(Activity activity);// 繫結Activity
 Glide.with(FragmentActivity activity);// 繫結FragmentActivity
 Glide.with(Fragment fragment);// 繫結Fragment

load()方法用於指定待載入的圖片資源。Glide支援載入各種各樣的圖片資源,包括網路圖片、本地圖片、應用資源、二進位制流、Uri物件等。

into()方法用於圖片顯示的對應ImageView

Glide支援載入gif圖片,其內部會自動判斷圖片格式,並且可以正確的將它解析並顯示出來。

使用Glide載入圖片不用擔心記憶體浪費,甚至是記憶體溢位的問題。因為Glide不會直接將圖片的完整尺寸全部載入到記憶體中,而是用多少載入多少。Glide會自動判斷ImageView的大小,然後只將這麼大的圖片畫素載入到記憶體當中,幫助我們節省記憶體開支。

六、方法

1.指定圖片格式

如果呼叫了.asBitmap()方法,則.load()中的引數指向的可以是一個靜態圖片也可以是GIF圖片,如果是一張GIF圖片,則載入之後只會展示GIF圖片的第一幀。

如果呼叫的.asGif()方法,則.load()方法中的引數指向的必須是一個GIF圖片,如果是一張靜態圖片,則圖片載入完成之後展示的只是圖片佔位符(如果沒有設定圖片佔位符,則什麼也不展示)。

//顯示靜態圖片(若載入的是gif圖那麼就會顯示第一幀的圖片)
.asBitmap()
//顯示動態圖片(若載入的是靜態圖會載入失敗)
.asGif()

2.指定佔位圖顯示

//載入時顯示的圖片
.placeholder(R.drawable.image_load)
//載入失敗時顯示的圖片
.error(R.drawable.image_error)

3.設定快取

//禁止記憶體快取
.skipMemoryCache(true)
//禁止磁碟快取(Glide預設快取策略是:DiskCacheStrategy.RESULT)
.diskCacheStrategy(DiskCacheStrategy.NONE)
//快取引數
//ALL:快取源資源和轉換後的資源(即快取所有版本影象,預設行為)
//NONE:不作任何磁碟快取,然而預設的它將仍然使用記憶體快取
//SOURCE:僅快取源資源(原來的全解析度的影象)
//RESULT:快取轉換後的資源(最終的影象,即降低解析度後的或者是轉換後的)

4.設定載入尺寸

以下方法可以設定圖片載入之後展示的寬度值和高度值,前提是目標ImageView的寬度和高度都設定為wrap_content

//載入圖片為100*100畫素的尺寸
.override(100, 100)

5.設定圖片縮放

如果呼叫了.centerCrop()方法,則顯示圖片的時候短的一邊填充容器,長的一邊跟隨縮放;如果呼叫了.fitCenter()方法,則顯示圖片的時候長的一邊填充容器,短的一邊跟隨縮放;這兩個方法可以都呼叫,如果都呼叫,則最終顯示的效果是後呼叫的方法展示的效果。

//它是一個裁剪技術,即縮放影象讓它填充到ImageView界限內並且裁剪額外的部分,ImageView可能會完全填充,但影象可能不會完整顯示
.centerCrop()
//它是一個裁剪技術,即縮放影象讓影象都測量出來等於或小於ImageView的邊界範圍,該影象將會完全顯示,但可能不會填滿整個ImageView
.fitCenter()

6.設定資源載入優先順序

.priority(Priority.HIGH)

7.設定圓角或圓形圖片

//圓角圖片
.transform(new GlideRoundTransform(this))
//圓形圖片
.transform(new GlideCircleTransform(this))

8.設定縮圖

設定以下方法後會先載入這張圖片的sizeMultiplier倍的縮圖到目標ImageView中,然後再慢慢載入完整的圖片,sizeMultiplier值的範圍是0~1

//方法一:係數需在(0,1)之間,0.5f為原圖的1/2,這樣會先載入縮圖然後在載入全圖
.thumbnail(0.5f)

//方法二:自定義資源圖片為縮圖
DrawableRequestBuilder<Integer> thumbnailRequest = Glide
        .with(context)
        .load(R.drawable.image_example);
Glide.with(context)
        .load(url)
        .thumbnail(thumbnailRequest)
        .into(view);

9.設定動畫

載入圖片時所展示的動畫,可以是Animator型別的屬性動畫,也可以是int型別的動畫資源。這個動畫只在第一次載入的時候會展示,以後都會從快取中獲取圖片,因此也就不會展示動畫了。

//設定載入動畫
.animate(R.anim.alpha_in)
//淡入淡出動畫,也是預設動畫,動畫預設的持續時間是300毫秒
.crossFade()
//移除所有動畫
.dontAnimate()

10.載入本地視訊(相當於一張縮圖)

//只能載入本地視訊(顯示的只是視訊的第一幀影象,相當於一張縮圖,不能播放視訊),網路視訊無法載入
String files = Environment.getExternalStorageDirectory().getAbsolutePath() + "/glide.avi";
Glide.with(this).load(files).into(view);

11.設定載入內容

//示例一
Glide.with(context).load(url).into(new SimpleTarget<GlideDrawable>() {
    @Override
    public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
        //圖片載入完成
        view.setImageDrawable(resource);
    }
});

//示例二
Glide.with(context).load(url).asBitmap().into(new SimpleTarget<Bitmap>() {
    @Override
    public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
        //圖片載入完成
        view.setImageBitmap(bitmap);
    }
});

12.設定監聽請求介面

Glide.with(this).load(url).listener(new RequestListener<String, GlideDrawable>() {
    @Override
    public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
        //載入異常
        return false;
    }

    @Override
    public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
        //載入成功
        //view.setImageDrawable(resource);
        return false;
    }
}).into(view);

13.設定取消或恢復請求

以下兩個方法是為了保證使用者介面的滑動流暢而設計的。當在ListView中載入圖片的時候,如果使用者滑動ListView的時候繼續載入圖片,就很有可能造成滑動不流暢、卡頓的現象,這是由於Activity需要同時處理滑動事件以及Glide載入圖片。Glide為我們提供了這兩個方法,讓我們可以在ListView等滑動控制元件滑動的過程中控制Glide停止載入或繼續載入,可以有效的保證介面操作的流暢。

//當列表在滑動的時候可以呼叫pauseRequests()取消請求
Glide.with(context).pauseRequests();
//當列表滑動停止時可以呼叫resumeRequests()恢復請求
Glide.with(context).resumeRequests();
// ListView滑動時觸發的事件
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
            case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
            case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
                // 當ListView處於滑動狀態時,停止載入圖片,保證操作介面流暢
                Glide.with(MainActivity.this).pauseRequests();
                break;
            case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
                // 當ListView處於靜止狀態時,繼續載入圖片
                Glide.with(MainActivity.this).resumeRequests();
                break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    }
});

14.獲取快取大小

new GetDiskCacheSizeTask(textView).execute(new File(getCacheDir(), DiskCache.Factory.DEFAULT_DISK_CACHE_DIR));
private class GetDiskCacheSizeTask extends AsyncTask<File, Long, Long> {

    private final TextView resultView;

    public GetDiskCacheSizeTask(TextView resultView) {
        this.resultView = resultView;
    }

    @Override
    protected void onPreExecute() {
        resultView.setText("Calculating...");
    }

    @Override
    protected void onProgressUpdate(Long... values) {
        super.onProgressUpdate(values);
    }

    @Override
    protected Long doInBackground(File... dirs) {
        try {
            long totalSize = 0;
            for (File dir : dirs) {
                publishProgress(totalSize);
                totalSize += calculateSize(dir);
            }
            return totalSize;
        } catch (RuntimeException ex) {
            final String message = String.format("Cannot get size of %s: %s", Arrays.toString(dirs), ex);
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    resultView.setText("error");
                    Toast.makeText(resultView.getContext(), message, Toast.LENGTH_LONG).show();
                }
            });
        }
        return 0L;
    }

    @Override
    protected void onPostExecute(Long size) {
        String sizeText = android.text.format.Formatter.formatFileSize(resultView.getContext(), size);
        resultView.setText(sizeText);
    }

    private long calculateSize(File dir) {
        if (dir == null) return 0;
        if (!dir.isDirectory()) return dir.length();
        long result = 0;
        File[] children = dir.listFiles();
        if (children != null)
            for (File child : children)
                result += calculateSize(child);
        return result;
    }
}

15.清除記憶體快取

//可以在UI主執行緒中進行
Glide.get(this).clearMemory();

16.清除磁碟快取

//需要在子執行緒中執行
Glide.get(this).clearDiskCache();

17.圖片裁剪、模糊、濾鏡等處理

依賴:

compile 'jp.wasabeef:glide-transformations:2.0.2'
// If you want to use the GPU Filters
compile 'jp.co.cyberagent.android.gpuimage:gpuimage-library:1.4.1'

可使用GenericRequestBuilder或其子類的transform()bitmapTransform()方法設定圖片處理。

示例:圓角處理

 Glide.with(mContext)
    .load(R.drawable.image_example)
    .bitmapTransform(new RoundedCornersTransformation(mContext, 30, 0, RoundedCornersTransformation.CornerType.BOTTOM))
    .into(imageView);

可實現Transformation介面,進行更靈活的圖片處理,如進行簡單地圓角處理。

public class RoundedCornersTransformation implements Transformation<Bitmap> {

    private BitmapPool mBitmapPool;
    private int mRadius;

    public RoundedCornersTransformation(Context context, int mRadius) {
        this(Glide.get(context).getBitmapPool(), mRadius);
    }

    public RoundedCornersTransformation(BitmapPool mBitmapPool, int mRadius) {
        this.mBitmapPool = mBitmapPool;
        this.mRadius = mRadius;
    }

    @Override
    public Resource<Bitmap> transform(Resource<Bitmap> resource, int outWidth, int outHeight) {
        //從其包裝類中拿出Bitmap
        Bitmap source = resource.get();
        int width = source.getWidth();
        int height = source.getHeight();
        Bitmap result = mBitmapPool.get(width, height, Bitmap.Config.ARGB_8888);
        if (result == null) {
            result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        }
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setShader(new BitmapShader(source, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP));
        canvas.drawRoundRect(new RectF(0, 0, width, height), mRadius, mRadius, paint);
        //返回包裝成Resource的最終Bitmap
        return BitmapResource.obtain(result, mBitmapPool);
    }

    @Override
    public String getId() {
        return "com.wiggins.glide.widget.GlideCircleTransform(radius=" + mRadius + ")";
    }
}

自定義圖片處理時為了避免建立大量Bitmap以及減少GC,可以考慮重用Bitmap,這就需要使用BitmapPool,例如從Bitmap池中取一個Bitmap,用這個Bitmap生成一個Canvas, 然後在這個Canvas上畫初始的Bitmap並使用MatrixPaint或者Shader處理這張圖片。為了有效並正確重用Bitmap需要遵循以下三條準則:

  1. 永遠不要把transform()傳給你的原始resource或原始Bitmaprecycle()了,更不要放回BitmapPool,因為這些都自動完成了。值得注意的是,任何從BitmapPool取出的用於自定義圖片變換的輔助Bitmap,如果不經過transform()方法返回,就必須主動放回BitmapPool或者呼叫recycle()回收。
  2. 如果你從BitmapPool拿出多個Bitmap或不使用你從BitmapPool拿出的一個Bitmap,一定要返回extrasBitmapPool
  3. 如果你的圖片處理沒有替換原始resource(例如由於一張圖片已經匹配了你想要的尺寸,你需要提前返回),transform()方法就返回原始resource或原始Bitmap

例如:

private static class MyTransformation extends BitmapTransformation {

    public MyTransformation(Context context) {
        super(context);
    }

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        Bitmap result = pool.get(outWidth, outHeight, Bitmap.Config.ARGB_8888);
        // 如果BitmapPool中找不到符合該條件的Bitmap,get()方法會返回null,就需要我們自己建立Bitmap了
        if (result == null) {
            // 如果想讓Bitmap支援透明度,就需要使用ARGB_8888
            result = Bitmap.createBitmap(outWidth, outHeight, Bitmap.Config.ARGB_8888);
        }
        //建立最終Bitmap的Canvas.
        Canvas canvas = new Canvas(result);
        Paint paint = new Paint();
        paint.setAlpha(128);
        // 將原始Bitmap處理後畫到最終Bitmap中
        canvas.drawBitmap(toTransform, 0, 0, paint);
        // 由於我們的圖片處理替換了原始Bitmap,就return我們新的Bitmap就行。
        // Glide會自動幫我們回收原始Bitmap。
        return result;
    }

    @Override
     public String getId() {
         // Return some id that uniquely identifies your transformation.
         return "com.wiggins.glide.MyTransformation";
     }
}

18.GlidePalette調色盤

七、GlideModule使用

GlideModule是一個抽象方法,全域性改變Glide行為的一種方式,通過全域性GlideModule配置Glide,用GlideBuilder設定選項,用Glide註冊ModelLoader等。所有的GlideModule實現類必須是public的,並且只擁有一個空的構造器,以便在Glide延遲初始化時,可以通過反射將它們例項化。

1.自定義一個GlideModule

public class MyGlideModule implements GlideModule {

    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        // Apply options to the builder here.
    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        // register ModelLoaders here.
    }
}

2.AndroidManifest.xml註冊

<meta-data
    android:name="com.wiggins.glide.MyGlideModule"
    android:value="GlideModule" />

3.混淆處理

-keepnames class com.wiggins.glide.MyGlideModule
# or more generally
#-keep public class * implements com.bumptech.glide.module.GlideModule

4.多個GlideModule衝突問題

GlideModule不能指定呼叫順序,所以應該避免不同的GlideModule之間有衝突的選項設定,可以考慮將所有的設定都放到一個GlideModule裡面,或者排除掉某個manifest檔案的某個Module

<meta-data
    android:name="com.wiggins.glide.MyGlideModule"
    tools:node="remove" />

5.GlideBuilder設定選項

5.1 設定Glide記憶體快取大小

MemoryCache用來把resources快取在記憶體裡,以便能馬上能拿出來顯示。預設情況下Glide使用LruResourceCache,我們可以通過它的構造器設定最大快取記憶體大小。

//獲取系統分配給應用的總記憶體大小
int maxMemory = (int) Runtime.getRuntime().maxMemory();
//設定圖片記憶體快取佔用八分之一
int memoryCacheSize = maxMemory / 8;
//設定記憶體快取大小
builder.setMemoryCache(new LruResourceCache(memoryCacheSize));

獲取預設的使用記憶體

//MemoryCache和BitmapPool的預設大小由MemorySizeCalculator類決定,MemorySizeCalculator會根據給定螢幕大小可用記憶體算出合適的快取大小,這也是推薦的快取大小,我們可以根據這個推薦大小做出調整
MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();

5.2 設定Glide磁碟快取大小

//方式一
//指定的是資料的快取地址
File cacheDir = context.getExternalCacheDir();
//最多可以快取多少位元組的資料
int diskCacheSize = 1024 * 1024 * 30;
//設定磁碟快取大小
builder.setDiskCache(new DiskLruCacheFactory(cacheDir.getPath(), "glide", diskCacheSize));

//方式二
//存放在data/data/xxxx/cache/
builder.setDiskCache(new InternalCacheDiskCacheFactory(context, "glide", diskCacheSize));

//方式三
//存放在外接檔案
builder.setDiskCache(new ExternalCacheDiskCacheFactory(context, "glide", diskCacheSize));

5.3 設定圖片解碼格式

預設格式RGB_565相對於ARGB_88884位元組/畫素可以節省一半的記憶體,但是圖片質量就沒那麼高了,而且不支援透明度。

builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);

5.4 設定BitmapPool快取記憶體大小

Bitmap池用來允許不同尺寸的Bitmap被重用,這可以顯著地減少因為圖片解碼畫素陣列分配記憶體而引發的垃圾回收。預設情況下Glide使用LruBitmapPool作為Bitmap池,LruBitmapPool採用Lru演算法儲存最近使用的尺寸的Bitmap,我們可以通過它的構造器設定最大快取記憶體大小。

builder.setBitmapPool(new LruBitmapPool(memoryCacheSize));

5.5 設定用來檢索cache中沒有的ResourceExecutorService

//為了使縮圖請求正確工作,實現類必須把請求根據Priority優先順序排好序
builder.setDiskCacheService(ExecutorService service);
builder.setResizeService(ExecutorService service);

6.整合網路框架

Glide包含一些小的、可選的整合庫,目前Glide整合庫當中包含了訪問網路操作的VolleyOkHttp,也可以通過GlideModelLoader介面自己寫網路請求。

6.1 將OkHttp整合到Glide當中

6.1.1 新增依賴

dependencies {
    //OkHttp 2.x
    compile 'com.github.bumptech.glide:okhttp-integration:[email protected]'
    compile 'com.squareup.okhttp:okhttp:2.7.5'

    //OkHttp 3.x
    compile 'com.github.bumptech.glide:okhttp3-integration:[email protected]'
    compile 'com.squareup.okhttp3:okhttp:3.2.0'
}

結尾的@aar可以將庫中的AndroidManifest.xml檔案一起匯出,Gradle自動合併必要的GlideModuleAndroidManifest.xml,然後使用所整合的網路連線,所以不用再將以下文字新增到專案的AndroidManifest.xml檔案中:

<meta-data
    android:name="com.bumptech.glide.integration.okhttp.OkHttpGlideModule"
    android:value="GlideModule" />

6.1.2 建立OkHttp整合庫的GlideModule

<meta-data
    android:name="com.wiggins.glide.okhttp.OkHttpGlideModule"
    android:value="GlideModule" />

6.1.3 混淆配置

-keep class com.wiggins.glide.okhttp.OkHttpGlideModule
#or
-keep public class * implements com.bumptech.glide.module.GlideModule

注意:
1.OkHttp 2.xOkHttp 3.x需使用不同的整合庫;
2. Gradle會自動將OkHttpGlideModule合併到應用的manifest檔案中;
3. 如果你沒有對所有的GlideModule配置混淆規則(即沒有使用-keep public class * implements com.bumptech.glide.module.GlideModule),則需要把OkHttpGlideModule進行混淆配置:-keep class com.wiggins.glide.okhttp.OkHttpGlideModule

6.2 將Volley整合到Glide當中

6.2.1 新增依賴

dependencies {
    compile 'com.github.bumptech.glide:volley-integration:[email protected]'
    compile 'com.mcxiaoke.volley:library:1.0.8'
}

6.2.2 建立Volley整合庫的GlideModule

<meta-data
    android:name="com.wiggins.glide.volley.VolleyGlideModule"
    android:value="GlideModule" />

6.2.3 混淆配置

-keep class com.wiggins.glide.volley.VolleyGlideModule
#or
-keep public class * implements com.bumptech.glide.module.GlideModule

7.使用ModelLoader自定義資料來源

如果需要根據不同的要求請求不同尺寸不同質量的圖片,這時我們就可以使用自定義資料來源。

7.1 定義處理URL介面

public interface IDataModel {
    String buildDataModelUrl(int width, int height);
}

7.2 實現不同的處理URL介面

public class JpgDataModel implements IDataModel {

    private String dataModelUrl;

    public JpgDataModel(String dataModelUrl) {
        this.dataModelUrl = dataModelUrl;
    }

    @Override
    public String buildDataModelUrl(int width, int height) {
        //http://78re52.com1.z0.glb.clouddn.com/resource/gogopher.jpg?imageView2/1/w/200/h/200/format/jpg
        return String.format("%s?imageView2/1/w/%d/h/%d/format/jpg", dataModelUrl, width, height);
    }
}

7.3 實現ModelLoader

public class MyDataLoader extends BaseGlideUrlLoader<IDataModel> {

    public MyDataLoader(Context context) {
        super(context);
    }

    public MyDataLoader(ModelLoader<GlideUrl, InputStream> urlLoader) {
        super(urlLoader, null);
    }

    @Override
    protected String getUrl(IDataModel model, int width, int height) {
        return model.buildDataModelUrl(width, height);
    }

    public static class Factory implements ModelLoaderFactory<IDataModel, InputStream> {

        @Override
        public ModelLoader<IDataModel, InputStream> build(Context context, GenericLoaderFactory factories) {
            return new MyDataLoader(factories.buildModelLoader(GlideUrl.class, InputStream.class));
        }

        @Override
        public void teardown() {
        }
    }
}

7.4 根據不同的要求採用不同的策略載入圖片

//載入jpg圖片
Glide.with(this).using(new MyDataLoader(this)).load(new JpgDataModel(imageUrl)).into(imageView);

7.5 如何跳過.using()

public class MyGlideModule implements GlideModule {
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {

    }

    @Override
    public void registerComponents(Context context, Glide glide) {
        glide.register(IDataModel.class, InputStream.class, new MyDataLoader.Factory());
    }
}
//載入jpg圖片
Glide.with(this).load(new JpgDataModel(imageUrl)).into(imageView);

八、特點

  1. 使用簡單;
  2. 可配置度及自適應程度高;
  3. 支援常見圖片格式如jpgpnggifwebp等;
  4. 支援多種資料來源如網路、本地、資源、Uri等;
  5. 高效快取策略(支援MemoryDisk圖片快取,預設Bitmap格式採用RGB_565記憶體使用至少減少一半);
  6. 生命週期整合(根據Context/Activity/Fragment/FragmentActivity生命週期自動管理請求);
  7. 高效處理Bitmap(使用BitmapPool使Bitmap複用,主動呼叫recycle回收需要回收的Bitmap,減小系統回收壓力)。

九、優點

  1. 多樣化媒體載入,支援GifWebPVideo及縮圖以等型別;
  2. 生命週期整合,提供多種方式與生命週期繫結,可以更好的讓載入圖片請求的生命週期動態管理起來;
  3. 高效的快取策略
    3.1 支援MemoryDisk圖片快取;
    3.2 快取相應大小的圖片尺寸(Picasso只會快取原始尺寸圖片,而Glide會根據你ImageView的大小來快取相應大小的圖片尺寸,因此Glide會比Picasso載入的速度要快);
    3.3 記憶體開銷小(Picasso預設的是ARGB_8888格式,而Glide預設的Bitmap格式是RGB_565格式,這個記憶體開銷大約可以減小一半)。

Android關於圖片記憶體計算共有四種,分別是:

  1. ALPHA_8:每個畫素佔用1byte記憶體;
  2. ARGB_4444:每個畫素佔用2byte記憶體;
  3. RGB_565:每個畫素佔用2byte記憶體;
  4. ARGB_8888:每個畫素佔用4byte記憶體(預設、色彩最細膩、顯示質量最高、佔用的記憶體也最大、8bit = 1byte);

舉例:一個32位的PNG = ARGB_8888 = 1204 x 1024,那麼佔用空間是:1024 x 1024 x (32/8) = 4,194,304kb = 4M左右,在解析圖片的時候為了避免OOM和節省記憶體,最好使用ARGB_4444模式(節省一半的記憶體空間)。

十、缺點

  1. 使用方法複雜:由於Glide功能強大,所以使用的方法非常多,其原始碼也相對的複雜;
  2. 包較大:Glide(v3.7.0)的大小約465kb

十一、使用場景

  1. 需要更多的內容表現形式(如Gif、縮圖等);
  2. 更高的效能要求(快取、載入速度等)。

十二、特別說明

1.ImageViewsetTag問題

問題描述:如果使用Glideinto(imageView)ImageView設定圖片的同時使用ImageViewsetTag(final Object tag)方法,將會導致java.lang.IllegalArgumentException: You must not call setTag() on a view Glide is targeting異常。因為GlideViewTarget中通過view.setTag(tag)view.getTag()標記請求的,由於Android 4.0之前Tag儲存在靜態map裡,如果Glide使用setTag(int key, final Object tag)方法標記請求則可能會導致記憶體洩露,所以Glide預設使用view.setTag(tag)標記請求,你就不能重複呼叫了。

解決辦法:如果你需要為ImageView設定Tag,必須使用setTag(int key, final Object tag)getTag(int key)方法,其中key必須是合法的資源id以確保key的唯一性,典型做法就是在資原始檔中宣告type="id"item資源。

2.placeholder()導致的圖片變形問題

問題描述:使用.placeholder()方法在某些情況下會導致圖片顯示的時候出現圖片變形的情況。這是因為Glide預設開啟的crossFade動畫導致的TransitionDrawable繪製異常,具體描述可以檢視https://github.com/bumptech/glide/issues/363。根本原因就是你的placeholder圖片和你要載入顯示的圖片寬高比不一樣,而AndroidTransitionDrawable無法很好地處理不同寬高比的過渡問題,這是Android也是GlideBug

解決辦法:使用.dontAnimate()方法禁用過渡動畫,或者使用animate()方法自己寫動畫,再或者自己修復TransitionDrawable的問題。

3.ImageView的資源回收問題

問題描述:預設情況下Glide會根據with()使用的ActivityFragment的生命週期自動調整資源請求以及資源回收。但是如果有很佔記憶體的FragmentActivity不銷燬而僅僅是隱藏檢視,那麼這些圖片資源就沒辦法及時回收,即使是GC的時候。

解決辦法:可以考慮使用WeakReference,如:

final WeakReference<ImageView> imageViewWeakReference = new WeakReference<>(imageView);
ImageView target = imageViewWeakReference.get();
if (target != null) {
    Glide.with(context).load(uri).into(target);
}

4.由於Bitmap複用導致的在某些裝置上圖片錯亂的問題

問題描述: Glide預設使用BitmapPool的方式對應用中用到的Bitmap進行復用,以減少頻繁的記憶體申請和記憶體回收,而且預設使用的Bitmap模式為RGB565以減少記憶體開銷。但在某些裝置上(通常在Galaxy系列5.X裝置上很容易復現)某些情況下會出現圖片載入錯亂的問題,具體詳見https://github.com/bumptech/glide/issues/601。原因初步確定是OpenGL紋理渲染異常。

解決辦法:GlideModule使用PREFER_ARGB_8888Glide4.X已經預設使用該模式了),雖然記憶體佔用比RGB565更多一點,但可以更好地處理有透明度Bitmap的複用問題,或者禁用Bitmap複用setBitmapPool(new BitmapPoolAdapter())來修復這個問題(不推薦這種處理方式)。

5.非同步執行緒完成後載入圖片的崩潰問題

問題描述:通常情況下非同步執行緒會被約束在Activity生命週期內,所以非同步執行緒完成後使用Glide載入圖片是沒有問題的。但如果你的非同步執行緒在Activity銷燬時沒有取消掉,那麼非同步執行緒完成後Glide就無法為一個已銷燬的Activity載入圖片資源,丟擲的異常如下(在with()方法中就進行判斷並丟擲異常):

java.lang.IllegalArgumentException: You cannot start a load for a destroyed activity
    at com.bumptech.glide.manager.RequestManagerRetriever.assertNotDestroyed(RequestManagerRetriever.java:134)
    at com.bumptech.glide.manager.RequestManagerRetriever.get(RequestManagerRetriever.java:102)
    at com.bumptech.glide.Glide.with(Glide.java:653)
    at com.frank.glidedemo.TestActivity.onGetDataCompleted(TestActivity.java:23)
    at com.frank.glidedemo.TestActivity.access$000(TestActivity.java:10)
    at com.frank.glidedemo.TestActivity$BackgroundTask.onPostExecute(TestActivity.java:46)
    at com.frank.glidedemo.TestActivity$BackgroundTask.onPostExecute(TestActivity.java:28)
    at android.os.AsyncTask.finish(AsyncTask.java:632)
    at android.os.AsyncTask.access$600(AsyncTask.java:177)
    at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:645)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:157)
    at android.app.ActivityThread.main(ActivityThread.java:5356)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
    at dalvik.system.NativeStart.main(Native Method)

解決辦法:正確管理Background Threads(非同步執行緒),當Activity停止或銷燬時,停止所有相關的非同步執行緒及後續的UI操作,或者載入前使用isFinishing()isDestroyed()進行限制(不建議這種處理方式)。

專案地址 ☞ 傳送門