專案正在改版,即時通訊功能暫時刪除了。
1 有哪些常用的圖片載入庫?
當下使用的主要有Piccaso、Fresco、Android-Universal-Image-Loader、Glide、Volley這五個圖片載入框架。 關於這些圖片載入框架的對比,網上可以找到很多文章。這裡不做過多贅述。具體請參考5中的參考連結,肯定會對你有幫助。
2 為什麼要封裝?
這個段落的答案,摘抄自Stormzhang的文章 如何正確使用開源專案?
計算機史上有個萬能的解決方案就是,如果原有層面解決不了問題,那麼就請再加一層!
對於開源專案,我們知道有些庫設計的確實很棒,使用者呼叫起來非常方便,一行程式碼直接搞定,拿圖片載入庫 Picasso 舉個例子:
1 |
Picasso.with(context).load(imageUrl).into(imageView); |
使用起來是不是特簡單?你也許問我,都封裝的這麼好了還用得著再封裝一層麼?那你錯了,哪怕他已經很完美了,我都會這麼做:
1 2 3 4 5 |
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(滑動返回)等。
4.2 ImageUtil類
作為整個ImageLoader的公共訪問入口,以後使用的方式,將會是
1 2 |
ImageLoader imageLoader =new ImageLoader.Builder().url("img url").imgView(mImgView).build(); ImageLoaderUtil.getInstance().loadImage(context,imageLoader); |
這種形式。可以看到ImageUtil提供的是單例模式,進行了封裝(後期引入Dagger2 之後直接會修改使用ImageLoaderUtil
例項的方式)。全域性應該只提供一個ImageLoader
的例項,因為圖片載入中又有執行緒池,快取系統和網路請求等,很消耗資源,所以不可能讓它構造多個例項。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package edu.com.base.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 BaseImageLoaderStrategy mStrategy; private ImageLoaderUtil(){ mStrategy =new GlideImageLoaderStrategy(); } //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){ mStrategy.loadImage(context,img); } public void setLoadImgStrategy(BaseImageLoaderStrategy strategy){ mStrategy =strategy; } } |
4.3 BaseImageLoaderProvider類
可以看到我們ImageUtil中是採用這個類的loadImage方法去載入圖片的。這裡是一個介面。由具體的子類(GlideImageLoaderProvider)去實現loadImage
方法。(這裡我也新增了PicassoImageLoaderStrategy
方法,但其中的loadImage
方法是空的實現)。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package edu.com.base.ui.widget.imageloader; import android.content.Context; /** * Created by Anthony on 2016/3/3. * Class Note: * abstract class/interface defined to load image * (Strategy Pattern used here) */ public interface BaseImageLoaderStrategy { void loadImage(Context ctx, ImageLoader img); } |
4.4 GlideImageLoaderProvider類
是BaseImageLoaderProvider的實現類,完成具體的載入圖片操作。這裡面會有wifi下載入圖片的判斷。具體判斷將放在util工具類中進行實現。這裡也是利用圖片載入庫Glide進行實現。後期如果工程專案決定使用其他的圖片載入框架,當然可以採用其他類繼承BaseImageLoaderProvider。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
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 implements 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。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 |
package edu.com.base.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 wifiStrategy;//載入策略,是否在wifi下才載入 private ImageLoader(Builder builder) { this.type = builder.type; this.url = builder.url; this.placeHolder = builder.placeHolder; this.imgView = builder.imgView; this.wifiStrategy = builder.wifiStrategy; } public int getType() { return type; } public String getUrl() { return url; } public int getPlaceHolder() { return placeHolder; } public ImageView getImgView() { return imgView; } public int getWifiStrategy() { return wifiStrategy; } public static class Builder { private int type; private String url; private int placeHolder; private ImageView imgView; private int wifiStrategy; public Builder() { this.type = ImageLoaderUtil.PIC_SMALL; this.url = ""; this.placeHolder = R.drawable.default_pic_big; this.imgView = null; this.wifiStrategy = 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.wifiStrategy = strategy; return this; } public ImageLoader build() { return new ImageLoader(this); } } } |
4.6 策略模式的使用 上面的圖片載入用到了策略模式。
策略模式是指定義了一系列的演算法,並將每一個演算法封裝起來(比如上面的Picasso和Glide),而且他們還可以互相替換。策略模式讓他的演算法獨立於使用它的客戶而獨立變化。
一起看看整個圖片載入封裝的類圖。
這裡真是利用同樣是圖片載入,我們可以將不同的載入方式抽象出來,提供一個統一的介面,不同的演算法或者策略有不同的實現方式,這樣我們的客戶端,也就是我們利用ImageLoaderUtil
類載入圖片的時候,就可以實現不同的載入策略。我們也可以通過
1 2 3 |
public void setLoadImgStrategy(BaseImageLoaderStrategy strategy){ mStrategy =strategy; } |
來傳入不同的載入策略,實現了策略的動態替換。也就提高了後期的可擴充套件性和可維護性。
5 參考連結
- Android 幾個圖片快取原理、特性對比
- Introduction to Glide, Image Loader Library for Android, recommended by Google
- FaceBook推出的Android圖片載入庫-Fresco
- StackOverflow–>Picasso v/s Imageloader v/s Fresco vs Glide
本篇文章專案github地址:MVPCommon
打賞支援我寫出更多好文章,謝謝!
打賞作者
打賞支援我寫出更多好文章,謝謝!
任選一種支付方式