写SDK的时候,碰到一个小的需求,银行的小图标需要访问服务器下载,并且要缓存在本地。
因为SDK项目中银行图标类似图片并不是很多,如果使用第三方框架的话太大,大材小用,而且类似加载图片的第三方很多,如果SDK接入了volley,但是接入我们SDK的app却使用了picasso,会导致同样功能代码的重复,方法数也容易超标。
所以就要自己写一个小的框架,简要描述就是给出图片url,第一次进行网络访问,并缓存在本地(包括内存和硬盘缓存)。后续都可以从本地读取。(不处理服务器给出的头部,例如有效期等等)。
代码:
git clone https://github.com/LxxCaroline/MyApplication.git
该小框架是主要模仿volley而写的,有类似的几个class,包括RequestQueue, ImageRequest, NetworkDispatcher, CacheDispatcher, HttpEngine(类似HurlStack), ImageCache, NetworkImageView
ImageRequest主要是封装了请求,包括url,listener,bitmap,isCanceled,tracer(Arraylist主要是记录该request流程的走向)
RequestQueue是中央处理枢纽,ImageRequest交给RequestQueue来处理,由RequestQueue开启网络线程NetworkDispatcher和缓存线程CacheDispatcher来处理一些将要处理的ImageRequest。一般ImageRequest是先通过CacheDispatcher来获取,如果获取不到,则通过NetworkDispatcher来获取,之后再写入到Cache中。ImageCache主要是来读写Cache的,这里用到了LruCache和DiskLruCache。
NetworkImageView是专门用来加载图片的,HttpEngine就是真正访问网络的地方。
在RequestQueue中有几个重要的变量:
private Map<String, Queue< ImageRequest >> mWaitingRequests;
private PriorityBlockingQueue<ImageRequest> mNetworkQueue;
private PriorityBlockingQueue<ImageRequest> mCacheQueue;
第一个变量是记录当往RequestQueue中加入request的时候,先检测是否有相同url的request,如果有,则将该request加入到相同url的queue中去,这样当第一个相同url的request返回的时候,可以把结果分享给queue中的request。
第二个参数是维护将要被网络访问的request队列。
第三个参数是维护将要访问Cache的request队列。
刚刚说到RequestQueue中会开启网络和cache线程,一般来说cache访问会很快,所以只用开一个cache线程。network比较慢,所以如果你项目中图片访问量大的话,则需要多开几个线程,如果图片访问量不大的话,开一至两个线程就可以了。
一个cache线程的话,都从requestQueue的mCacheQueue中取,而多个network线程的话,都从requestQueue的mNetworkQueue中取(所有线程共享)。
priorityBlockingQueue是无界队列,并且具有优先级,可以以一定的顺序去读取request,至于如何定优先级,用户可以自己定义,例如先来后到,或给某些request一些高的优先级。
在NetworkImageView中,拿到bitmap后,需要给自己setImageBitmap,但是注意,需要在主线程中进行。
原来NetworkImageView的代码:
private ImageRequest request = null;
private RequestQueue queue;
public void setRequestQueue(RequestQueue queue){
this.queue = queue;
}
public void setImageUrl(String url) {
if (request != null) {
request.setListener(null);
}
request = new ImageRequest(url);
request.setListener(this);
queue.addRequest(request);
}
@Override
public void onResponse(final Bitmap bitmap) {
Log.d("RequestQueue-View", "setBitmap " + (bitmap != null));
post(new Runnable() {
@Override
public void run() {
Log.d("RequestQueue-View", "setBitmap succ");
setImageBitmap(bitmap);
}
});
}
@Override
protected void onDetachedFromWindow() {
if (request != null) {
request.isCanceled = true;
request.setListener(null);
}
super.onDetachedFromWindow();
}
发现在onResponse函数中,只打印了第一个log,第二个却没打印(也就是说runnable中的内容都没有执行),导致图片未显示。。。
后来查到post(runnable)在view没有被attach到window之前,是没有效果的,后来代码改成如下:
public void setImageUrl(String url) {
if (request != null) {
request.setListener(null);
}
request = new ImageRequest(url);
request.setListener(this);
if (isAttachedToWindow()) {
queue.addRequest(request);
}
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (request != null) {
queue.addRequest(request);
}
}
接下来说下DiskLruCache,这个是硬盘缓存技术,这个类的代码需从网上下,并非google撰写,但得到官方认可。具体说明可以看这篇文章:
Android DiskLruCache缓存完全解析