volley二次封装
产品中使用Volley框架已有多时,本身已有良好封装的Volley确实给程序开发带来了很多便利与快捷。但随着产品功能的不断增加,服务器接口的不断复杂化,直接使用Volley原生的JSONObjectRequest已经导致Activity或Fragment层中耦合了大量的数据解析代码,同时当多处调用同一接口时,类似的数据解析代码还不可复用,导致大量重复代码的出现,已经让我越发地无法忍受。基于此,最近思考着对Volley原生的JSONObjectRequest(因为产品中目前和服务器交互所有的接口,数据都是json格式的)进行二次封装,把Activity和Fragment中大量的数据解析代码剥离出来,同时实现数据解析代码的复用。
为了把问题表现出来,先上一段坑爹的代码。
package com.backup;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map; import org.json.JSONException;
import org.json.JSONObject; import com.amuro.volleytest01_image.R;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.TextView; public class TestActivity02 extends Activity
{
private RequestQueue mQueue;
private ListView listView;
private List<Map<String, String>> list = new ArrayList<Map<String,String>>(); String url = "http://10.24.4.196:8081/weather.html"; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test02_layout);
listView = (ListView)findViewById(R.id.lv_test02);
mQueue = Volley.newRequestQueue(this);
getWeatherInfo(); SimpleAdapter simpleAdapter = new SimpleAdapter(this, list,
android.R.layout.simple_list_item_2, new String[] {"title","content"},
new int[] {android.R.id.text1, android.R.id.text2}); listView.setAdapter(simpleAdapter); listView.setOnItemClickListener(new OnItemClickListener()
{ @Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id)
{
TextView tv = (TextView)view.findViewById(android.R.id.text1);
tv.setText("111111111111111111");
}
});
} public void getWeatherInfo()
{
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(url, null, new Response.Listener<JSONObject>()
{ @SuppressWarnings("unchecked")
@Override
public void onResponse(JSONObject jsonObject)
{
list.clear();
Iterator<String> it = jsonObject.keys();
while (it.hasNext())
{
String key = it.next();
JSONObject obj = null;
try
{
obj = jsonObject.getJSONObject(key);
}
catch (JSONException e)
{
e.printStackTrace();
}
if (obj != null)
{
Iterator<String> objIt = obj.keys();
while (objIt.hasNext())
{
String objKey = objIt.next();
String objValue;
try
{
objValue = obj.getString(objKey);
HashMap<String, String> map = new HashMap<String, String>();
map.put("title", objKey);
map.put("content", objValue);
list.add(map);
}
catch (JSONException e)
{
e.printStackTrace();
}
}
}
}
}
}, new Response.ErrorListener()
{
@Override
public void onErrorResponse(VolleyError arg0)
{
}
}); mQueue.add(jsonObjectRequest);
}
}
上面的代码大家可以看到,复杂的json解析代码全部写在Activity里,现在如果又来一个Activity需要调用这个接口,这些解析json的代码是完全无法复用的,这不科学
下面开始分析:
1. 面向对象,对于Activity这层来说,它要的只是拿到数据进行展示,至于数据怎么变出来的,它不应该关注,所以第一件事,对数据进行封装,每个接口返回的最终数据,不应该是一个未经解析的jsonObject,而应该是一个bean,千千万万的bean最终可通过泛型来统一,so,我们先需要一个监听器,让我们封装后的Volley层直接把bean回调给Activity。
2. 对错误的处理,从目前的产品需求来看,上层Activity就是要对不同的错误展示不同的界面或跳转不同的界面,所以我们把错误统一为errorCode和errorMessage,在底层封装好后,直接抛给Activity。所以这样一个返回bean或者error的接口就出来了。
package com.amuro.volley_framwork.network_helper; public interface UIDataListener<T>
{
public void onDataChanged(T data);
public void onErrorHappened(String errorCode, String errorMessage);
}
3. 好,监听器剥离了Activity与我们的Volley层,下面我们就要自己对Volley的JsonObjectRequest进行封装了,先贴这个类:
package com.amuro.volley_framwork.network_request; import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map; import org.apache.http.NameValuePair;
import org.apache.http.client.utils.URLEncodedUtils;
import org.json.JSONObject; import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.toolbox.HttpHeaderParser;
import com.android.volley.toolbox.JsonRequest; public class NetworkRequest extends JsonRequest<JSONObject>
{
private Priority mPriority = Priority.HIGH; public NetworkRequest(int method, String url,
Map<String, String> postParams, Listener<JSONObject> listener,
ErrorListener errorListener)
{
super(method, url, paramstoString(postParams), listener, errorListener);
setRetryPolicy(new DefaultRetryPolicy(30000, 0, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT));
} public NetworkRequest(String url, List<NameValuePair> params,
Listener<JSONObject> listener, ErrorListener errorListener)
{
this(Method.GET, urlBuilder(url, params), null, listener, errorListener);
} public NetworkRequest(String url, Listener<JSONObject> listener, ErrorListener errorListener)
{
this(Method.GET, url, null, listener, errorListener);
} private static String paramstoString(Map<String, String> params)
{
if (params != null && params.size() > 0)
{
String paramsEncoding = "UTF-8";
StringBuilder encodedParams = new StringBuilder();
try
{
for (Map.Entry<String, String> entry : params.entrySet())
{
encodedParams.append(URLEncoder.encode(entry.getKey(),
paramsEncoding));
encodedParams.append('=');
encodedParams.append(URLEncoder.encode(entry.getValue(),
paramsEncoding));
encodedParams.append('&'); }
return encodedParams.toString();
}
catch (UnsupportedEncodingException uee)
{
throw new RuntimeException("Encoding not supported: "
+ paramsEncoding, uee);
}
}
return null;
} @Override
protected Response<JSONObject> parseNetworkResponse(NetworkResponse response)
{ try
{ JSONObject jsonObject = new JSONObject(new String(response.data, "UTF-8")); return Response.success(jsonObject,
HttpHeaderParser.parseCacheHeaders(response)); }
catch (Exception e)
{ return Response.error(new ParseError(e)); }
} @Override
public Priority getPriority()
{
return mPriority;
} public void setPriority(Priority priority)
{
mPriority = priority;
} private static String urlBuilder(String url, List<NameValuePair> params)
{
return url + "?" + URLEncodedUtils.format(params, "UTF-8");
}
}
4. 接下来就是我们的重头戏,写一个Controller来操作这个request,同时对数据进行bean或error的封装,这是一个抽象类,让不同的子类根据不同的接口,趋实现不同的数据解析方式:
package com.amuro.volley_framwork.network_helper; import java.util.List;
import java.util.Map; import org.apache.http.NameValuePair;
import org.json.JSONObject; import android.content.Context;
import android.util.Log; import com.amuro.volley_framwork.network_request.NetworkRequest;
import com.amuro.volley_framwork.volley_queue_controller.VolleyQueueController;
import com.android.volley.Request.Method;
import com.android.volley.Response;
import com.android.volley.Response.ErrorListener;
import com.android.volley.VolleyError; public abstract class NetworkHelper<T> implements Response.Listener<JSONObject>, ErrorListener
{
private Context context; public NetworkHelper(Context context)
{
this.context = context;
} protected Context getContext()
{
return context;
} protected NetworkRequest getRequestForGet(String url, List<NameValuePair> params)
{
if(params == null)
{
return new NetworkRequest(url, this, this);
}
else
{
return new NetworkRequest(url, params, this, this);
} } protected NetworkRequest getRequestForPost(String url, Map<String, String> params)
{
return new NetworkRequest(Method.POST, url, params, this, this);
} public void sendGETRequest(String url, List<NameValuePair> params)
{
VolleyQueueController.getInstance().
getRequestQueue(getContext()).add(getRequestForGet(url, params));
} public void sendPostRequest(String url, Map<String, String> params)
{
VolleyQueueController.getInstance().
getRequestQueue(context).add(getRequestForPost(url, params));
} @Override
public void onErrorResponse(VolleyError error)
{
Log.d("Amuro", error.getMessage());
disposeVolleyError(error);
} protected abstract void disposeVolleyError(VolleyError error); @Override
public void onResponse(JSONObject response)
{
Log.d("Amuro", response.toString());
disposeResponse(response);
} protected abstract void disposeResponse(JSONObject response); private UIDataListener<T> uiDataListener; public void setUiDataListener(UIDataListener<T> uiDataListener)
{
this.uiDataListener = uiDataListener;
} protected void notifyDataChanged(T data)
{
if(uiDataListener != null)
{
uiDataListener.onDataChanged(data);
}
} protected void notifyErrorHappened(String errorCode, String errorMessage)
{
if(uiDataListener != null)
{
uiDataListener.onErrorHappened(errorCode, errorMessage);
}
} }
这里对外直接提供了sendGetRequest方法和sendPostRequest方法,做为api就是要清晰明了,不要让调用者去了解还有Method.GET这样的东西,同时getRequestForGet方法和getRequestForPost方法把最常用的request直接封装好,不需要子类再去写new request的代码。当然为了拓展,这两个方法是protected的,default的request不能符合要求的时候,子类就可直接覆盖这两个方法返回自己的request,而disposeResponse和disponseError两个方法都为抽象方法,让子类针对不同的接口,实现不同的功能。
5. 下面来个子类实例,一看就懂。
package com.amuro.controller.networkhelper; import org.json.JSONObject; import android.content.Context; import com.amuro.bean.RRBean;
import com.amuro.utils.SystemParams;
import com.amuro.volley_framwork.network_helper.NetworkHelper;
import com.android.volley.VolleyError; //{"errorCode":"0000","errorMessage":"成功","respMsg":"success","success":"true"}
public class ReverseRegisterNetworkHelper extends NetworkHelper<RRBean>
{ public ReverseRegisterNetworkHelper(Context context)
{
super(context);
} @Override
protected void disposeVolleyError(VolleyError error)
{
notifyErrorHappened(
SystemParams.VOLLEY_ERROR_CODE,
error == null ? "NULL" : error.getMessage());
} @Override
protected void disposeResponse(JSONObject response)
{
RRBean rrBean = null; if(response != null)
{
try
{
String errorCode = response.getString("errorCode");
String errorMessage = response.getString("errorMessage");
String respMsg = response.getString("respMsg");
String success = response.getString("success"); if("0000".equals(errorCode))
{
rrBean = new RRBean();
rrBean.setErrorCode(errorCode);
rrBean.setErrorMessage(errorMessage);
rrBean.setRespMsg(respMsg);
rrBean.setSuccess(success); notifyDataChanged(rrBean);
}
else
{
notifyErrorHappened(errorCode, errorMessage);
}
}
catch(Exception e)
{
notifyErrorHappened(SystemParams.RESPONSE_FORMAT_ERROR, "Response format error");
}
}
else
{
notifyErrorHappened(SystemParams.RESPONSE_IS_NULL, "Response is null!");
} } }
5. 大功告成,这个NetworkHelper封装了数据解析的代码,完全可复用,最后看Activity
package com.amuro.ui; import com.amuro.bean.RRBean;
import com.amuro.controller.networkhelper.ReverseRegisterNetworkHelper;
import com.amuro.utils.SystemParams;
import com.amuro.volley_framwork.network_helper.NetworkHelper;
import com.amuro.volley_framwork.network_helper.UIDataListener;
import com.amuro.volleytest01_image.R; import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast; public class MyVolleyTestActivity extends Activity implements UIDataListener<RRBean>
{
private Button button; private NetworkHelper<RRBean> networkHelper; @Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_volley_test_layout); networkHelper = new ReverseRegisterNetworkHelper(this);
networkHelper.setUiDataListener(this); button = (Button)findViewById(R.id.bt);
button.setOnClickListener(new OnClickListener()
{ @Override
public void onClick(View v)
{
sendRequest();
}
});
} private void sendRequest()
{
networkHelper.sendGETRequest(SystemParams.TEST_URL, null);
} @Override
public void onDataChanged(RRBean data)
{
Toast.makeText(
this,
data.getErrorCode() + ":" +
data.getErrorMessage() + ":" +
data.getRespMsg() + ":" +
data.getSuccess(),
Toast.LENGTH_SHORT).show(); } @Override
public void onErrorHappened(String errorCode, String errorMessage)
{
Toast.makeText(
this,
errorCode + ":" + errorMessage,
Toast.LENGTH_SHORT).show(); }
}
看,Activity直接拿到的就是数据或者errorCode,把一大堆复杂的数据解析代码剥离了。
转自:http://www.aichengxu.com/view/46975
volley二次封装的更多相关文章
- Android-Volley网络通信框架(二次封装数据请求和图片请求(包含处理请求队列和图片缓存))
1.回想 上篇 使用 Volley 的 JsonObjectRequest 和 ImageLoader 写了 电影列表的样例 2.重点 (1)封装Volley 内部 请求 类(请求队列,数据请求,图片 ...
- OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据
OkHttp框架从入门到放弃,解析图片使用Picasso裁剪,二次封装OkHttpUtils,Post提交表单数据 我们这片博文就来聊聊这个反响很不错的OkHttp了,标题是我恶搞的,本篇将着重详细的 ...
- Volley的简单封装
算了一下,好像有很久没有写博客了.其实,关于写博客这件事,我从来没有把他当成我的一种任务,而是在学习过程中的一种总结和自我发现,同样也是为了练一练文笔,说不定有一天,我也能出一本书像<第一行代码 ...
- 对百度WebUploader开源上传控件的二次封装,精简前端代码(两句代码搞定上传)
前言 首先声明一下,我这个是对WebUploader开源上传控件的二次封装,底层还是WebUploader实现的,只是为了更简洁的使用他而已. 下面先介绍一下WebUploader 简介: WebUp ...
- iOS项目相关@AFN&SDWeb的二次封装
一,AFNetworking跟SDWebImge是功能强大且常用的第三方,然而在实际应用中需要封装用来复用今天就跟大家分享一下AFN&SDWeb的二次封装 1. HttpClient.h及.m ...
- Quick Cocos (2.2.5plus)CoinFlip解析(MenuScene display AdBar二次封装)
转载自:http://cn.cocos2d-x.org/tutorial/show?id=1621 从Samples中找到CoinFlip文件夹,复制其中的 res 和 script 文件夹覆盖新建工 ...
- 对jquery的ajax进行二次封装以及ajax缓存代理组件:AjaxCache
虽然jquery的较新的api已经很好用了, 但是在实际工作还是有做二次封装的必要,好处有:1,二次封装后的API更加简洁,更符合个人的使用习惯:2,可以对ajax操作做一些统一处理,比如追加随机数或 ...
- Android 应用程序集成Google 登录及二次封装
谷歌登录API: https://developers.google.com/identity/sign-in/android/ 1.注册并且登录google网站 https://accounts. ...
- Android 应用程序集成FaceBook 登录及二次封装
1.首先在Facebook 开发者平台注册一个账号 https://developers.facebook.com/ 开发者后台 https://developers.facebook.com/ap ...
随机推荐
- OGLplus 0.33.0 发布,OpenGL 的 C 封装库
OGLplus 0.33.0 引入很多新的 OGLplus 和 OALplus 示例,更新了构建系统.CamMatrix::LookingAt 构造器.Texture::MaxLevel getter ...
- Unity2.0容器自动注册机制
现如今可能每个人都会在项目中使用着某种 IoC 容器,并且我们的意识中已经形成一些固定的使用模式,有时会很难想象如果没有 IoC 容器工作该怎么进展. IoC 容器通过某种特定设计的配置,用于在运行时 ...
- Ora-01536:超出了表空间users的空间限量(转)
Ora-01536:超出了表空间users的空间限量(转) 正在开会,同事跑过来说数据库有问题,通讯程序不能入库,赶快获取一条insert into a values()语句后在toad工具中手动插入 ...
- Spec 网上选课
角色定义 管理员 管理员负责向系统中添加学生和教师的个人信息以及课程信息,实现对教师.学生和课程信息的修改.删除.查询操作,并对管理员个人密码进行修改. 学生 学生具有查看待选课程信息和任课教师信息. ...
- 创建Fragment
你可以认为fragment是Activity中模块化的部分.Fragment有它自己的生命周期,接收它自己的输入事件,并且你可以在Activity运行的时候添加或移除它(有点像可以被重用在不同Acti ...
- unity3d——自带寻路Navmesh (三)(转)
继续介绍NavMesh寻路的功能,接下来阿赵打算讲一下以下两个例子,先看看完成的效果: 第一个例子对于喜欢DOTA的朋友应该很熟悉了,就是不同小队分不同路线进攻的寻路,红绿蓝三个队伍分别根据三条路 ...
- 有关CSS中字体响应式的设置
在进行页面响应式设计中,往往需要根据屏幕分辨率来显示不同大小的字体.通常的做法是通过media queries给不同的分辨率指定不同的字体样式,例如: body { font-size: 22px; ...
- 自旋锁-SpinLock(.NET 4.0+)
短时间锁定的情况下,自旋锁(spinlock)更快.(因为自旋锁本质上不会让线程休眠,而是一直循环尝试对资源访问,直到可用.所以自旋锁线程被阻塞时,不进行线程上下文切换,而是空转等待.对于多核CPU而 ...
- java5 Lock用法
锁是控制多个线程对共享资源进行访问的工具.通常,锁提供了对共享资源的独占访问.一次只能有一个线程获得锁,对共享资源的所有访问都需要首先获得锁.不过,某些锁可能允许对共享资源并发访问,如 ReadWri ...
- C#与数据库访问技术总结(十一)之数据阅读器(DataReader)1
数据阅读器 当执行返回结果集的命令时,需要一个方法从结果集中提取数据. 处理结果集的方法有两个: 第一,使用数据阅读器(DataReader): 第二,同时使用数据适配器(Data Adapter)和 ...