大部分的android应用开发都会涉及到网络操作,而在网络操作中绝大部分又是http的操作,你可能会选用java的 HttpUrlConnection、也可能是Apache的HttpClient,想要高度的封装的还有Google的亲儿子Volley,其他的还有诸如OkHttp,Xutils等很多第三方的框架。
而我在开发中更加倾向于使用Google的Volley,毕竟亲儿子嘛,虽然用起来比一些三方的要复杂一点,但是我们完全可以自己封装一下,这篇博客,我们就去高度封装一下volley,让volley看上去逼格更好。
你理想中的封装是怎样呢?
Net.url().params().parser().get();
看看这样的封装对不对你的胃口,还是我们更加简化一下,形如这样:
Net.get(url, params, parser, callback)
今天我们就来实现一下第二种操作方式的封装!
分析一下参数,url不用说了,params我们直接使用Map? no,肯定要对Map进行简单的封装,为什么要封装?volley需要的就是map格式的参数啊?我们还是需要形如:
new RequestParams().put().put().put...
这样的级联操作,封装很简单,看代码:
/** * 发送网络请求 * post方法的 参数对象 * @author loader * */ public class RequestParams { private LinkedHashMap<String, String> mParams; public RequestParams() { mParams = new LinkedHashMap<String, String>(); } /** * 添加参数 * @param key 键 * @param value 值 * @return */ public RequestParams add(String key, Object value) { mParams.put(key, value.toString()); return this; } public LinkedHashMap<String, String> get() { return mParams; } }
不多说了, 继续下一个参数parser,parser是干嘛的? 当然是用来解析网络请求的返回值的,大部分情况下是返回json,当然也有可能是xml,那为什么不是在callback里去解析呢? 这里主要是为了解耦。也就是解析部分的代码并不影响callback, 第二个好处就是parser和callback可以并行开发,写callback的coder可以和写parser的coder并行开发,甚至可以先于parser。说了这么多parser的好处,我们来赶紧看看parser是个啥玩意吧!
/** * 解析数据 * @author loader * @param <T> */ public interface Parser<T> { public Result<T> parse(String response); }
仅仅是一个接口,而且返回值是一个Result的泛型,来看看Result:
/** * * @author loader * * @param <T> */ public class Result<T> { public static final int ERR = 0; // error public static final int OK = 1; // ok private int status; // 状态 ERR OK private String msg; // 错误信息 private T result; // 结果 private Object obj; // 附加 public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getResult() { return result; } public void setResult(T result) { this.result = result; } public Object getObj() { return obj; } public void setObj(Object obj) { this.obj = obj; } public boolean isOK() { return status == OK; } }
两个常量,代表成功和失败,status的取值在ERR和OK中选择,msg是失败的原因,成功的时候可以不用管它,result是我们的结果,还有一个obj是一个附加值,有时候我们可能要返回不只一个的值(参考了Message)。
这里面最重要的就是那个T result了,这个T是我们在写parser的时候指定的。也就是我们parser想返回什么类型,我们的结果就是什么类型。
继续回到Parser,说到他是一个接口,那对于我的项目,我还提供了一个抽象的基类,来看看:
/** * 解析器的基类 * @author loader * * @param <T> */ public abstract class AbsParser<T> implements Parser<T> { @Override public Result<T> parse(String response) { Result<T> result = new Result<T>(); try { JSONObject baseObject = new JSONObject(response); if(!baseObject.optBoolean("success")) throw new Exception(baseObject.optString("message")); result.setStatus(Result.OK); parseResponse(baseObject, result); } catch (Exception e) { result.setStatus(Result.ERR); result.setMsg(e.getMessage()); e.printStackTrace(); } return result; } /** * 解析json内容 * @param baseObject * @return */ public abstract void parseResponse(JSONObject baseObject, Result<T> result) throws Exception; }
在这里,我们实现了parse方法,在parse方法中去解析了公用的部分,而且又提供了一个抽象的parseResponse方法供用户去扩展解析。
还有一个callback,也是一个接口,需要我们自己去实现:
/** * 回调数据 * @author loader * @param <T> */ public interface Callback<T> { public void onSuccess(Result<T> result); public void onFailed(String msg); }
成功的时候返回Result,失败了是message。
哦,对了,对于volley的封装呢?细心的朋友可能发现了一个VolleyManager,来看一下:
/** * 封装volley * @author loader * */ public class VolleyManager { private RequestQueue mRequestQueue; private static VolleyManager sInstance; public synchronized static VolleyManager getInstance() { if(sInstance == null) sInstance = new VolleyManager(); return sInstance; } private VolleyManager() { mRequestQueue = Volley.newRequestQueue(App.getInstance()); } /** * 添加一个请求 * @param request */ public <T> void add(Request<T> request) { mRequestQueue.add(request); } /** * 添加一个请求,并设置tag * @param request * @param tag */ public <T> void add(Request<T> request, Object tag) { request.setTag(tag); add(request); } /** * 取消全部请求 */ public void cancelAll() { mRequestQueue.cancelAll(new RequestQueue.RequestFilter() { @Override public boolean apply(Request<?> request) { return true; } }); } /** * 取消指定tag的请求 * @param tag */ public void cancel(Object tag) { mRequestQueue.cancelAll(tag); } }
提供了一个单例,去add一个请求。
好了,封装的代码就这些,我们来看看使用吧。
/** * * @author loader * */ public class UserBiz { /** * 用户登录 * @param phoneNumber * @param pwd * @param li */ public void login(final String phoneNumber, final String pwd, final Callback<Boolean> callback) { String url = Constants.LOGIN_URL + "?mobile=" + phoneNumber + "&password=" + pwd + "&apptype=2&type=2"; Net.get(url, new LoginParser(), callback); } }
继续看看LoginParser:
class LoginParser extends AbsParser<Boolean> { @Override public void parseResponse(JSONObject baseObject, Result<Boolean> result) throws Exception { if(baseObject.optBoolean("success")) { JSONObject dataObject = null; if (baseObject.has("userinfo")) { dataObject = baseObject.optJSONObject("userinfo"); }else if (baseObject.has("barberinfo")) { dataObject =baseObject.optJSONObject("barberinfo"); } if(dataObject == null) { result.setStatus(Result.ERR); return; } User user = new User(); onUserInfo(dataObject, user); result.setStatus(Result.OK); result.setResult(baseObject.optBoolean("success")); }else { result.setStatus(Result.ERR); result.setMsg(baseObject.optString("message")); } } }
根据自己的解析结果去给Result赋值,到现在结果已经出来了,那就需要Callback回调到UI了:
mUserBiz.login(mPhoneNumber, mPassword, mLoginListener);
private Callback<Boolean> mLoginListener = new Callback<Boolean>() { @Override public void onFailed(String msg) { Log.debug("failed:" + msg); } @Override public void onSuccess(Result<Boolean> result) { boolean success = result.getResult(); if(!success) { Log.debug("登录失败"); return; } // TODO : 成功后你的逻辑 } };
在mLoginListener中根据Result的结果去做不同的逻辑。
ok,至此一次简单的volley封装就完成了,而且实现了Parser和Callback的分离,是不是很爽? 当然你也可能将parser部分放在callback里,没有什么是不可以的,视你的项目而定。