1. 程式人生 > >網路圖片載入的封裝-(從零開始搭建android框架系列(4))

網路圖片載入的封裝-(從零開始搭建android框架系列(4))

本篇文章專案github地址:MVPCommon
本文章原地址:簡書部落格
專案效果

1 有哪些常用的圖片載入庫?


當下使用的主要有Piccaso、Fresco、Android-Universal-Image-Loader、Glide、Volley這五個圖片載入框架。
關於這些圖片載入框架的對比,網上可以找到很多文章。這裡不做過多贅述。具體請參考5中的參考連結,肯定會對你有幫助。

2 為什麼要封裝?

這個段落的答案,摘抄自Stormzhang的文章 如何正確使用開源專案?

計算機史上有個萬能的解決方案就是,如果原有層面解決不了問題,那麼就請再加一層!

對於開源專案,我們知道有些庫設計的確實很棒,使用者呼叫起來非常方便,一行程式碼直接搞定,拿圖片載入庫 Picasso 舉個例子:

Picasso.with(context).load(imageUrl).into(imageView);
 ```

使用起來是不是特簡單?你也許問我,都封裝的這麼好了還用得著再封裝一層麼?那你錯了,哪怕他已經很完美了,我都會這麼做:

public class ImageLoader {
    public static void with(Context context, String imageUrl, ImageView imageView) {
        Picasso.with(context).load(imageUrl).into(imageView); 
    }
}


這樣我所有專案呼叫的方式直接就是 ImageLoader.with() ,這樣做的好處是:

入口統一,所有圖片載入都在這一個地方管理,一目瞭然,即使有什麼改動我也只需要改這一個類就可以了。

隨著你們業務的需求,發現 Picasso 這個圖片載入庫已經滿足不了你們了,你們需要換成 Fresco ,如果你沒有封裝一層的話,想要替換這個庫那你要崩潰了,要把所有呼叫 Picasso 的地方都改一遍,而如果你中間封裝了一層,那真的非常輕鬆,三天兩頭的換一次也沒問題。

這就是所謂的外部表現一致,內部靈活處理原則。

****

####3 有哪些需求?

這裡提供我平常開發用到的兩個需求:

**3.1 圖片封裝,提供統一入口。**封裝成熟的圖片框架,也就解決了這些問題:(記憶體快取,磁碟快取,網路載入的結合,利用取樣率對圖片進行一定的壓縮,高效載入bitmap,圖片的載入策略,快取策略(LRU),圖片錯位 ,優化列表的卡頓)

**3.2 提供wifi下載入圖片開關,非wifi下顯示佔位圖片。**

****
####4 怎麼實現圖片封裝?

**4.1 整體目錄*
* 在我的mvp搭建的專案中,imageloader放在和activity,fragment同級的widget下面。當然後續也會不斷的新增widget(小控制元件),比如這裡的loading(載入),netstatus(網路狀態監聽),progress(Material 進度條),swipeback(滑動返回)等。 ![](http://upload-images.jianshu.io/upload_images/1833901-cc796f6e9d9c44da.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) **4.2 ImageUtil類** 作為整個ImageLoader的公共訪問入口,以後使用的方式,將會是

ImageLoader imageLoader =new ImageLoader.Builder().url(“img url”).imgView(mImgView).build();
ImageLoaderUtil.getInstance().loadImage(context,imageLoader);


這種形式。可以看到ImageUtil提供的是單例模式,進行了封裝。全域性應該只提供一個ImageLoader的例項,因為圖片載入中又有執行緒池,快取系統和網路請求等,很消耗資源,所以不可能讓它構造多個例項。

package edu.com.mvplibrary.ui.widget.imageloader;

import android.content.Context;

/**
* Created by Anthony on 2016/3/3.
* Class Note:use this class to load image,single instance
*/
public class ImageLoaderUtil {

public static final int PIC_LARGE = 0;
public static final int PIC_MEDIUM = 1;
public static final int PIC_SMALL = 2;

public static final int LOAD_STRATEGY_NORMAL = 0;
public static final int LOAD_STRATEGY_ONLY_WIFI = 1;

private static ImageLoaderUtil mInstance;
private BaseImageLoaderProvider mProvider;

private ImageLoaderUtil(){
    mProvider =new GlideImageLoaderProvider();
}

//single instance
public static ImageLoaderUtil getInstance(){
if(mInstance ==null){
synchronized (ImageLoaderUtil.class){
if(mInstance == null){
mInstance = new ImageLoaderUtil();
return mInstance;
}
}
}
return mInstance;
}

public void loadImage(Context context,ImageLoader img){
    mProvider.loadImage(context,img);
}

}

**4.3 BaseImageLoaderProvider類**

可以看到我們ImageUtil中是採用這個類的loadImage方法去載入圖片的。這裡是一個抽象方法,提供空的實現。由具體的子類(GlideImageLoaderProvider)去實現。

package edu.com.mvplibrary.ui.widget.imageloader;

import android.content.Context;

/**
* Created by Anthony on 2016/3/3.
* Class Note:
* abstract class defined to load image
*/
public abstract class BaseImageLoaderProvider {
public abstract void loadImage(Context ctx, ImageLoader img);
}

**4.4 GlideImageLoaderProvider類**

是BaseImageLoaderProvider的實現類,完成具體的載入圖片操作。這裡面會有wifi下載入圖片的判斷。具體判斷將放在util工具類中進行實現。這裡也是利用圖片載入庫Glide進行實現。後期如果工程專案決定使用其他的圖片載入框架,當然可以採用其他類繼承BaseImageLoaderProvider。

package edu.com.mvplibrary.ui.widget.imageloader;

import android.content.Context;

import com.bumptech.glide.Glide;
import com.bumptech.glide.Priority;
import com.bumptech.glide.load.data.DataFetcher;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.load.model.stream.StreamModelLoader;

import java.io.IOException;
import java.io.InputStream;

import edu.com.mvplibrary.AbsApplication;
import edu.com.mvplibrary.util.AppUtils;
import edu.com.mvplibrary.util.SettingUtils;

/**
* Created by Anthony on 2016/3/3.
* Class Note:
* provide way to load image
*/
public class GlideImageLoaderProvider extends BaseImageLoaderProvider {
@Override
public void loadImage(Context ctx, ImageLoader img) {

    boolean flag= SettingUtils.getOnlyWifiLoadImg(ctx);
    //如果不是在wifi下載入圖片,直接載入
    if(!flag){
        loadNormal(ctx,img);
        return;
    }

    int strategy =img.getStrategy();
    if(strategy == ImageLoaderUtil.LOAD_STRATEGY_ONLY_WIFI){
        int netType = AppUtils.getNetWorkType(AbsApplication.app());
        //如果是在wifi下才載入圖片,並且當前網路是wifi,直接載入
        if(netType == AppUtils.NETWORKTYPE_WIFI) {
            loadNormal(ctx, img);
        } else {
            //如果是在wifi下才載入圖片,並且當前網路不是wifi,載入快取
            loadCache(ctx, img);
        }
    }else{
        //如果不是在wifi下才載入圖片
        loadNormal(ctx,img);
    }

}


/**
 * load image with Glide
 */
private void loadNormal(Context ctx, ImageLoader img) {
    Glide.with(ctx).load(img.getUrl()).placeholder(img.getPlaceHolder()).into(img.getImgView());
}


/**
 *load cache image with Glide
 */
private void loadCache(Context ctx, ImageLoader img) {
    Glide.with(ctx).using(new StreamModelLoader<String>() {
        @Override
        public DataFetcher<InputStream> getResourceFetcher(final String model, int i, int i1) {
            return new DataFetcher<InputStream>() {
                @Override
                public InputStream loadData(Priority priority) throws Exception {
                    throw new IOException();
                }

                @Override
                public void cleanup() {

                }

                @Override
                public String getId() {
                    return model;
                }

                @Override
                public void cancel() {

                }
            };
        }
    }).load(img.getUrl()).placeholder(img.getPlaceHolder()).diskCacheStrategy(DiskCacheStrategy.ALL).into(img.getImgView());
}

}

**4.5 ImageLoader類**

在ImageUtil的load方法中進行圖片載入,第一個引數是Context,那麼第二個引數呢?正是這裡的ImageLoader,採用Builder建造者模式。Builder模式可以將一個複雜物件的構建和它的表示分離,使得同樣的構建過程可以構建不同的物件。

**why builder pattern?**
因為在圖片載入中,會處理到的資料必定有圖片的url,必定有ImageView的例項,可能有載入策略(是否wifi下載入),可能有圖片載入型別(大圖,中圖,小圖),也會有圖片載入沒有成功時候的佔位符。那麼這麼多資料操作,所以用到了Builder模式,一步一步的建立一個複雜物件的建立者模式,它允許使用者在不知道內部構建細節的情況下,可以更精細的控制物件的構建流程。比如這裡的ImageLoader。

package edu.com.mvplibrary.ui.widget.imageloader;

import android.widget.ImageView;

import edu.com.mvplibrary.R;

/**
* Created by Anthony on 2016/3/3.
* Class Note:
* encapsulation of ImageView,Build Pattern used
*/
public class ImageLoader {
private int type; //型別 (大圖,中圖,小圖)
private String url; //需要解析的url
private int placeHolder; //當沒有成功載入的時候顯示的圖片
private ImageView imgView; //ImageView的例項
private int strategy;//載入策略,是否在wifi下才載入

private ImageLoader(Builder builder) {
    this.type = builder.type;
    this.url = builder.url;
    this.placeHolder = builder.placeHolder;
    this.imgView = builder.imgView;
    this.strategy = builder.strategy;
}
public int getType() {
    return type;
}

public String getUrl() {
    return url;
}

public int getPlaceHolder() {
    return placeHolder;
}

public ImageView getImgView() {
    return imgView;
}

public int getStrategy() {
    return strategy;
}

public static class Builder {
    private int type;
    private String url;
    private int placeHolder;
    private ImageView imgView;
    private int strategy;

    public Builder() {
        this.type = ImageLoaderUtil.PIC_SMALL;
        this.url = "";
        this.placeHolder = R.drawable.default_pic_big;
        this.imgView = null;
        this.strategy = ImageLoaderUtil.LOAD_STRATEGY_NORMAL;
    }

    public Builder type(int type) {
        this.type = type;
        return this;
    }

    public Builder url(String url) {
        this.url = url;
        return this;
    }

    public Builder placeHolder(int placeHolder) {
        this.placeHolder = placeHolder;
        return this;
    }

    public Builder imgView(ImageView imgView) {
        this.imgView = imgView;
        return this;
    }

    public Builder strategy(int strategy) {
        this.strategy = strategy;
        return this;
    }

    public ImageLoader build() {
        return new ImageLoader(this);
    }

}

}

“`

5 參考連結

本篇文章專案github地址:MVPCommon