Android架构那些事之第三方库的隔离
在进入主题之前我们先说一下app客户端为什么需要有一个好的架构。
我们都知道一个好的架构会使我们的开发变得事半功倍。
设计架构的目的在于使我们的客户端易于扩展、方便单元测试、可复用。
做到使模块之间低耦合,模块内部高内聚。
我们在开发的过程中会不可避免的引用一些第三方库,比如网络请求库、图片加载库等等。就拿我们的图片加载库来说,程序中不会只有一个地方来引用到此库,可能有N个类会用到此库来显示图片。比如我们现在使用Universal-Image-Loader来展示客户端需要的图片,客户端总共有10个类使用该来显示图片。迭代了两个版本后老板突然说APP经常出现显示图片出现OOM的问题,我们需要将Universal-Image-Loader换成更高效的Picasso来显示图片。
听到这个需求我们的第一反应是"天哪,鬼知道项目里有多少个地方引用了ImageLoader库,我们得改多少代码,万一改代码的时候引发了其他的bug怎么办"。
问题来了我们应该如何避免这种“牵一发而动全身”的囧况。
好了,我们先来看下我们平常是怎么在项目中引用图片加载库的。
before.png上图是我们大多数人的做法。那么我们怎么才能做到换了一个第三方加载库而是这四个引用类不做改动呢。
下面看下我们重新新设计之后的引用流程图
after.png
从上图我们能看到我们通过一个中间层来引用“第三方图片加载库”。这样做的好处是不管第三方图片加载库你换成Picasso还是Glide我们改变的只是这个中间层,其他的我们一行代码都不需要改动。
如果当时你是这样设计的当老板让你把Universal-Image-Loader换成Picasso时你一行代码也不用改动只需要扩展一个类就可以了。好了话不多说 我们来看代码如何设计。
我这里是使用代理模式来实现代码与图片加载库的隔离的。
首先抽象一个ImageLoader接口
**
* 图片加载器功能接口;
* 添加或者替换新的图片加载器实现该接口即可
*/
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);
}
我们当前是使用UniversalImageLoader来展示项目中的图片我们就建一个UniversalImageLoader类来实现上面的接口
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);
}
接下来我们写一个代理类来帮我们实现图片加载的任务
/**
* 图片加载代理类,所有的图片操作都通过该代理类去实现;
* 如果要改变图片加载框架,只需要在该类里替换相应的图片加载框架即可,客户端所有引用的图片操作都不需要修改
*/
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);
}
}
这样客户端所有需要显示图片的地方只需要调用代理类的图片显示方法即可
ImageLoaderProxy.getInstance().displayImage();
当老板让我们换成Picasso来完成图片加载时 ,我们只需增加一个 PicassoImageLoader类然后将代理类中的这行代码 imageLoaderProxy = new UniversalImageLoader();换成imageLoaderProxy = new PicassoImageLoader()即可。
怎么样我们只改动一行代码就替换了一个图片加载库方便吧。