本文介绍如何抛弃那些高大上的、花里胡哨的、适配所有的完美http封装(壳子而已)。
首先,OKhttp是现在所有安卓开发的网络请求框架(还在用Apache的我就不多说什么了,自行绕道),那些所谓的完美封装只是在OKhttp上加了几行代码而已。
其次,那些封装也是要考虑全局性,只是把OKhttp简化了一两行而已,真正用的时候并没有方便太多。如下,网络请求获取text并展示需要做的操作
由于多数博客鱼龙混杂,本博客如果让你非常满意或解决了大家的根本性问题,希望多多支持在下方点赞和回复一下,举手之劳方便大家。
final Dialog loading=new LaodingDialog(this);//添加进度条
Body body=new Body();
body.put("token","xxx");
body.put("activityId",1);
XXHttp.getInstance()//单例(自己还单独封装了一层,设置了请求时间等)
.addHeader(new Header().add("userId",1).add("type","json")...)//添加header
.url("http...").tag("http...").post()//添加url并设置post
.body(body.build())//添加body
.build().execute(new XXListener(){//回调
@Override
public void onError(int errorCode) {//网络失败相关
loading.dismiss();//关闭对话框
switch (errorCode){
case 1:
//提示1
break;
...
default:
break;
}
}
@Override
public void onSuccess(final Response response) {//网络畅通
runOnUiThread(new Runnable() {//需要回到主线程,有的甚至还用了handler
@Override
public void run() {
loading.dismiss();
if (response.code()==200){//需要判断请求状态
XXBean bean = JSON.parseObject(body, XXBean.Class);//解析后台数据
if (bean.code==200){//后台状态码
if (!XXActivity.this.isFinish()){//还得判断Activity是否挂了
mTv.setText(bean.data.text);//终于完成了
}
}else {
//提示2
}
}else {
//提示3
}
}
});
}
});
虽然写的可能有些极端,但上述问题或多或少都有遇到,下面我就给大家展示一下封装后的效果吧
HttpUtils.postDialog(this, "http...",
MapUtils.getHttpInstance().put("activityId", 1),
XXBean.class, new OKHttpListener() {
@Override
public void onSuccess(XXBean bean) {
mTv.setText(bean.data.text);
}
});
由一个40多行冗长的代码变成了8行简短的代码,上面所做的这里都有,这到底经历了什么?请看下回分析...?
所谓条条大路通罗马,你要想请求到网络数据可谓是有千万种方式,对于我们普通人来讲虽然方式多,但世界上这么多路根本无从选择。
所以OKhttp诞生了,OKhttp将路修好并规范化
虽然路是修好了,但路依然很多,经常会出现走反、绕路或到死胡同的情况。
所以一些第三方框架也诞生了,将路添加了导航功能,这样就一定能到达罗马
虽然第三方框架最终能到达,但每次到路口都需要查看路牌,看路牌就要浪费时间。
其实我们的目的其实很简单,就是快速的到达罗马。根据开车经验,我们只需要熟悉几条路即可(防止堵车),如下才是我们真正需要的
不扯这么远了,进入正题吧
还是老样子,先理思路:
1.封装基类问题:别人的封装是基于OKhttp的,但自己用的比较繁琐,所以直接封装OKhttp;
2.header问题:每次都要加上userId和json,所以应该将header封装进去;
3.body问题:我们的body应该是链式的,这样方便put,还有token当然也要封装进去;
4.dialog问题:我的项目默认是showdialog的,所以也需要封装进去,但不是每个接口都要dialog,所以可以为null;
5.线程问题:线程切换太麻烦,当然必须在封装层解决,另外Thread当然不如AsyncTask了(有人说AsyncTask100多个线程就不行了,请问你的app同时开了100多线程还能用吗,系统的东西不是随随便便就玩坏的);
6.网络问题:每次都要判断网络的正确性,当然必须封装了;
7.解析问题:每次都要fastjson解析一下不烦吗,封装起来岂不更好;
8.服务器返回不同状态码问题:这个我也懒得管,当然封装;
9.Activity finish问题:没有对Activity finish判断,经常崩溃,但判断又很烦躁,你说呢;
implementation 'com.squareup.okhttp3:okhttp:3.6.0'
implementation 'com.squareup.okio:okio:1.6.0'
private static final OkHttpClient mClient = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.readTimeout(20, TimeUnit.SECONDS)
.build();
有人说,既然链式编程这么火,当然是如下这样了
HttpUtils.getInstance().setActivity(this).url("http...").setJsonClass(XXBean.Class)
.body(new Body().put("activityId",1)).post().clinet(new ...);
但有没有想过链式编程也是要一步一步的,这和别人封装的不就一样了吗,你的路口只是比别人少了几个而已。我说过,我们是要直通罗马的,显然链式编程并不适合我们。
初步思想当然是先写一个支持post、get、dialog、解析、finish、body、服务器判断的方法了
/**
*
* @param activity 可以对finish进行判断
* @param httpUrl
* @param body
* @param type post还是get
* @param dialog
* @param mClass 有class才能解析
* @param 要想对服务器的状态判断当然必须有个BaseBean了
* @param listener 回调监听
*/
public static void http(Activity activity, String httpUrl, Map body,@type int type, Dialog dialog,Class mClass,OKHttpListener listener){
}
但是这参数也太太太多了吧。。。其实作为工具类就一个方法合适吗。所以我们应该重载派生一些出来。
将其拆分成post和get,当你写Request.Builder就会发现post和get都是它,另外get的body并没太大卵用,还有重载并不能明显区分post和get,所以就成了这样
//类型 utf-8
public static final MediaType mMediaType = MediaType.parse("application/json;charset=UTF-8");
public static void httpGet(Activity activity, String httpUrl, Dialog dialog, Class mClass, OKHttpListener listener) {
http(activity, httpUrl, new Request.Builder(), dialog, mClass, listener);
}
public static void httpPost(Activity activity, String httpUrl, Map body, Dialog dialog, Class mClass, OKHttpListener listener) {
if (body.get("token") == null) {//已经有了当然最好不要替换了
body.put("token", 用户token);
}
http(activity, httpUrl, new Request.Builder().post(RequestBody.create(mMediaType, JSON.toJSONString(body))), dialog, mClass, listener);
}
public static void http(Activity activity, String httpUrl, Request.Builder builder, Dialog dialog, Class mClass, OKHttpListener listener) {
}
再将httpGet、httpPost拆分成httpGetDialog、httpGetDefault、httpPostDialog、httpPostDefault,此时已经无法再分了,也许你感觉这样分有什么用,等你用的时候就知道了,只要不超过10个还是非常方便的。
等你封装完成之后使用时就会发现:1.有些数据在Activity finish时可能还需要用到;2.类似HashMap不是链式编程,使用不方便;3.特殊情况的网络请求无法解决。
解决方案:1.为了可拓展性,写一个接口HttpInterface由Activity的基类继承即可,这样的话基类fragment、基类adapter等继承之后都可以愉快的传this了
public interface HttpInterface {
Activity getActivity();
//是否丢弃http请求的数据数据
boolean isDiscardHttp();
}
2.写一个Map直接继承JSONObject(看准了是fastjson的),将put重写即可,并且toString就可以变成json字符串
@MainThread
public final class MapUtils extends JSONObject implements Serializable {
private static MapUtils mMap = new MapUtils();
private MapUtils() {
}
/**
* 返回自己
*/
@Override
public MapUtils put(@NonNull String k, Object v) {
if (v instanceof CharSequence) {//CharSequence说明是字符串
v = v.toString();
}
super.put(k, v);
return this;
}
@Nullable
public Object get(@NonNull String k) {
return super.get(k);
}
///////////////////////////////////////////////////////////////////////////
// 以下是获取方法
///////////////////////////////////////////////////////////////////////////
//获得一个新的实例
public static MapUtils getNewInstance() {
return new MapUtils();
}
/**
* 只能使用在网络请求,其他地方请使用{@link JSONObject}或{@link #getNewInstance}
* 获得单一实例,注意代码执行顺序,当是list的时候使用{@link #getNewInstance}或直接使用{@link com.alibaba.fastjson.JSONArray}
*/
public static MapUtils getHttpInstance() {
mMap.clear();
return mMap;
}
}
3.将最初的http再次抽象剥离(可能参数更多,主要是为了备用)
public static void httpCustom(final HttpInterface httpInterface, final String httpUrl,
Request.Builder builder, final DialogPopwindowInterface dialog,
final Class mClass, final OkHttpClient client, final OKHttpListener listener) {
由于代码太多,学习地址见:https://github.com/weimingjue/http