为什么会突然想写这个呢,一个是太长时间没碰博客了,忙着做项目,另一个是接手了几个烂项目,才知道这东西又多么重要!!被坑的
言归正传,做一个网络框架,需要做到什么呢?
层次分明,这里我归类为三层
1)底层网络调用,所有的网络都会经过底层这里,方便做统一的管理 BaseHttpRequest
(家里总要有一个管事的)
2)中间层封装,为了隔离代码与底层,并且方便开发者处理网络接口,这里统一将网络接口封装到一起,方便看,方便改 NetRequest
3)应用层调用,好的接口调用在应用层应该就只有一行代码,这样不会有过多的冗余的东西要处理。
层思想很丰满,现实很骨感,素不素感觉还是无从下手!!!
1.先选择网络框架(其实你选啥都一样,我这种设计所有网络框架通吃!!),这里选择afinal作为示例。
2.准备好jar包丢到libs目录下,网上随便下一个就好了,afinal_0.5.1_bin.jar
3.创建BaseHttpRequest.class
1 )先写构造函数,无论何时都会需要上下文滴,并初始化fh
public BaseHttpRequest(Context context) { this.context = context; fh = getFh(); } public FinalHttp getFh() { if (fh == null) { fh = new FinalHttp(); fh.configTimeout(30 * 1000); // 设置超时连接 10秒 } return fh; }
2)考虑到用get请求选择太少了,毕竟不安全,这里直接用POST,需要用GET自己稍微改一下就好了。
做这一步先你要知道一个东西,我们网络请求网络接口的时候,一般服务器域名是不会变的,变的只是不同接口调用不同的service跟传不同的params参数
所以,调用post请求之前,先把url的各部分传递过来,组装成url后并打印,方便调试。
serviceAction:service的访问路径
requestId:简单的标示,这样的话在同一个页面调用多个网络接口时候,可以区分,这个跟startActivityforResult时候多传个参数是同个道理。
url:服务器地址
params:接口封装好的参数,是键值对格式
ifShosDialog:考虑网络请求时候一般都需要考虑弱网情况,所以这里加个字段判断是否需要显示加载框,一定不要写死,因为实际应用中往往会有需求是偷偷调接口,那时候就不要加载框了~createLoadingDialog我就不贴代码了,你根据你喜欢自己画吧。
这里需要注意一点:不同公司后台肯定要有自己的规范,所以这里要提前约定好,网络请求成功之后返回统一的格式是什么,这里就跟后台商量好了,如果成功就会返回succ,数据统一丢到data里面,失败就返回error_msg
{"rsp_code":"succ","data":{"version_code":57,"version_name"}
public void doPostRquest(String serviceAction, final int requestId, String url, AjaxParams params, final boolean ifShowDialog) { Log.i("zyl", "requestURL:" + url + serviceAction + "?" + params.toString()); fh.post(url + serviceAction, params, ajaxcallback = new AjaxCallBack<Object>() { @Override public void onStart() { super.onStart(); if (ifShowDialog) { if (loadingDialog == null) { createLoadingDialog(context, "数据加载中..."); } loadingDialog.show(); } } @Override public void onSuccess(Object t) { super.onSuccess(t); loadingDialog.dismiss(); String result = t.toString(); try { JSONObject json = new JSONObject(result); String rspCode = json.getString("rsp_code"); Log.v("zyl", "onSuccess:" + result); if (rspCode.equals("succ")) { String data = json.getString("data"); ResultEntity resultEntity = new ResultEntity("", data, requestId); Message message = new Message(); message.what = HANDLER_WHAT_SUCESS; message.obj = resultEntity; baseHandler.sendMessage(message); } else { String msg = json.getString("error_msg"); if (msg.contains("token")) { //token失效重新登录这里严谨一点应该返回固定的code,收到这信号就去重新登录就对了。 baseHandler.sendEmptyMessageDelayed(EXIT, 0); } else { Message message = new Message(); message.what = HANDLER_WHAT_FAIL; ResultEntity resultEntity = new ResultEntity(msg, "", requestId); message.obj = resultEntity; baseHandler.sendMessage(message); } } } catch (JSONException e) { e.printStackTrace(); } } /** * 处理请求失败的回调函数 */ @Override public void onFailure(Throwable t, int errorNo, String strMsg) { Log.i("zyl", "onAfinalFail:" + strMsg); if (loadingDialog != null) { loadingDialog.dismiss(); } super.onFailure(t, errorNo, strMsg); Message message = new Message(); message.what = HANDLER_WHAT_NET; ResultEntity resultEntity = new ResultEntity("", "", requestId); message.obj = resultEntity; baseHandler.sendMessage(message); } }); }
就是这么简单!!网络请求成功之后拿到了data,然后将数据封装到下面的实体bean里面,成功的时候一般我关心data,失败的时候我们关心失败的原因msg.
public class ResultEntity { String msg; //失败原因 String data; //成功返回数据 int requestId; //请求的标示,区分不同的网络请求 public ResultEntity(String msg, String data, int requestId) { this.msg = msg; this.data = data; this.requestId = requestId; } }ok,到这里我们已经从onSuccess拿到接口调用成功的数据或者被拒绝原因,从onFailure拿到网络调用失败的原因
那么,怎么传回给NetRequest,再传回应用层呢??????????
写个回调接口:
public interface IOnHttpListener { /** * 网络请求失败(没有网络或连接服务器原因) * * @param requestId */ void onAfinalFail(int requestId); /** * 网络请求成功,拿到想要的数据result,一般msg用不上。 * * @param requestId * @param result */ void onExecuteSuccess(int requestId, String result, String msg); /**网络通的,但请求方式有误或者数据有误失败; * 返回失败信息 * * @param requestId * 请求id * @param result * 失败提示 */ void onExecuteFail(int requestId, String msg); }
回调用法就很简单了吧。在Activity实现这个接口,重写下面三个方法
public class MainActivity extends BaseActivity implements IOnHttpListener { private NetRequest netRequest; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(com.netframework.R.layout.activity_main); netRequest = new NetRequest(this); netRequest.requestCheckVersion(1,true); } @Override public void onAfinalFail(int requestId) { Log.v("zyl","onAfinalFail requestId :"+requestId); } @Override public void onExecuteSuccess(int requestId, String result, String msg) { } @Override public void onExecuteFail(int requestId, String msg) { } }到这里可能有人会说我骗你,只写一行代码就能调用接口???
看来我只能放绝招了,修改为一下:
new NetReuest(this).requestCheckVersion(1,true);
哈哈。开玩笑,实际上还是不建议这样写,还是参考上面的写法吧。这样同个页面NetRequest不用重复new
有的同学有点懵逼。底层BaseHttpRequest 写好了,应用层调用代码也写好了,那么中间层NetRequest该怎么写呢?
先看构造:
/** * 给Activity调用 * * @param context */ public NetRequest(Context context) { this.context = context; this.listener = (IOnHttpListener) context; request = new BaseHttpRequest(context); request.setHttpListener(listener); } /** * 给Fragment调用 * * @param listener * @param context */ public NetRequest(IOnHttpListener listener, Context context) { this.context = context; request = new BaseHttpRequest(context); request.setHttpListener(listener); }
能看懂为什么Fragment调用要多传个参数么,因为Activity本身就继承Context,所以直接就是实现我们接口的那个实例 。
这里补充一下BaseHttpRequest的一个方法,方便将示例对象从Activity->NetRequest->BaseHttpRequest
public void setHttpListener(IOnHttpListener httpListener) { this.httpListener = httpListener; }
在BaseHttpRequest调用接口之后拿到状态,我们就可以通过这个IOnHttpListener传回去。是不是觉得一切都在套路中。。冥冥之中自有安排。
* 把服务器原始结果进行处理 */ private Handler baseHandler = new Handler() { public void handleMessage(Message msg) { ResultEntity result = (ResultEntity) msg.obj; switch (msg.what) { case HANDLER_WHAT_SUCESS: if (httpListener != null) httpListener.onExecuteSuccess(result.requestId, result.data, result.msg); break; case HANDLER_WHAT_FAIL: if (httpListener != null) httpListener.onExecuteFail(result.requestId, result.msg); break; case HANDLER_WHAT_NET: if (httpListener != null) httpListener.onAfinalFail(result.requestId); break; case EXIT: break; default: break; } } };
最后的最后,我回到中间层补充完最后那段代码,
netRequest.requestCheckVersion(1,true);
1.这个接口很简单,我需要参数就是包名而已
2.requestData 传递serviceAction,requestId,ifShowDialog,params
3.requestData则调用网络基类中的那个request
private BaseHttpRequest request; //网络请求的基类
/** * @param requestId 网络请求id,用于区分同个页面的不同网络请求 * @param ifShowDialog 是否显示提示框 */ public void requestData(String serviceAction, int requestId, boolean ifShowDialog, AjaxParams params) { request.requestData(serviceAction, requestId, ifShowDialog, params); }
结束!!!!!!!!!!终于写完了,到了这里,有的同学会说,不是说所有网络框架通吃么,那么怎么换呢?你要做的只是把底层那个BaseHttpRequst的post请求方式改为你那个网络框架的post请求方式,其他全都不用变。。毕竟思想是一样的,大道至简!!