WangWY 2014-10-31
我理解的基于网络的应用最核心的其实就是request跟response的分发。这个分发机制写好了,框架够健壮,扩展性高,那么以后涉及到这个层次的 改动就少(因为从一开始我们的设计都是比较初级的,会随着需求的不断增多而出现一些需要对框架机制的改动需求),因为对于分发机制的改动是很核心的,说牵 一发而动全身一点也不为过,稍微改一点点都会对项目有很大的影响甚至是未知的影响,而且构建得不好的话,有些需求的改动会非常麻烦,而这些改动如果是良好 的构架的话往往是可以避免的,所以,在初期能够构建好一些是很有必要的。我大概看过一些开源的项目以及这些年来自己做过的项目(没做过游戏,所以游戏的我 不知道哈),感觉基于网络的应用要么用http要么用套接字,如果不需要推送的,一般都是使用http来做。
http的请求步骤大概分为这么几步,就我看来:
1)发起request,这里的发起者要是个context之类的(一般是activity或者service)东东,当然很多时候发起者也是最后数据的 接受者。不同的框架对于这个发起的机制实现各不相同,但总的说来都是统一给出一个公共接口,让发起者封装好必要的信息,使用公共接口,让其实现类去启动实 际的http请求操作,大概主要区别在于如何封装这些request信息。
2)进行request请求,并获取response。真正的请求和响应就在这里完成,这里可能有两个需要考虑到的,一个是线程的阻塞,有的框架会让线程 阻塞直到获取到响应为止,有的框架则会在这里分发出多个线程,以避免这种阻塞,当然大多数都会采用后者了。二个就是数据的存储,有些固定的请求,比如一些 不会轻易变动的信息,在第一次请求到了之后就不需要再多次请求了,一般也可以在这个部分完成这个功能。但总而言之,不管你线程分配也好,使用本地数据也 好,还是你的请求出问题了也好,总之你必须在一定的时间内,给我的request一个response。
3)分派response,我们去餐馆吃饭都知道,哪桌点的菜就端到哪桌去,不能端错了。谁发出的request,最后就要给到他的接受者手里,不能给错了。有些框架是靠一些接口类的注入来保证数据的正确接受,有些框架是靠一个寻找机制来保障的(比如广播)。
好了,说了这么多我们来看看volley吧。前面那么多铺垫也不是白用的,我就按照我理解的这三步来拆析volley框架。
1)发起request:volley使用Request抽象类来封装请求。Request类也是整个 volley项目中最核心的类(还真没有之一)。从源码我们可以看到,Request的实现类有好几种,而这些实现类有个共同的特点就是他们其实已经定义 好了response返回的数据类型,也就是说,其实这里的Requst类并不单单只做request,他们也负责对response的数据处理(当然直 接使用实现类的话,我们一般不管这些数据如何被处理的),我们可以自己写实现类并实现其中的数据处理方法
abstract protected Response<T> parseNetworkResponse(NetworkResponse response);
来将返回数据变成我们想要的类型,然后由我们再来进一步加工处理。
Request可不单单只做了这么一件事情。为了证明他为什么是最核心的类,我再列举几个他做了的貌似跟他不相关的事情。
abstract protected void deliverResponse(T response);
public void deliverError(VolleyError error)
有木有震惊的赶脚,是的,最后response以及error的分发的实际执行者也是Request。所以这个Request类是贯穿一个请求到响应过 程自始至终的类。这个其实不难理解,最后一步的分派工作需要一定的信息来保证分派的正确性,而最能提供这些信息的,其实就是最初我们发出的request 了。
简单的介绍下Request如何使用:
从源码可以看出 getUrl 是返回的 url地址,getBody是 返回的请求参数,这两个方法是在http的实际请求中肯定要用到的,使用get请求的话是需要保证getUrl的返回值正确即可,而使用post的话还要 保证后面一个方法的返回值正确。(返回值如何注入就是你们的事情了,构造函数注入也可,set 方法注入也可)这里需要给大家注意的地方原生态的Stringrequest 没有重写 getBody 方法,也就是说 getBody 返回为null,那么 volley将默认使用get请求,如果大家想用post请求并使用Stringrequest的话,请重写这个方法。
另外还有两个很重要的类,请大家在构造request的时候务必要注入,ErrorListener 和 Listener,一个是用来处理error(public void onErrorResponse(VolleyError error))的,一个是用来处理正常返回的(public void onResponse(T response))这两个接口的定义都在Response类中。不难想出上面我所说的两个方法abstract protected void deliverResponse(T response)和public void deliverError(VolleyError error) 的实现其实就是调用这两个接口各自的方法就可以了。
有些附加的条件我们可以酌情的考虑是否需要实现。
public String getCacheKey(),CacheKey是volley从 Request获取的用于本地存储请求数据的键值,相信大家很快能明白,如果两个request的这个方法返回的值一样的话,后者将能够有机会取到前者存 储在本地的数据,从而减少了网络请求。原生态的Request是使用url为cechekey,这样并不一定科学,有本地存储需求的童鞋,请切记重写此方 法。当然前提是保证 public final boolean shouldCache()返回为true。否则前功尽弃。
public void setRetryPolicy(RetryPolicy retryPolicy),从字面意思就可以看出,这个是对一个request的重新请求策略的设置,不同的项目是否需要重新请求,重新请求几次,请求超时的时间,这些就在这设置到里面。一般放就是继承RetryPolicy 类,根据自己的需求实现父类方法。
不知不觉写了这么多,先写到这里吧,后面我再介绍volley如何实现第二步跟第三步。
前一篇粗略的介绍了一下Volley,并在最后附上了一段使用代码,这一篇详细的介绍一下Volley的使用。前面也说了Volley主要获取JSON对象和图片加载,这里也分为两部分介绍。
private RequestQueue mRequestQueue;
mRequestQueue = Volley.newRequestQueue(this);
JsonObjectRequest jr = new JsonObjectRequest(Request.Method.GET,url,null,new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.i(TAG,response.toString()); parseJSON(response); va.notifyDataSetChanged(); pd.dismiss(); } },new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.i(TAG,error.getMessage()); } }); mRequestQueue.add(jr);
JsonObjectRequest:返回JSON对象。
JsonArrayRequest:返回JsonArray。
StringRequest:返回String,这样可以自己处理数据,更加灵活。
另外可以继承Request<T>自定义Request。
@Override public void onStop() { for (Request <?> req : mRequestQueue) { req.cancel(); } }
@Override protected void onStop() { // TODO Auto-generated method stub super.onStop(); mRequestQueue.cancelAll(this); }
@Override rotected void onStop() { // TODO Auto-generated method stub super.onStop(); mRequestQueue.cancelAll( new RequestFilter() {}); or mRequestQueue.cancelAll(new Object());
singleImg=(ImageView)findViewById(R.id.volley_img_single_imgeview); ImageRequest imgRequest=new ImageRequest(url, new Response.Listener<Bitmap>() { @Override public void onResponse(Bitmap arg0) { // TODO Auto-generated method stub singleImg.setImageBitmap(arg0); } }, 300, 200, Config.ARGB_8888, new ErrorListener(){ @Override public void onErrorResponse(VolleyError arg0) { // TODO Auto-generated method stub } }); mRequestQueue.add(imgRequest);
RequestQueue mRequestQueue = Volley.newRequestQueue(this); final LruCache<String, Bitmap> mImageCache = new LruCache<String, Bitmap>( 20); ImageCache imageCache = new ImageCache() { @Override public void putBitmap(String key, Bitmap value) { mImageCache.put(key, value); } @Override public Bitmap getBitmap(String key) { return mImageCache.get(key); } }; ImageLoader mImageLoader = new ImageLoader(mRequestQueue, imageCache); // imageView是一个ImageView实例 // ImageLoader.getImageListener的第二个参数是默认的图片resource id // 第三个参数是请求失败时候的资源id,可以指定为0 ImageListener listener = ImageLoader .getImageListener(imageView, android.R.drawable.ic_menu_rotate, android.R.drawable.ic_delete); mImageLoader.get(url, listener);
public class NetworkImageView extends ImageView
继承自ImageView,添加了一个
public void setImageUrl(String url, ImageLoader imageLoader) {}
方法,参数包含一个Url地址和一个ImageLoader对象
核心方法:
private void loadImageIfNecessary(final boolean isInLayoutPass) {}
内部实现,boolean 类型参数代表是否重新请求网络 ,true:重新请求 false:取缓存
内 部实现和ImageLoader类似,都是通过ImageContainer中new一个ImageListener,在ImageListener,只 是做了Url的空判断,如果Url为null,则调用ImageContainer.cancelRequest();取消请求。
覆写方法:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //onLayout时重新请求 loadImageIfNecessary(true); } @Override protected void onDetachedFromWindow() { //销毁View的时候Release操作 if (mImageContainer != null) { // If the view was bound to an image request, cancel it and clear // out the image from the view. mImageContainer.cancelRequest(); setImageBitmap(null); // also clear out the container so we can reload the image if necessary. mImageContainer = null; } super.onDetachedFromWindow(); } //drawable状态改变重绘 @Override protected void drawableStateChanged() { super.drawableStateChanged(); invalidate(); }
总结:网络请求下载图片显示,可以使用此控件,比传统的ImageView多了网络处理,也添加了2个方法,设置开始下载的默认图和下载出错后显示图。
/** * Sets the default image resource ID to be used for this view until the attempt to load it * completes. */ public void setDefaultImageResId(int defaultImage) { mDefaultImageId = defaultImage; } /** * Sets the error image resource ID to be used for this view in the event that the image * requested fails to load. */ public void setErrorImageResId(int errorImage) { mErrorImageId = errorImage; }