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

圖片載入之Glide使用總結

概述

作為Glide是谷歌推薦的圖片載入庫,Glide又著 支援video,Gif,SVG格式,同時有著很好的生命週期管理,支援Volley,OkHttp,更好的記憶體管理策略等優點。

相關方法

  • with():指定了宣告週期
  • load():載入資源,String/Uri/File/Integer/URL/byte[]/T,或者 loadFromMediaStore(Uri uri)
  • placeholder(resourceId/drawable): 設定資源載入過程中的佔位Drawable。
  • error():load失敗時顯示的Drawable。
  • crossFade()/crossFade(int duration):imageView改變時的動畫,version 3.6.1後預設開啟300毫秒
  • dontAnimate():移除所有的動畫。
  • override() :調整圖片大小
  • centerCrop():圖片裁剪,ImageView 可能會完全填充,但影象可能不會完整顯示。
  • fitCenter(): 影象裁剪,影象將會完全顯示,但可能不會填滿整個 ImageView。
  • animate(): 指定載入動畫。
  • transform():圖片轉換。
  • bitmapTransform(): bitmap轉換,不可以和(centerCrop() 或 fitCenter())共用。
  • priority(Priority priority):當前執行緒的優先順序,Priority.IMMEDIATE,Priority.HIGH,Priority.NORMAL(default),Priority.LOW
  • thumbnail(): 縮圖.
  • listener():異常監聽
  • preload(int width, int height): 預載入resource到快取中(單位為pixel)
  • fallback(Drawable drawable):設定model為空時顯示的Drawable。
  • using() :為單個的請求指定一個 model
  • asGif():Gif 檢查,如果是圖片且加了判斷,則會顯示error佔位圖,否則會顯示圖片
  • asBitmap():bitmap轉化,如果是gif,則會顯示第一幀

Glide 可以以load(File)的形式播放本地視訊,但是如果需要播放網路視屏,則要用VideoView

快取策略

一張圖片變化很快,需要禁止記憶體快取

.skipMemoryCache(true)

即使關閉記憶體快取,請求圖片將會仍然被儲存在裝置的磁碟快取中,如果一張圖片變化很快,仍需要禁止磁碟快取

.diskCacheStrategy(DiskCacheStrategy.NONE)

Glide 快取了原始影象,全解析度影象和另外小版本的影象,因此禁用磁碟快取是用列舉來控制的

DiskCacheStrategy.NONE //什麼都不快取,就像剛討論的那樣
DiskCacheStrategy.SOURCE //僅僅只快取原來的全解析度的影象。在我們上面的例子中,將會只有一個 1000x1000 畫素的圖片
DiskCacheStrategy.RESULT //僅僅快取最終的影象,即,降低解析度後的(或者是轉換後的)
DiskCacheStrategy.ALL //快取所有版本的影象(預設行為)

Glide使用InternalCacheDiskCacheFactory類建立磁碟快取。目錄在
/data/data/<package-name>/cache,其他應用程式無法訪問。

自定義快取策略:

磁碟快取,用類DiskLruCacheWrapper來設定目錄

builder.setDiskCache(new DiskCache.Factory() {
            @Override
            public DiskCache build() {
            // 自己的快取目錄
                File imgFile = new File(Environment.getExternalStorageDirectory()+"/Android/data/package-name");
                return DiskLruCacheWrapper.get(imgFile,DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE);
            }
        });

記憶體快取,用類Memory Cache 來設定大小

MemorySizeCalculator calculator = new MemorySizeCalculator(context); 
builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));

Bitmap Pool

重複使用及銷燬策略。設定方法:GlideBuilder.setBitmapPool()
預設採用的是LruBitmapPool,使用了LRU演算法。

MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();
int customBitmapPoolSize = (int) (1.2 * defaultBitmapPoolSize);
builder.setBitmapPool( new LruBitmapPool( customBitmapPoolSize );

Bitmap Format

Bitmap Format用於設定全域性預設首選Bitmap規格,設定方法:GlideBuilder.setDecodeFormat()
預設採用RGB_565(比ARGB_8888節省一半的記憶體),但不支援透明度。

優先順序

會發現優先順序高的先 顯示出來,即是圖片比較大。

    //優先載入
    Glide
        .with(context)
        .load(heroImageUrl)
        .priority(Priority.HIGH)
        .into(imageViewHero);
    //後加載
    Glide
        .with(context)
        .load(itemImageUrl)
        .priority(Priority.LOW)
        .into(imageViewItem);

先顯示縮圖,後顯示原圖

   //用原圖的1/10作為縮圖
    Glide
        .with(this)
        .load("http://inthecheesefactory.com/uploads/source/nestedfragment/fragments.png")
        .thumbnail(0.1f)
        .into(iv_0);
    //用其它圖片作為縮圖
    DrawableRequestBuilder<Integer> thumbnailRequest = Glide
        .with(this)
        .load(R.drawable.news);
    Glide.with(this)
        .load("http://inthecheesefactory.com/uploads/source/nestedfragment/fragments.png")
        .thumbnail(thumbnailRequest)
        .into(iv_0);

圖片處理

實現Transformation 介面,或者使用抽象類BitmapTransformation,
通過transform()或bitmapTransform()來處理圖片

    //圓形裁剪
    Glide.with(this)
        .load("http://inthecheesefactory.com/uploads/source/nestedfragment/fragments.png")
        .bitmapTransform(new CropCircleTransformation(this))
        .into(iv_0);
    //圓角處理
    Glide.with(this)
        .load("http://inthecheesefactory.com/uploads/source/nestedfragment/fragments.png")
        .bitmapTransform(new RoundedCornersTransformation(this,30,0, RoundedCornersTransformation.CornerType.ALL))
        .into(iv_0);
    //灰度處理
    Glide.with(this)
        .load("http://inthecheesefactory.com/uploads/source/nestedfragment/fragments.png")
        .bitmapTransform(new GrayscaleTransformation(this))
        .into(iv_0);
    //模糊處理
    Glide.with(this).load(R.drawable.demo)
            .bitmapTransform(new BlurTransformation(context))
            .into((ImageView) findViewById(R.id.image));
    //其它變換...

自定義轉換

public class BlurTransformation extends BitmapTransformation {

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

    @Override
    protected Bitmap transform(BitmapPool pool, Bitmap toTransform, int outWidth, int outHeight) {
        return null; // todo
    }

    @Override
    public String getId() {
        return null; // todo
    }
}

其中getId() :描述了這個轉換的唯一識別符號,將作為快取系統的一部分

可以使用單種轉換和多種轉換,如多種轉換:

Glide  
    .with( context )
    .load( eatFoodyImages[1] )
    .transform( new GreyscaleTransformation( context ), new BlurTransformation( context ) )
    .into( imageView2 );

回撥 Target

SimpleTarget

target 泛型:Bitmap,GlideDrawable,GifDrawable
其中使用欄位宣告target’而不是使用匿名內部類的形式,可以避免被垃圾回收機制回收,而回調為空的現象。

private SimpleTarget target = new SimpleTarget<Bitmap>() {  
    @Override
    public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
        // do something with the bitmap
        // for demonstration purposes, let's just set it to an ImageView
        imageView1.setImageBitmap( bitmap );
    }
};

private void loadImageSimpleTarget() {  
    Glide
        .with( context ) // could be an issue!
        .load( eatFoodyImages[0] )
        .asBitmap()
        .into( target );
}

Target生命週期

with( context ):關係到生命週期。如果請求需要在 activity 生命週期之外去做時,需要使用:

private void loadImageSimpleTargetApplicationContext() {  
    Glide
        .with( context.getApplicationContext() ) // safer!
        .load( eatFoodyImages[1] 
        .asBitmap()
        .into( target2 );
}

Target 指定尺寸

使用ImageView 作為引數給 .into()的時候,Glide 會用 ImageView 的大小去限制影象的大小
但是target並沒有已知的大小,如果知道圖片大小則應指出,減少記憶體消耗。

private SimpleTarget target2 = new SimpleTarget<Bitmap>( 250, 250 ) {  
    @Override
    public void onResourceReady(Bitmap bitmap, GlideAnimation glideAnimation) {
        imageView2.setImageBitmap( bitmap );
    }
};

private void loadImageSimpleTargetApplicationContext() {  
    Glide
        .with( context.getApplicationContext() ) // safer!
        .load( eatFoodyImages[1] )
        .asBitmap()
        .into( target2 );
}

ViewTarget

常用於自定以控制元件中,比如自定義控制元件中組合了ImageView控制元件,如下

public class FutureStudioView extends FrameLayout {  
    ImageView iv;
    TextView tv;

    public void initialize(Context context) {
        inflate( context, R.layout.custom_view_futurestudio, this );

        iv = (ImageView) findViewById( R.id.custom_view_image );
        tv = (TextView) findViewById( R.id.custom_view_text );
    }

    public FutureStudioView(Context context, AttributeSet attrs) {
        super( context, attrs );
        initialize( context );
    }

    public FutureStudioView(Context context, AttributeSet attrs, int defStyleAttr) {
        super( context, attrs, defStyleAttr );
        initialize( context );
    }

    public void setImage(Drawable drawable) {
        iv = (ImageView) findViewById( R.id.custom_view_image );

        iv.setImageDrawable( drawable );
    }
}

使用如下方法給自定義控制元件中的ImageView設定圖片

private void loadImageViewTarget() {  
    FutureStudioView customView = (FutureStudioView) findViewById( R.id.custom_view );

    //傳遞了引數 customView
    viewTarget = new ViewTarget<FutureStudioView, GlideDrawable>( customView ) {
        @Override
        public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {
        //呼叫了FutureStudioView的setImage方法
            this.view.setImage( resource.getCurrent() );
        }
    };

    Glide
        .with( context.getApplicationContext() ) // safer!
        .load( eatFoodyImages[2] )
        .into( viewTarget );
}

NotificationTarget

動畫

animate() :可以載入資原始檔animate.xml檔案
或者 屬性動畫:

  • ViewPropertyAnimation
  • ViewAnimation
  • NoAnimation
  • DrawableCrossFadeViewAnimation
ViewPropertyAnimation.Animator animationObject = new ViewPropertyAnimation.Animator() {  
    @Override
    public void animate(View view) {
        // if it's a custom view class, cast it here
        // then find subviews and do the animations
        // here, we just use the entire view for the fade animation
        view.setAlpha( 0f );

        ObjectAnimator fadeAnim = ObjectAnimator.ofFloat( view, "alpha", 0f, 1f );
        fadeAnim.setDuration( 2500 );
        fadeAnim.start();
    }
};

Glide  
    .with( context )
    .load( eatFoodyImages[1] )
    .animate( animationObject )
    .into( imageView2 );

Glide module

Glide module 是一個抽象方法,全域性改變 Glide 行為的一個方式。
前面單策略的定義中都用到了GlideBuilder類
如果你需要訪問 GlideBuilder,你需要去實現一個 GlideModule 介面的公共類

public class FileGlideModule  implements GlideModule{
    @Override
    public void applyOptions(final Context context, GlideBuilder builder) {
//builder 內的所有方法你都可以設定。
//        builder.setDiskCache(new InternalCacheDiskCacheFactory(context));
        builder.setDiskCache(new ExternalCacheDiskCacheFactory(context));
      /*  builder.setDiskCache(new DiskCache.Factory() {
            @Override
            public DiskCache build() {
                File imgFile = new File(Environment.getExternalStorageDirectory()+"/Android/data/com.leying365");
                return DiskLruCacheWrapper.get(imgFile,DiskCache.Factory.DEFAULT_DISK_CACHE_SIZE);
            }
        });*/
        MemorySizeCalculator calculator = new MemorySizeCalculator(context);
        builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
        builder.setBitmapPool(new LruBitmapPool(calculator.getBitmapPoolSize()));
        builder.setDecodeFormat(DecodeFormat.PREFER_ARGB_8888);
    }

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

    }
}

最終通過<meta-data android:name=”com.mypackage.MyGlideModule” tools:node=”GlideModule” /> 來生效

常用方法:

  • setMemoryCache(MemoryCache memoryCache)
  • setBitmapPool(BitmapPool bitmapPool)
  • setDiskCache(DiskCache.Factory diskCacheFactory)
  • setDiskCacheService(ExecutorService service)
  • setResizeService(ExecutorService service)
  • setDecodeFormat(DecodeFormat decodeFormat)

網路庫

Glide 使用ModeLoader 介面來 整合網路庫,Glide提供了兩種網路庫 的實現,分別為OkHttp 和 Volley。

在build.gradle中新增如下依賴,glide會預設使用OKhttp來做所有的網路連線

dependencies {  
    // your other dependencies
    // ...

    // Glide
    compile 'com.github.bumptech.glide:glide:3.6.1'

    // Glide's OkHttp Integration 
    compile 'com.github.bumptech.glide:okhttp-integration:[email protected]'
    compile 'com.squareup.okhttp:okhttp:2.5.0'
}

上面會將必要的GlideModule 到你的 Android.Manifest中。

快取與失效機制

Glide 通過 url、viewwidth、viewheight、螢幕的解析度等以一種雜湊演算法生成一個獨有、安全的檔名作為key儲存到disk上.因為其是通過一個雜湊演算法來實現的,因此很難定位檔案的快取,幸好Glide提供了signature()方法允許將一個附加的資料加入到快取key當中,可以用來保證快取的及時性.

系統預設實現了MediaStoreSignature,StringSignature.

Glide.with(fragment)
    .load(mediaStoreUri)
    .signature(new MediaStoreSignature(mimeType, dateModified, orientation))
    .into(view);

當然也可以自己來實現Signature

public class MSignature implements Key {

    @Override
    public boolean equals(Object o) {
     //...
    }
    @Override
    public int hashCode() {
       //...
    }
    @Override
    public void updateDiskCacheKey(MessageDigest messageDigest) {
        messageDigest.update(ByteBuffer.allocate(Integer.SIZE)
.putInt(signature).array());
    }
}

極端情況,不快取可以用diskCacheStrategy(DiskCacheStrategy.NONE.)來實現

圖片下載

Glide除了提供into方法來載入圖片外,也提供了downloadOnly方法來實現圖片下載.

FutureTarget<File> future = Glide.with(applicationContext)
    .load(yourUrl)
    .downloadOnly(500, 500);
File cacheFile = future.get();

下載之後可以通過如下方式來載入圖片

Glide.with(yourFragment)
    .load(yourUrl)
    //DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE可以保證程式會先去讀快取檔案
    .diskCacheStrategy(DiskCacheStrategy.ALL)
    .into(yourView);

如果想獲取一張圖片的Bitmap,可以使用如下方式

//此方法需要try/catch處理,並且最好在子執行緒中,否則會阻塞主執行緒
Bitmap myBitmap = Glide.with(applicationContext)
    .load(yourUrl)
    .asBitmap()
    .centerCrop()
    //設定大小
    .into(500, 500)
    .get()