Android架構(gòu)那些事之第三方庫的隔離
在進入主題之前我們先說一下app客戶端為什么需要有一個好的架構(gòu)。
我們都知道一個好的架構(gòu)會使我們的開發(fā)變得事半功倍。
設(shè)計架構(gòu)的目的在于使我們的客戶端易于擴展、方便單元測試、可復(fù)用。
做到使模塊之間低耦合,模塊內(nèi)部高內(nèi)聚。
我們在開發(fā)的過程中會不可避免的引用一些第三方庫,比如網(wǎng)絡(luò)請求庫、圖片加載庫等等。就拿我們的圖片加載庫來說,程序中不會只有一個地方來引用到此庫,可能有N個類會用到此庫來顯示圖片。比如我們現(xiàn)在使用Universal-Image-Loader來展示客戶端需要的圖片,客戶端總共有10個類使用該來顯示圖片。迭代了兩個版本后老板突然說APP經(jīng)常出現(xiàn)顯示圖片出現(xiàn)OOM的問題,我們需要將Universal-Image-Loader換成更高效的Picasso來顯示圖片。
聽到這個需求我們的***反應(yīng)是"天哪,鬼知道項目里有多少個地方引用了ImageLoader庫,我們得改多少代碼,萬一改代碼的時候引發(fā)了其他的bug怎么辦"。
問題來了我們應(yīng)該如何避免這種“牽一發(fā)而動全身”的囧況。
好了,我們先來看下我們平常是怎么在項目中引用圖片加載庫的。
before.png
上圖是我們大多數(shù)人的做法。那么我們怎么才能做到換了一個第三方加載庫而是這四個引用類不做改動呢。
下面看下我們重新新設(shè)計之后的引用流程圖
after.png
從上圖我們能看到我們通過一個中間層來引用“第三方圖片加載庫”。這樣做的好處是不管第三方圖片加載庫你換成Picasso還是Glide我們改變的只是這個中間層,其他的我們一行代碼都不需要改動。
如果當(dāng)時你是這樣設(shè)計的當(dāng)老板讓你把Universal-Image-Loader換成Picasso時你一行代碼也不用改動只需要擴展一個類就可以了。好了話不多說 我們來看代碼如何設(shè)計。
我這里是使用代理模式來實現(xiàn)代碼與圖片加載庫的隔離的。
首先抽象一個ImageLoader接口
- **
- * 圖片加載器功能接口;
- * 添加或者替換新的圖片加載器實現(xiàn)該接口即可
- */public interface ImageLoader { /**
- * Init ImageLoader
- */
- void init(Context context); /**
- * Show Image
- *
- * @param imageUrl
- * @param imageView
- * @param defaultImage
- */
- void displayImage(String imageUrl, ImageView imageView, int defaultImage);
- }
我們當(dāng)前是使用UniversalImageLoader來展示項目中的圖片我們就建一個UniversalImageLoader類來實現(xiàn)上面的接口
- public class UniversalImageLoader implements ImageLoader { private final long discCacheLimitTime = 3600 * 24 * 15L; private com.nostra13.universalimageloader.core.ImageLoader imageLoader = com.nostra13.universalimageloader.core.ImageLoader.getInstance(); @Override
- public void init(Context context) { if (!imageLoader.isInited()) {
- ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(
- context)
- .threadPriority(Thread.NORM_PRIORITY)
- .denyCacheImageMultipleSizesInMemory()
- .memoryCache(new WeakMemoryCache())
- .memoryCacheSize((2 * 1024 * 1024))
- .memoryCacheSizePercentage(13)
- .discCacheFileNameGenerator(new Md5FileNameGenerator())
- .discCache( new LimitedAgeDiskCache(StorageUtils
- .getCacheDirectory(context),
- discCacheLimitTime))
- .tasksProcessingOrder(QueueProcessingType.LIFO).build();
- com.nostra13.universalimageloader.core.ImageLoader.getInstance().init(config);
- }
- } @Override
- public void displayImage(String uri, ImageView img, int default_pic) {
- DisplayImageOptions options = new DisplayImageOptions.Builder()
- .showImageOnLoading(default_pic)
- .showImageForEmptyUri(default_pic).showImageOnFail(default_pic)
- .cacheInMemory(true).cacheOnDisc(true)
- .bitmapConfig(Bitmap.Config.RGB_565)
- .displayer(new SimpleBitmapDisplayer()).build();
- imageLoader.displayImage(uri, img, options);
- }
接下來我們寫一個代理類來幫我們實現(xiàn)圖片加載的任務(wù)
- /**
- * 圖片加載代理類,所有的圖片操作都通過該代理類去實現(xiàn);
- * 如果要改變圖片加載框架,只需要在該類里替換相應(yīng)的圖片加載框架即可,客戶端所有引用的圖片操作都不需要修改
- */public class ImageLoaderProxy implements ImageLoader { private ImageLoader imageLoader;//代理對象
- private static ImageLoaderProxy imageLoaderProxy; public static ImageLoaderProxy getInstance() { if (imageLoaderProxy == null) {
- imageLoaderProxy = new ImageLoaderProxy();
- } return imageLoaderProxy;
- } public ImageLoaderProxy() {
- imageLoader= new UniversalImageLoader();
- } @Override
- public void init(Context context) {
- imageLoader.init(context);
- } @Override
- public void displayImage(String imageUrl, ImageView imageView, int defaultImage) {
- imageLoader.displayImage(imageUrl, imageView, defaultImage);
- }
- }
這樣客戶端所有需要顯示圖片的地方只需要調(diào)用代理類的圖片顯示方法即可 ImageLoaderProxy.getInstance().displayImage();
當(dāng)老板讓我們換成Picasso來完成圖片加載時 ,我們只需增加一個 PicassoImageLoader類然后將代理類中的這行代碼 imageLoaderProxy = new UniversalImageLoader();換成imageLoaderProxy = new PicassoImageLoader()即可。
怎么樣我們只改動一行代碼就替換了一個圖片加載庫方便吧。