Volley完全解析之进阶最佳实践与二次封装
来自: http://www.lcode.org/volley完全解析之进阶最佳实践与二次封装/
(一).前言:
上一讲我们已经对Volley使用基础阶段涉及到字符串,JSON,图片等等网络数据请求相关方法的使用。今天我们对于Volley框架来一个进阶使用扩展封装,以及对上一篇遗留的问题做一下具体修改使用。主要涉及新增GsonRequest,ImageLoader列表图片加载,ImageCache,Volley框架StringRequest二次封装以及post请求新增设置请求参数方法。
FastDev4Android框架项目地址: https://github.com/jiangqqlmj/FastDev4Android
(二).post请求扩展请求参数方法
还记得前面文章我们只是使用Request的GET请求方法,当然作为完善的网络框架POST请求必须支持,不过很可惜的是Volley框架没有给我们显示的提供设置POST请求参数的方法,不过我们有两种方法来进行设置。不过在做之前我们需要先从源码角度来分析一下。
查看源码,我们发现Volley请求最终会被封装成HttpRequest子类对象HttpUrlRequest,该方法在HttpClientStack中的createHttpRequest()方法,代码和相关注释如下:
/** * 进行创建httprequest,这边获取httprequest的子类,httpurlrequest * Creates the appropriate subclass of HttpUriRequest for passed in request. */ @SuppressWarnings("deprecation") /* protected */ static HttpUriRequest createHttpRequest(Request< ?> request, MapadditionalHeaders) throws AuthFailureError { switch (request.getMethod()) { case Method.DEPRECATED_GET_OR_POST: { // This is the deprecated way that needs to be handled for backwards compatibility. // If the request's post body is null, then the assumption is that the request is // GET. Otherwise, it is assumed that the request is a POST. byte[] postBody = request.getPostBody(); if (postBody != null) { HttpPost postRequest = new HttpPost(request.getUrl()); postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType()); HttpEntity entity; entity = new ByteArrayEntity(postBody); postRequest.setEntity(entity); return postRequest; } else { return new HttpGet(request.getUrl()); } } case Method.GET: return new HttpGet(request.getUrl()); case Method.DELETE: return new HttpDelete(request.getUrl()); case Method.POST: { HttpPost postRequest = new HttpPost(request.getUrl()); postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); //设置请求体 body信息 -如果请求体不为空---[注意] setEntityIfNonEmptyBody(postRequest, request); return postRequest; } case Method.PUT: { HttpPut putRequest = new HttpPut(request.getUrl()); putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setEntityIfNonEmptyBody(putRequest, request); return putRequest; } case Method.HEAD: return new HttpHead(request.getUrl()); case Method.OPTIONS: return new HttpOptions(request.getUrl()); case Method.TRACE: return new HttpTrace(request.getUrl()); case Method.PATCH: { HttpPatch patchRequest = new HttpPatch(request.getUrl()); patchRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType()); setEntityIfNonEmptyBody(patchRequest, request); return patchRequest; } default: throw new IllegalStateException("Unknown request method."); } }
注意查看以上case Method.POST部分中的setEntityIfNonEmptyBody();中进行设置请求体相关数据:
/** * 如果request的请求体不为空,进行设置请求体信息 * @param httpRequest * @param request * @throws AuthFailureError */ private static void setEntityIfNonEmptyBody(HttpEntityEnclosingRequestBase httpRequest, Request< ?> request) throws AuthFailureError { byte[] body = request.getBody(); if (body != null) { HttpEntity entity = new ByteArrayEntity(body); httpRequest.setEntity(entity); } }
然后会执行Request中getBody()方法来获取请求体。
/** * Returns the raw POST or PUT body to be sent. * 如果请求是POST或者PUT方法,去获取请求参数信息,然后设置到请求中 *
By default, the body consists of the request parameters in * application/x-www-form-urlencoded format. When overriding this method, consider overriding * {@link #getBodyContentType()} as well to match the new body format. * * @throws AuthFailureError in the event of auth failure */ public byte[] getBody() throws AuthFailureError { //获取请求参数信息 Map
最后会获取XxxRequest的基类Requst类中的getParams()来获取请求参数信息,然后设置进去。OK这个设置POST请求参数信息的流程大家应该能清楚吧。但是Volley源代码这边就直接返回null,如下:
protected Map
又因为Volley是开源的,那么下面我们开源对Request和XxxRequest类进行改造修改源代码,往外提高POST请求参数设置的方法,修改步骤如下:
2.1.Request类一个Map对象mParams,然后添加set方法,然后在getParams()进行return 该mParams。具体修改如下:
/** * 自定义修改 新增POST请求参数map */ protected Map
Note that you can directly override {@link #getBody()} for custom data.
* * @throws AuthFailureError in the event of auth failure */ protected Map2.2.间接着我们对StringRequest类进行扩展一个构造方法,加入请求参数Map对象,具体代码如下:
/** * 扩展POST请求构造函数 * @param url 请求地址 * @param listener 数据请求加载成功监听器 * @param errorListener 数据请求加载失败监听器 * @param params POST请求参数 */ public StringRequest(String url, Listener
OK这两步修改就完成了POST请求参数设置扩展,下面我们来一看一下具体使用方法:
RequestQueue requestQueue=Volley.newRequestQueue(this); //修改Volley源代码,扩展StringRequest支持post参数设置 Map
这样就完成POST请求参数设置的扩展,另外的一种方案是创建StringRequest对象的时候实匿名类中重写getParams()方法,然后构造Map对象设置参数信息return该对象即可。不过个人觉的这种方案不推荐还是使用设置的方法比较好。
(三).Imageloader和ImageCache
在前面使用ImageLoder对象加载图片的时候,有一个优点我们没有深入讲解,因为ImageLoader是支持缓存功能,所以我们在创建ImageLoader需要传入一个缓存管理器,里面传入我们自定义的缓存策略,Fdv_ImageCache我们这边引入LruCache算法具体实现如下:
package com.chinaztt.fdv; import android.graphics.Bitmap; import android.util.LruCache; import com.android.volley.toolbox.ImageLoader; /** * 当前类注释:图片缓存器,实现ImageLoder.ImageCache实现其中的方法,具体图片怎么样缓存让我们自己来实现 * 这样可以考虑到将来的扩展性 * 项目名:FastDev4Android * 包名:com.chinaztt.fdv * 作者:江清清 on 15/11/12 12:31 * 邮箱:jiangqqlmj@163.com * QQ: 781931404 * 公司:江苏中天科技软件技术有限公司 */ public class Fdv_ImageCache implements ImageLoader.ImageCache { private LruCache
有了图片缓存机制,然后我们在配合ImageLoader来进行列表上面加载异步图片的实现,具体实现方式如下:
/** * 当前类注释:使用ImageLoader来进行测试列表图片异步加载以及缓存 * 项目名:FastDev4Android * 包名:com.chinaztt.fda.test * 作者:江清清 on 15/11/12 15:19 * 邮箱:jiangqqlmj@163.com * QQ: 781931404 * 公司:江苏中天科技软件技术有限公司 */ @EActivity(R.layout.base_adapter_test_layout) public class VolleyLoaderActivity extends BaseActivity { @ViewById ListView lv_base_adapter; @ViewById TextView tv_title; private QuickAdaptermAdapter; private List moduleBeans; private RequestQueue requestQueue; private ImageLoader imageLoader; private ImageLoader.ImageListener listener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestQueue= Volley.newRequestQueue(this); imageLoader=new ImageLoader(requestQueue,new Fdv_ImageCache()); } @AfterViews public void setViews(){ tv_title.setText("Loader 列表图片"); } @AfterViews public void bindLvData(){ moduleBeans=DataUtils.getAdapterData(); if(mAdapter==null) { mAdapter = new QuickAdapter (this, R.layout.lv_item_base_layout,moduleBeans) { @Override protected void convert(BaseAdapterHelper helper, ModuleBean item) { //列表底下显示进度 mAdapter.showIndeterminateProgress(true); helper.setText(R.id.text_lv_item_title, item.getModulename()) .setText(R.id.text_lv_item_description, item.getDescription()); //setImageUrl(R.id.img_lv_item, item.getImgurl()); //使用ImageLoader进行加载图片 ImageView loader_img=helper.getView(R.id.img_lv_item); listener=ImageLoader.getImageListener(loader_img,R.drawable.ic_loading,R.drawable.ic_loading); imageLoader.get(item.getImgurl(),listener); } }; lv_base_adapter.setAdapter(mAdapter); } } }
运行效果如下:
(四).GsonRequest封装
前面我们已经使用过JsonObjectRequest,JsonArrayRequest两个工具,这边是采用Android自身提供的JSONObject和JSONArray来进行实现解析JSON数据的,不过我们也知道如果我们已经有JSONObject和JSONArray对象然后一步步的解析还是挺麻烦的,所以我们这边可以使用JSON 快速解析框架Gson来进行解决这个问题。现在我们自定义一个GsonRequest来专门处理请求GSON解析。在做之前我们先要看一下StringRequest的实现,首先StringRequest是继承Request类然后实现两个抽象方法,间接着提供若干个构造方法(进行数据初始化,请求类型GET/POST…,请求服务器地址,相应成功是否回调接口)。因为StringRequest的实现代码很少而且较简单,那么我们模仿StringRequest类的实现可以写出来一下GsonRequest代码如下:
/** * 当前类注释:进行扩展GSON数据解析json数据 * 项目名:FastDev4Android * 包名:com.android.volley.toolbox * 作者:江清清 on 15/11/12 18:28 * 邮箱:jiangqqlmj@163.com * QQ: 781931404 * 公司:江苏中天科技软件技术有限公司 */ public class GsonRequestextends Request { private final Response.Listener listener; private Gson gson; private Class mClass; /** * GsonRequest 构造函数 * @param method 请求方法 * @param url 请求地址 * @param listener 数据请求成功回调接口 * @param errorListener 数据请求失败回调接口 * @param pClass 需要进行解析的类 */ public GsonRequest(int method,String url,Response.Listener listener,Response.ErrorListener errorListener,Class pClass){ super(method,url,errorListener); this.listener=listener; gson=new Gson(); mClass=pClass; } /** * GsonRequest 构造函数 默认使用GET请求方法 * @param url * @param listener * @param errorListener * @param pClass */ public GsonRequest(String url,Response.Listener listener,Response.ErrorListener errorListener,Class pClass){ super(Method.GET,url,errorListener); this.listener=listener; gson=new Gson(); mClass=pClass; } /** * 数据解析 * @param response Response from the network 网络请求返回数据 * @return */ @Override protected Response parseNetworkResponse(NetworkResponse response) { try { String jsonStr=new String(response.data,HttpHeaderParser.parseCharset(response.headers)); T data=gson.fromJson(jsonStr,mClass); return Response.success(data,HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } } /** * 数据分发 * @param response The parsed response returned by */ @Override protected void deliverResponse(T response) { listener.onResponse(response); } }
以上实现详解:GsonRequest继承Request,同样提供两个构造函数,然后在parseNetworkResponse()方法中进行解析数据,最后通过Gson组件来封装数据成Bean对象,最终数据回调即可。具体使用方法如下:
GsonRequestgsonRequest=new GsonRequest ("http://interface.zttmall.com/update/mallUpdate", new Response.Listener () { @Override public void onResponse(UpdateBean response) { tv_result.setVisibility(View.VISIBLE); img_result.setVisibility(View.GONE); tv_result.setText(response.toString()); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }, UpdateBean.class);
运行效果如下:
(五).Volley二次封装(StringRequest为例)
我们在使用Volley框架请求网络数据获取的时候,一般就是以下三步骤:
1.创建RequestQueue对象
2.创建XXRequest对象(XX代表String,JSON,Image等等)
3.把XXRequest对象添加到RequestQueue中即可
虽然代码量不是不多,不过也还是需要做创建队列对象和请求对象加入到队列中的操作,而创建XxxRequest对象的构造函数中的参数又比较多,所以这边想了一种方案就是想进行网络请求的时候创建和add操作在内部然后构造函数中的参数减少。所以这边只是以StringRequest为例,简单的封装了一个Get方法请求的方法,其他的功能还有待继续添加更新。我们已StringRequest为例二次封装了一个类Fdv_StringRequst,我们来看以下该类的使用方法代码:
new Fdv_StringRequest(VolleyTestActivity.this).get("http://www.baidu.com", new Fdv_CallBackListener () { @Override public void onSuccessResponse(String response) { } @Override public void onErrorResponse(VolleyError error) { } });
这样即可,不在需要以前的RequestQueue requestQueue=Volley.newRequestQueue(this);和
requestQueue.add(object)这两句代码了,直接在内部已经做了。然后如果Get请求直接调用get()方法即可,不需要显示传入请求方法。下面我们来看一下具体实现:
5.1.定义Fdv_Volley类,里边是一个单例方法,来获取RequestQueue请求队列对象.
/** * 当前类注释:全局Fdv_Volley封装类管理类 * 项目名:FastDev4Android * 包名:com.chinaztt.fdv * 作者:江清清 on 15/11/11 23:02 * 邮箱:jiangqqlmj@163.com * QQ: 781931404 * 公司:江苏中天科技软件技术有限公司 */ public class Fdv_Volley { private static RequestQueue instance; public static RequestQueue getInstace(Context pContext){ if(instance==null){ instance= Volley.newRequestQueue(pContext); } return instance; } }
5.2.定义XxxRequest的基类Fdv_BaseRequest,主要功能获取请求队列对象,然后定义一个addRequest()方法,让子类进行调用,添加当前请求对象到请求队列中。
/** * 当前类注释: * 项目名:FastDev4Android * 包名:com.chinaztt.fdv * 作者:江清清 on 15/11/11 22:59 * 邮箱:jiangqqlmj@163.com * QQ: 781931404 * 公司:江苏中天科技软件技术有限公司 */ public class Fdv_BaseRequest{ protected static RequestQueue requestQueue; private Context mContext; protected Fdv_BaseRequest(Context pContext){ this.mContext=pContext; } /** * 请求加入到Volley Request请求队列中 * @param request */ protected void addRequest(Request request){ Fdv_Volley.getInstace(mContext).add(request); }
5.3.请求结果回调接口Fdv_CallBackListener,这边暂时只加了两个很简单的方法,后期会进行扩展
/** * 当前类注释: * 项目名:FastDev4Android * 包名:com.chinaztt.fdv * 作者:江清清 on 15/11/11 23:18 * 邮箱:jiangqqlmj@163.com * QQ: 781931404 * 公司:江苏中天科技软件技术有限公司 */ public interface Fdv_CallBackListener{ void onSuccessResponse(T response); void onErrorResponse(VolleyError error); }
5.4.最后是我们的核心类封装过后的StringRequest,Fdv_StringRequest该类继承Fdv_BaseRequest类,在该类中我们暂时只是提供一个get()方法来获取数据中,get()方法的实现还是使用原来的StringRequest进行获取数据,得到数据使用接口回调即可,实现代码如下:
/** * 当前类注释:Volley 字符串、文本数据请求封装类 * 项目名:FastDev4Android * 包名:com.chinaztt.fdv * 作者:江清清 on 15/11/11 13:43 * 邮箱:jiangqqlmj@163.com * QQ: 781931404 * 公司:江苏中天科技软件技术有限公司 */ public class Fdv_StringRequestextends Fdv_BaseRequest{ public Fdv_StringRequest(Context pContext){ super(pContext); } public void get(String url, final Fdv_CallBackListener listener){ StringRequest stringRequest=new StringRequest(Request.Method.GET, url, new Response.Listener () { @Override public void onResponse(String response) { if(listener!=null){ listener.onSuccessResponse((T) response); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { if(listener!=null){ listener.onErrorResponse(error); } } }); addRequest(stringRequest); } }
OK有了以上的几步骤,我们的对于StringRequest的get方法请求简单封装就完成了,有了这样的封装我们的功能实现就会变得代码稍微少了一点,当然我们现在的封装的功能还非常的弱,后面我这边会继续更新封装功能以及重构,让整个工具类变得更加使用。具体更新代码都会FastDev4Android项目中。以上的功能测试代码如下:
@EActivity(R.layout.volley_test_layout) public class VolleyTestActivity extends BaseActivity { private static final String TAG=VolleyTestActivity.class.toString(); @ViewById LinearLayout top_bar_linear_back; @ViewById TextView top_bar_title,tv_result; @ViewById ImageView img_result; @ViewById Button btn_string,btn_json,btn_image_request,btn_image_loader,btn_image_network,btn_string_post,btn_loader_list,btn_gson; @ViewById NetworkImageView img_result_network; private RequestQueue requestQueue; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestQueue=Volley.newRequestQueue(this); } @Click({R.id.top_bar_linear_back,R.id.btn_string,R.id.btn_json,R.id.btn_image_request,R.id.btn_image_loader,R.id.btn_image_network,R.id.btn_string_post,R.id.btn_loader_list,R.id.btn_gson}) public void backLinearClick(View view){ switch (view.getId()){ case R.id.top_bar_linear_back: this.finish(); break; case R.id.btn_string: //获取字符串 Log.d(TAG, "点击获取字符串..."); new Fdv_StringRequest
整个功能测试功能点如下图:
(六).结束语:
到此为止我们已经讲完了Volley框架进阶部分:POST请求参数修改,图片缓存器,使用最佳实践,二次封装等相关高级进阶。具体实例和框架注释过的全部代码已经上传到FastDev4Android项目中了。同时欢迎大家去Github站点进行clone或者下载浏览:
https://github.com/jiangqqlmj/FastDev4Android 同时欢迎大家star和fork整个开源快速开发框架项目~
最后对于Volley框架二次封装的代码库,我这边会继续进行更新,后面会放入单独的Git库中,库地址后面会放出来的,敬请期待~
尊重原创,转载请注明:From Sky丶清( http://www.lcode.org/ ) 侵权必究!
关注我的订阅号(codedev123),每天分享移动开发技术(Android/IOS),项目管理以及博客文章!(欢迎关注,第一时间推送精彩文章)
关注我的微博,可以获得更多精彩内容