使用Glide模仿微信圖片加載策略

爲什麼要對圖片進行壓縮

比較壓縮與否造成的資源消耗

你能區別出:下面兩個九方格圖片展示的區別不?
圖1:
這裏寫圖片描述
圖2:
這裏寫圖片描述
區別在於:圖1使用的是壓縮圖,圖2使用的是高清圖
以下是高清圖地址
http://o9xuvf3m3.bkt.clouddn.com/new_york.jpg
http://o9xuvf3m3.bkt.clouddn.com/peru.jpg
http://o9xuvf3m3.bkt.clouddn.com/trochilidae.jpg
http://o9xuvf3m3.bkt.clouddn.com/france-217.jpg
http://o9xuvf3m3.bkt.clouddn.com/france-220.jpg
http://o9xuvf3m3.bkt.clouddn.com/france-216.jpg
http://o9xuvf3m3.bkt.clouddn.com/france-221.jpg
以九方格中第一張圖爲例(高清地址:http://o9xuvf3m3.bkt.clouddn.com/new_york.jpg
大小2.7M2560*1920

壓縮後的地址http://o9xuvf3m3.bkt.clouddn.com/new_york.jpg?imageView2/3/h/360/w/360
壓縮後大小31KB,480*360
壓縮後的樣子這裏寫圖片描述
壓縮比高達1:89,但是神奇的是,它們在界面展示上展示效果幾乎一致。溜不溜….

Monitors

手機尺寸總共就那麼大,這裏我是用小米Note,一格的寬度=一格的高度=一個1/3屏幕寬度,經過計算佔用像素值是360*360,控件既不能放大又不能縮小,我們給再大的圖又有什麼意義。當然,圖片很難可以達到完全適配的理想效果,所以我是用一張480*360大小的圖片,然後用Center_Crop模式填充即可。
我們AndroidStudio Monitors上測試下,影響到底有多大
這裏寫圖片描述
我家是20M光纖,我一個人獨顯,都要6,7S,我在想要是2G手機使用高清圖加載的話,得多久。
動手算算,2.2M*7=15.4M,
10K/S,1540秒,也就是26min,這種App要是拿給網絡差一點的用戶,估計直接就刪了。
總結下爲什麼要對圖片進行壓縮:

  1. 節約帶寬,消耗更少的流量和網絡請求時間
  2. 節約CPU資源(又爲Android機動不動就發熱的問題盡了一份力)
  3. 節約內存資源,這個影響不明顯,但是蚊子肉也是肉不是

另一個用處,加載縮略圖

這裏寫圖片描述
就像微信一樣,在加載圖片的時候,先加載一張縮略圖,然後再加載大圖,這種模式下,我們可以在圖片還沒完全加載出來的時候大概已經知道它要描述的事物了。
不是說我們剛纔已經採用了壓縮過圖片的方式了麼,怎麼還要加載縮略圖。
這裏寫圖片描述
查看日誌,這裏因爲ViewPager預加載的原因,所以會一次加載4張圖片(可以修改默認值),其中2張大圖,2張縮略圖
我們對原高清圖,進行適應手機大小壓縮後的大小爲
http://o9xuvf3m3.bkt.clouddn.com/new_york.jpg?imageView2/3/h/1134/w/800
他已經已經從原來的2.7M下降到418K,而且界面顯示效果和原來一致,成果是喜人的,但是如果在一些網絡不好的情況,比如10k/s的2G網絡 他任然需要42S才能加載完成。所以我們還想它更快一點,但是圖片已經沒法優化了,這時候我們可以考慮犧牲一部分圖片質量來換取速度的方式呈現
這時候在它加載出來之前先加載縮略圖
http://o9xuvf3m3.bkt.clouddn.com/new_york.jpg?imageView2/3/h/50/w/100這裏寫圖片描述
http://o9xuvf3m3.bkt.clouddn.com/peru.jpg?imageView2/3/h/50/w/100這裏寫圖片描述
只有4KB大小的縮略圖,對帶寬和流量的消耗可以忽略不計,就算是10K/S的網絡一秒內也可以加載完成,這時候如果用戶判定對這張圖不感興趣,就可以直接切換,我們取消後臺加載大圖的任務,就可以達到節約帶寬的目的。

實現步驟

服務器接口支持

這裏的Demo用的API,我直接使用了Saas服務—七牛雲。
七牛雲圖片處理API文檔參考這裏:http://developer.qiniu.com/code/v6/api/kodo-api/image/imageview2.html
很簡單,你看幾個例子就知道咯
http://o9xuvf3m3.bkt.clouddn.com/france-221.jpg
原圖,2560*1026 (寬*高)
http://o9xuvf3m3.bkt.clouddn.com/france-221.jpg?imageView2/3/h/50/w/100
按比例切分,125*50
http://o9xuvf3m3.bkt.clouddn.com/france-221.jpg?imageView2/3/h/100/w/50
按比例切分, 250*100
根據這個規律,很快就能知道它的用法了。

指定圖片需要具體尺寸參數

剛纔,我們提到需要什麼尺寸,手機端發送需要尺寸的url請求到服務器就行了,但是我們怎麼知道手機需要什麼尺寸呢?手動算嗎,控件那麼多,那不得算得蛋疼。
所幸Glide提供了這個功能

定義接口

接口主要用來實現,把
http://o9xuvf3m3.bkt.clouddn.com/france-221.jpg 結合Glide測繪的width和height
組裝成
http://o9xuvf3m3.bkt.clouddn.com/france-221.jpg?imageView2/3/h/100/w/50的樣式

public class CustomImageModelLoader extends BaseGlideUrlLoader<CustomImageSizeModel> {
    public CustomImageModelLoader(Context context) {
        super(context);
    }

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

實現接口

public class CustomImageSizeModelImp implements CustomImageSizeModel, Parcelable {
    private String baseUrl;
    private static final String TAG = "CustomImageSizeModelImp";

    public CustomImageSizeModelImp(String baseUrl) {
        this.baseUrl = baseUrl;
    }


    @Override
    public String requestCustomSizeUrl(int width, int height) {
        String url = baseUrl + "?imageView2/3/h/" + height + "/w/" + width;
        Log.d(TAG, "requestCustomSizeUrl: " + url);
        return url;
    }

    @Override
    public String getBaseUrl() {
        return baseUrl;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(this.baseUrl);
    }

    protected CustomImageSizeModelImp(Parcel in) {
        this.baseUrl = in.readString();
    }

    public static final Parcelable.Creator<CustomImageSizeModelImp> CREATOR = new Parcelable.Creator<CustomImageSizeModelImp>() {
        @Override
        public CustomImageSizeModelImp createFromParcel(Parcel source) {
            return new CustomImageSizeModelImp(source);
        }

        @Override
        public CustomImageSizeModelImp[] newArray(int size) {
            return new CustomImageSizeModelImp[size];
        }
    };
}

這裏的Parcelable主要是用來傳輸的,這裏可以忽略不看

定義一個BaseGlideUrlLoader的子類

這個用來,通知Glide,我們load不使用load(url)這種,而是使用如下配置加載

    private List<CustomImageSizeModel> mDatas;
    Glide.with(context)
      .using(new CustomImageModelLoader(mContext))
      .load(mDatas.get(position))
public class CustomImageModelLoader extends BaseGlideUrlLoader<CustomImageSizeModel> {
    public CustomImageModelLoader(Context context) {
        super(context);
    }

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

最後就像普通加載一樣使用

 Glide.with(mContext)
                    .using(new CustomImageModelLoader(mContext))
                    .load(mDatas.get(position))
                    .into(imageHolder.mImageView);

這裏的寬高測繪過程,對我們用戶完全封閉,由Glide內部自己實現和傳遞給CustomImageSizeModelImp

先加載縮略圖,再加載大圖

這裏需要使用.thumbnail()操作符,也很簡單,使用如下

        void displayImage(final CustomImageSizeModel model, final ImageView imageView, final View loading, final View download) {
            DrawableRequestBuilder thumbnailBuilder = Glide
                    .with(imageView.getContext())
                    .load(new CustomImageSizeModelImp(model
                            .getBaseUrl())
                            .requestCustomSizeUrl(100, 50));

            Glide.with(ImageViewPagerActivity.this)
                    .using(new CustomImageModelLoader(imageView.getContext()))
                    .load(model)
                    .listener(new RequestListener<CustomImageSizeModel, GlideDrawable>() {
                        @Override
                        public boolean onException(Exception e, CustomImageSizeModel model, Target<GlideDrawable> target, boolean isFirstResource) {
                            return false;
                        }

                        @Override
                        public boolean onResourceReady(GlideDrawable resource, CustomImageSizeModel model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                            loading.setVisibility(View.GONE);
                            download.setVisibility(View.VISIBLE);
                            PhotoViewAttacher attacher = new PhotoViewAttacher(imageView);
//                            mAttacher.update();
                            return false;
                        }
                    })
                    .thumbnail(thumbnailBuilder)
                    .into(imageView);
        }

手動請求生成一個100*50大小的縮略圖,參數值不正確,服務器選擇了一個最接近的125*50返回給我們。
這兒.listener()操作符,主要用來監聽控件是否繪製完成,好通知進度條隱藏,點擊下載高清圖按鈕呈現出來。

查看原圖,下載高清圖

這個很好實現吧,4點多了太晚了,睡了
週末愉快~~~

代碼下載地址:https://github.com/zhouruikevin/ImageLoadPK

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章