参考链接

https://www.jianshu.com/p/e1d9ddc86c7d

先看几个demo demo的目的很简单 就是发送一个Http请求

先给出几个工具类的代码 各个demo都依赖工具类或者公共类的代码

public class BaseApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 测试不同的demo时记得切换导包PreferencesUtil.getInstance().init(this);// 初始化xUtilsx.Ext.init(this);x.Ext.setDebug(BuildConfig.DEBUG); // 是否输出debug日志, 开启debug会影响性能.HttpUtils.initHttpRequest(new OKHttpRequest());}
}public class ConstantValue {public static final class UrlConstant{public static final String BASE_URL = "http://is.snssdk.com/2/essay/";public static final String HOME_DISCOVERY_URL = BASE_URL+"discovery/v3/?";}
}public class Utils {private Utils() {}// 字符串拼接public static String jointParams(String url, Map<String, Object> params) {if (params == null || params.size() <= 0) {return url;}StringBuffer stringBuffer = new StringBuffer(url);if (!url.contains("?")) {stringBuffer.append("?");} else {if (!url.endsWith("?")) {stringBuffer.append("&");}}for (Map.Entry<String, Object> entry : params.entrySet()) {stringBuffer.append(entry.getKey() + "=" + entry.getValue() + "&");}stringBuffer.deleteCharAt(stringBuffer.length() - 1);return stringBuffer.toString();}/*** 解析一个类上面的class信息**/public static Class<?> analysisClazzInfo(Object object) {Type genType = object.getClass().getGenericSuperclass();Type[] params = ((ParameterizedType) genType).getActualTypeArguments();return (Class<?>) params[0];}
}

demo1

public class MainActivity extends AppCompatActivity {private static final String TAG = "Sample1.MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/********************访问网络开始*******************/OkHttpClient mOkHttpClient = new OkHttpClient();Map<String, Object> params = new HashMap<>();// 特定参数params.put("iid", 6152551759L);params.put("aid", 7);// 公共参数params.put("app_name", "joke_essay");params.put("version_name", "5.7.0");params.put("ac", "wifi");params.put("device_id", "30036118478");params.put("device_brand", "Xiaomi");params.put("update_version_code", "5701");params.put("manifest_version_code", "570");params.put("longitude", "113.000366");params.put("latitude", "28.171377");params.put("device_platform", "android");final String jointUrl = Utils.jointParams(ConstantValue.UrlConstant.HOME_DISCOVERY_URL, params);  //打印Log.e(TAG, "url:" + jointUrl);Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(this);//可以省略,默认是GET请求Request request = requestBuilder.build();mOkHttpClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, final IOException e) {// 失败Log.e(TAG, "fail " + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {// 内涵段子上面的url请求的网络服务其实已经关闭final String resultJson = response.body().string();Log.e(TAG, "success " + resultJson);}});/********************访问网络结束*******************/}
}

demo看起来很简洁,所有请求参数 发起请求 以及请求 但是每一次我们调用http请求的时候都要添加这些冗杂的参数么 于是有了demo2

demo2

public class MainActivity extends AppCompatActivity {private static final String TAG = "Sample2.MainActivity";/*** 相比于sample1 sample2将冗余繁杂的重复参数抽取出来放在公共的get方法中* 并且外部只需要传递一个CallBack即可 请求和CallBack的处理都集中放在工具类中*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/********************访问网络开始*******************/Map<String, Object> params = new HashMap<>();// 特定参数params.put("iid", 6152551759L);params.put("aid", 7);HttpUtils.get(this, ConstantValue.UrlConstant.HOME_DISCOVERY_URL, params, new Callback() {@Overridepublic void onFailure(Call call, final IOException e) {// 失败Log.e(TAG, "fail " + e.getMessage());}@Overridepublic void onResponse(Call call, Response response) throws IOException {final String resultJson = response.body().string();Log.e(TAG, "success " + resultJson);}});/********************访问网络结束*******************/}
}
public class HttpUtils {private static final String TAG = "Sample2";private HttpUtils() {}public static void get(Context context, String url, Map<String, Object> params, final Callback callback) {OkHttpClient mOkHttpClient = new OkHttpClient();// 公共参数params.put("app_name", "joke_essay");params.put("version_name", "5.7.0");params.put("ac", "wifi");params.put("device_id", "30036118478");params.put("device_brand", "Xiaomi");params.put("update_version_code", "5701");params.put("manifest_version_code", "570");params.put("longitude", "113.000366");params.put("latitude", "28.171377");params.put("device_platform", "android");final String jointUrl = Utils.jointParams(url, params);  //打印Log.e(TAG, "url:" + jointUrl);Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(context);//可以省略,默认是GET请求Request request = requestBuilder.build();mOkHttpClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, final IOException e) {// 失败callback.onFailure(call, e);}@Overridepublic void onResponse(Call call, Response response) throws IOException {// 1.JSON解析转换// 2.显示列表数据// 3.缓存数据callback.onResponse(call, response);}});}
}

demo2在HttpUtils封装了get方法

1.这样外部只需要添加几个特殊的参数 而不需要列出冗杂的参数,可以一定程度做到代码的复用

2.OkHttpClient的使用也被封装到方法内部 外部调用者(MainActivity)不需要了解OkHttpClient的存在

3.外部传递的callback在get方法中可以进行统一的处理之后在返回给调用者

但是新的需求来了 我们希望增加一个缓存的功能 如何做呢

demo3

public class MainActivity extends AppCompatActivity {private static final String TAG = "Sample3.MainActivity";/*** Sample3 比起Sample2* 1.多考虑了缓存问题 对比缓存数据是否和cloud最新数据一致* 先显示缓存数据* 如果一致 直接返回* 如果不一致 更新界面 将新数据缓存* 2.使用了泛型类型的HttpCallBack*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/********************访问网络开始*******************/Map<String, Object> params = new HashMap<>();// 特定参数params.put("iid", 6152551759L);params.put("aid", 7);HttpUtils.get(this, ConstantValue.UrlConstant.HOME_DISCOVERY_URL,params,new HttpCallBack<DiscoverListResult>() {@Overridepublic void onSuccess(DiscoverListResult result) {if (result.isOK()) {// 没有列表数据的情况, 打印 Toast 或者做一些其他处理Log.e(TAG, "success is OK "+result);} else {// 有数据列表的情况,显示列表// showListData(result);Log.e(TAG, "success is NG "+result);}}@Overridepublic void onFailure(Exception e) {Log.e(TAG, "fail " + e.getMessage());}},true);/********************访问网络结束*******************/}
}

http请求的调用者没发生多大的变化 倒是get方法的最后一个参数由OKHttp的CallBack变成了我们自己定义的带有泛型类型的回调 其定义如下

public abstract class HttpCallBack<T>{// 返回可以直接操作的对象public abstract void onSuccess(T result);public abstract void onFailure(Exception e);
}

这样可以规范回调的返回类型 方便外部的调用 至于DiscoverListResult的具体实现不需要了解 想象它是一个Student差不多的类型即可 不过也需要考虑当返回的DiscoverListResult如果数据不完整如何处理,比如返回的数据缺少一些必要的数据 或者返回就不是一个有效的DiscoverListResult对象 这时应该考虑增加一个isOK类似的接口判断数据是否可用

public class HttpUtils {private static String TAG = "Sample3";private HttpUtils() {}public static <T> void get(Context context, String url, Map<String, Object> params, final HttpCallBack<T> callback, final boolean cache) {OkHttpClient mOkHttpClient = new OkHttpClient();// 公共参数params.put("app_name", "joke_essay");params.put("version_name", "5.7.0");params.put("ac", "wifi");params.put("device_id", "30036118478");params.put("device_brand", "Xiaomi");params.put("update_version_code", "5701");params.put("manifest_version_code", "570");params.put("longitude", "113.000366");params.put("latitude", "28.171377");params.put("device_platform", "android");final String jointUrl = Utils.jointParams(url, params);  //打印Log.e(TAG, " url: " + jointUrl);// 缓存问题// 缓存写到  SP 里面, 考虑多级缓存(比如说 最经常使用的放在内存中,2天内使用的数据放在数据库,其余放在文件中 )final String cacheJson = (String) PreferencesUtil.getInstance().getParam(jointUrl, "");// 写一大堆多级缓存处理逻辑,内存怎么扩展等等// 这里只考虑了一级缓存if (cache && !TextUtils.isEmpty(cacheJson)) {// 从缓存中取得数据Gson gson = new Gson();// data:{"name","darren"}   data:"请求失败"T objResult = (T) gson.fromJson(cacheJson,Utils.analysisClazzInfo(callback));callback.onSuccess(objResult);}// 同时并行得实时查询cloud数据Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(context);//可以省略,默认是GET请求Request request = requestBuilder.build();mOkHttpClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, final IOException e) {// 失败callback.onFailure(e);}@Overridepublic void onResponse(Call call, Response response) throws IOException {final String resultJson = response.body().string();Log.e(TAG, resultJson.equals(cacheJson) + "");if (cache && resultJson.equals(cacheJson)) {// 缓存数据与cloud数据相同 直接返回return;}// 1.JSON解析转换// 2.显示列表数据// 3.缓存数据Gson gson = new Gson();// data:{"name","darren"}   data:"请求失败"T objResult;try {objResult = (T) gson.fromJson(resultJson,Utils.analysisClazzInfo(callback));} catch (Exception e) {Log.e(TAG, "onResponse: " + e.getMessage());e.printStackTrace();return;}if (objResult != null) {callback.onSuccess(objResult);}if (cache) {// 缓存数据PreferencesUtil.getInstance().saveParam(jointUrl, resultJson);}}});}
}

HttpUtils方面主要是增加了缓存的判断逻辑 并且在处理回调时增加了对对象类型的转换逻辑 将对象从Json对象转换为某个类型的对象 注意这里的onSuccess可能发生两次 一次是读取到缓存数据发生的 一次是真的访问网络获取数据 其实HttpCallBack的onSuccess可以增加一个参数 说明是从缓存获取的数据还是从cloud获取

最后缓存是存放在sharedPreference的 部分逻辑如下

public class PreferencesUtil {private SharedPreferences preferences = null;private SharedPreferences.Editor editor = null;private Object object;public static PreferencesUtil preferencesUtil;public static PreferencesUtil getInstance() {if (preferencesUtil == null) {synchronized (PreferencesUtil.class) {if (preferencesUtil == null) {// 使用双重校验锁preferencesUtil = new PreferencesUtil();}}}return preferencesUtil;}public void init(Context context){/*** 问题在于,这个Context哪来的我们不能确定,很大的可能性,你在某个Activity里面为了方便,直接传了个this;* 这样问题就来了,我们的这个类中的sInstance是一个static且强引用的,在其内部引用了一个Activity作为Context,也就是说,* 我们的这个Activity只要我们的项目活着,就没有办法进行内存回收。而我们的Activity的生命周期肯定没这么长,造成了内存泄漏。* 所以这里使用context.getApplicationContext()*/preferences = PreferenceManager.getDefaultSharedPreferences(context.getApplicationContext());}private PreferencesUtil() {}/*** 保存数据 所有的类型都适用** 以key为key object为value 保存信息到shared preference*/public synchronized void saveParam(String key, Object object) {if (editor == null)editor = preferences.edit();// 得到object的类型String type = object.getClass().getSimpleName();if ("String".equals(type)) {// 保存String 类型editor.putString(key, (String) object);} else if ("Integer".equals(type)) {// 保存integer 类型editor.putInt(key, (Integer) object);} else if ("Boolean".equals(type)) {// 保存 boolean 类型editor.putBoolean(key, (Boolean) object);} else if ("Float".equals(type)) {// 保存float类型editor.putFloat(key, (Float) object);} else if ("Long".equals(type)) {// 保存long类型editor.putLong(key, (Long) object);} else {if (!(object instanceof Serializable)) {throw new IllegalArgumentException(object.getClass().getName() + " 必须实现Serializable接口!");}// 不是基本类型则是保存对象ByteArrayOutputStream baos = new ByteArrayOutputStream();try {ObjectOutputStream oos = new ObjectOutputStream(baos);oos.writeObject(object);String productBase64 = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT);editor.putString(key, productBase64);Log.d(this.getClass().getSimpleName(), "save object success");} catch (IOException e) {e.printStackTrace();Log.e(this.getClass().getSimpleName(), "save object error");}}editor.commit();}/*** 移除信息*/public synchronized void remove(String key) {if (editor == null)editor = preferences.edit();editor.remove(key);editor.commit();}/*** 从shared preference 获取缓存数据的方法,所有类型都适用** @param key* @param defaultObject* @return*/public Object getParam(String key, Object defaultObject) {if (defaultObject == null) {return getObject(key);}String type = defaultObject.getClass().getSimpleName();if ("String".equals(type)) {return preferences.getString(key, (String) defaultObject);} else if ("Integer".equals(type)) {return preferences.getInt(key, (Integer) defaultObject);} else if ("Boolean".equals(type)) {return preferences.getBoolean(key, (Boolean) defaultObject);} else if ("Float".equals(type)) {return preferences.getFloat(key, (Float) defaultObject);} else if ("Long".equals(type)) {return preferences.getLong(key, (Long) defaultObject);}// 以上所有类型都不是 就是序列化对象了return getObject(key);}public Object getObject(String key) {String wordBase64 = preferences.getString(key, "");byte[] base64 = Base64.decode(wordBase64.getBytes(), Base64.DEFAULT);ByteArrayInputStream bais = new ByteArrayInputStream(base64);try {ObjectInputStream bis = new ObjectInputStream(bais);object =  bis.readObject();Log.d(this.getClass().getSimpleName(), "Get object success");return object;} catch (Exception e) {}Log.e(this.getClass().getSimpleName(), "Get object is error");return null;}
}

demo4

我们将HttpUtils的get方法改造 运用上builder设计模式

改造完如下

public class HttpUtils {private OKHttpRequest mHttpRequest;private final int TYPE_POST = 0x0011, TYPE_GET = 0x0022;private int mType = TYPE_GET;// 用于临时存储参数private Map<String, Object> mParams;private String mUrl;private boolean mCache = false;private Context mContext;private static String TAG = "Sample4";// 使用了builder设计模式public static HttpUtils with(Context context) {return new HttpUtils(context);}private HttpUtils(Context context) {mHttpRequest = new OKHttpRequest();mParams = new HashMap<>();this.mContext = context;}public HttpUtils param(String key, Object value) {mParams.put(key, value);return this;}public HttpUtils url(String url) {mUrl = url;return this;}public HttpUtils cache(boolean cache) {mCache = cache;return this;}public <T> void request() {request(null);}public <T> void request(final HttpCallBack<T> callback) {// 异常判断if (mContext == null) {Log.e(TAG, "request: mContext is null,请调用with方法!");}if (mType == TYPE_GET) {mHttpRequest.get(mContext, mUrl, mParams, callback, mCache);} else {mHttpRequest.post(mContext, mUrl, mParams, callback, mCache);}}public HttpUtils get() {mType = TYPE_GET;return this;}// 外部直接调用该方法 传递参数太多会觉得很难用 应该提供较少的参数的方法 外部不需要知道所有参数的意义// 真正想用的参数自己调用方法设定即可 没有传递的参数可以使用默认值/*public static <T> void get(Context context, String url, Map<String, Object> params, final HttpCallBack<T> callback, final boolean cache) {}*/
}

然后调用端修改如下

    private static final String TAG = "Sample4.MainActivity";/*** Sample4 比起Sample3* 1.HttpUtils使用了builder设计模式* 2.将请求单独新建为OKHttpRequest 与 HttpUtils分离 结构更清晰* 同时 缓存部分也新建为一个类 与 HttpUtils分离*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/********************访问网络开始*******************/HttpUtils.with(this).cache(true).get().param("iid", 6152551759L).param("aid", 7).url(ConstantValue.UrlConstant.HOME_DISCOVERY_URL).request(new HttpCallBack<DiscoverListResult>() {@Overridepublic void onSuccess(DiscoverListResult result) {if (result.isOK()) {// 没有列表数据的情况, 打印 Toast 或者做一些其他处理} else {// 有数据列表的情况,显示列表}}@Overridepublic void onFailure(Exception e) {}});/********************访问网络结束*******************/}
}

这样的有时是可以省略一些不必要的参数 比如默认的请求方式为get 是否缓存的默认值为false

那么调用者可以不添加这两个参数。事实上 传递的参数越少 外部的调用者会越觉得该方法调用起来简单

此外还有重要的一点是从HttpUtils中抽取出http相关的东西单独构成类OKHttpRequest

这样HttpUtils只负责存储各种参数 OKHttpRequest则专门处理Http相关的逻辑 做到分工明确 职责单一

最后 我们封装出一个SPHttpCache 代码如下

public class SPHttpCache {public void saveCache(String finalUrl,String resultJson){PreferencesUtil.getInstance().saveParam(finalUrl,resultJson);}public String getCache(String finalUrl){return (String) PreferencesUtil.getInstance().getObject(finalUrl);}
}

PreferencesUtil则没有变化 这样让OKHttpRequest不是直接依赖PreferencesUtil

SPHttpCache起到纽带的作用,OKHttpRequest依赖SPHttpCach,SPHttpCache调用PreferencesUtil 这样做的目的也是解耦(OKHttpRequest与PreferencesUtil 不直接关联),让我们可以轻易替换SPHttpCache为其他存储方式

demo5

最后 我们需要能灵活切换http的引擎 比如切换OKHttp为XUtils 如何做呢? 这就是面向接口编程了

首先提供一个接口

public interface IHttpRequest {<T> void get(Context context, String url, Map<String, Object> params,final HttpCallBack<T> callback, final boolean cache);<T> void post(Context context, String url, Map<String, Object> params,final HttpCallBack<T> callback, final boolean cache);<T> void download(Context context, String url, Map<String, Object> params,final HttpCallBack<T> callback);<T> void upload(Context context, String url, Map<String, Object> params,final HttpCallBack<T> callback);
}

OKHttpRequest和XUtilsRequest都实现了IHttpRequest接口

public class OKHttpRequest implements IHttpRequest{private static final String TAG = "Sample4.OKHttpRequest";private SPHttpCache mHttpCache;public OKHttpRequest() {mHttpCache = new SPHttpCache();}// 参数还是很多@Overridepublic <T> void get(Context context, String url, Map<String, Object> params,final HttpCallBack<T> callback, final boolean cache) {OkHttpClient mOkHttpClient = new OkHttpClient();// 公共参数params.put("app_name", "joke_essay");params.put("version_name", "5.7.0");params.put("ac", "wifi");params.put("device_id", "30036118478");params.put("device_brand", "Xiaomi");params.put("update_version_code", "5701");params.put("manifest_version_code", "570");params.put("longitude", "113.000366");params.put("latitude", "28.171377");params.put("device_platform", "android");final String jointUrl = Utils.jointParams(url, params);  //打印// 缓存问题Log.e(TAG, "request: mUrl "+jointUrl); // 缓存写到  SP 里面,多级缓存(内存中 30条,数据库 ,文件中 )final String cacheJson = mHttpCache.getCache(jointUrl);// 写一大堆处理逻辑 ,内存怎么扩展等等if (cache && !TextUtils.isEmpty(cacheJson)) {Gson gson = new Gson();// data:{"name","darren"}   data:"请求失败"T objResult = (T) gson.fromJson(cacheJson,Utils.analysisClazzInfo(callback));callback.onSuccess(objResult);}Request.Builder requestBuilder = new Request.Builder().url(jointUrl).tag(context);//可以省略,默认是GET请求Request request = requestBuilder.build();mOkHttpClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, final IOException e) {// 失败callback.onFailure(e);}@Overridepublic void onResponse(Call call, Response response) throws IOException {final String resultJson = response.body().string();Log.e(TAG, resultJson.equals(cacheJson) + "");if (cache && resultJson.equals(cacheJson)) {return;}// 1.JSON解析转换// 2.显示列表数据// 3.缓存数据Gson gson = new Gson();// data:{"name","darren"}   data:"请求失败"T objResult;try {objResult = (T) gson.fromJson(resultJson,Utils.analysisClazzInfo(callback));} catch (Exception e) {Log.e(TAG, "onResponse: " + e.getMessage());e.printStackTrace();return;}if (objResult != null) {callback.onSuccess(objResult);}if (cache) {mHttpCache.saveCache(jointUrl, resultJson);}}});}@Overridepublic <T> void post(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback, boolean cache) {}@Overridepublic <T> void download(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {}@Overridepublic <T> void upload(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {}
}public class XUtilsRequest implements IHttpRequest {private static final String TAG = "Sample5.XUtilsRequest";private SPHttpCache mHttpCache;public XUtilsRequest() {mHttpCache = new SPHttpCache();}// 参数还是很多public <T> void get(Context context, String url, Map<String, Object> params,final HttpCallBack<T> callback, final boolean cache) {// 公共参数params.put("app_name", "joke_essay");params.put("version_name", "5.7.0");params.put("ac", "wifi");params.put("device_id", "30036118478");params.put("device_brand", "Xiaomi");params.put("update_version_code", "5701");params.put("manifest_version_code", "570");params.put("longitude", "113.000366");params.put("latitude", "28.171377");params.put("device_platform", "android");final String jointUrl = Utils.jointParams(url, params);// 缓存问题final String cacheJson = mHttpCache.getCache(jointUrl);// 写一大堆处理逻辑 ,内存怎么扩展等等if (cache && !TextUtils.isEmpty(cacheJson)) {Gson gson = new Gson();// data:{"name","darren"}   data:"请求失败"T objResult = (T) gson.fromJson(cacheJson,Utils.analysisClazzInfo(callback));callback.onSuccess(objResult);}Log.e(TAG, "request: mUrl " + jointUrl);RequestParams requestParams = new RequestParams();requestParams.setUri(jointUrl);x.http().get(requestParams, new org.xutils.common.Callback.CommonCallback<String>() {@Overridepublic void onSuccess(String result) {Log.e(TAG, "onSuccess: " + result);}@Overridepublic void onError(Throwable ex, boolean isOnCallback) {Log.e(TAG, "onSuccess: " + ex.getMessage());ex.printStackTrace();}@Overridepublic void onCancelled(CancelledException cex) {Log.e(TAG, "onCancelled: " + cex.getMessage());cex.printStackTrace();}@Overridepublic void onFinished() {Log.e(TAG, "onFinished: ");}});}@Overridepublic <T> void post(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback, boolean cache) {}@Overridepublic <T> void download(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {}@Overridepublic <T> void upload(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {}
}

然后使用使用到HttpRequest的地方都使用IHttpRequest 这样

其中提供了

    // 重点:提供给外部用于切换引擎的接口public HttpUtils httpRequest(IHttpRequest httpRequest){mHttpRequest = httpRequest;return this;}

用于中途无缝切换引擎

public class HttpUtils {// 重点 面向接口编程 使用IHttpRequest接口对象 而不是实现类// 可以中途切换Http引擎 比如换成Xutilsprivate IHttpRequest mHttpRequest;//初始化Http引擎private static IHttpRequest mInitHttpRequest;private final int TYPE_POST = 0x0011, TYPE_GET = 0x0022;private int mType = TYPE_GET;// 用于临时存储参数private Map<String, Object> mParams;private String mUrl;private boolean mCache = false;private Context mContext;private static String TAG = "Sample5";// 使用了builder设计模式public static HttpUtils with(Context context) {return new HttpUtils(context);}private HttpUtils(Context context) {mHttpRequest = new OKHttpRequest();mParams = new HashMap<>();this.mContext = context;}// 提供给外部初始化Http引擎的接口public static void initHttpRequest(IHttpRequest httpRequest) {mInitHttpRequest = httpRequest;}// 重点:提供给外部用于切换引擎的接口public HttpUtils httpRequest(IHttpRequest httpRequest){mHttpRequest = httpRequest;return this;}public HttpUtils param(String key, Object value) {mParams.put(key, value);return this;}public HttpUtils url(String url) {mUrl = url;return this;}public HttpUtils cache(boolean cache) {mCache = cache;return this;}public <T> void request() {request(null);}public <T> void request(final HttpCallBack<T> callback) {if(mHttpRequest == null){mHttpRequest = mInitHttpRequest;}// 异常判断if (mContext == null) {Log.e(TAG, "request: mContext is null,请调用with方法!");}mHttpRequest.get(mContext, mUrl, mParams, callback, mCache);}public HttpUtils get() {mType = TYPE_GET;return this;}// 外部直接调用该方法 传递参数太多会觉得很难用 应该提供较少的参数的方法 外部不需要知道所有参数的意义// 真正想用的参数自己调用方法设定即可 没有传递的参数可以使用默认值/*public static <T> void get(Context context, String url, Map<String, Object> params, final HttpCallBack<T> callback, final boolean cache) {}*/
}

调用者变化不大

public class MainActivity extends AppCompatActivity {private static final String TAG = "Sample5.MainActivity";/*** Sample5 比起Sample4* 1.面向接口编程 HttpUtils中的IHttpRequest是一个接口 可以被赋值为任意接口实现者 如果需要切换引擎 十分方便* 同时提供了XUtils的请求 使用XUtils访问网络 可以灵活切换使用的网络引擎*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);/********************访问网络开始*******************/HttpUtils.with(this).cache(true).get()//.httpRequest(new XUtilsRequest())//中途切换引擎.param("iid", 6152551759L).param("aid", 7).url(ConstantValue.UrlConstant.HOME_DISCOVERY_URL).request(new HttpCallBack<DiscoverListResult>() {@Overridepublic void onSuccess(DiscoverListResult result) {if (result.isOK()) {// 没有列表数据的情况, 打印 Toast 或者做一些其他处理} else {// 有数据列表的情况,显示列表}}@Overridepublic void onFailure(Exception e) {}});/********************访问网络结束*******************/}
}

我们可以关注17行 这里可以灵活的切换其他http引擎

事实上SPHttpCache也可抽取出一个接口 比如

public interface ICache {void saveCache(String finalUrl,String resultJson);String getCache(String finalUrl);
}

然后让FileCache和SPHttpCache分别实现该接口

这样我们可以像切换Http引擎一样灵活的切换缓存类型

完整代码

https://github.com/caihuijian/LearnCodeArchitecture2/tree/master/sixprinciple

下面我们结合上面的代码一点点理解面向对象的6个基本原则

1.单一职责原则

单一职责原则的英文名称是Single Responsibility Principle,简称SRP。它的定义是:就一个类而言,应该仅有一个引起它变化的原因。简单来说,一个类中应该是一组相关性很高的函数、数据的封装。

比如我们改造demo3为4的时候 从HttpUtils中抽取出http相关的东西单独构成类OKHttpRequest

这样HttpUtils只负责存储各种参数 OKHttpRequest则专门处理Http相关的逻辑 这里就是遵守了单一职责的原则

2.开闭原则

开闭原则的英文全称是Open Close Principle,简称OCP。开闭原则的定义是:软件中的对象(类、模块、函数等)应该对于扩展是开放的,但是,对于修改是封闭的。简而言之,对修改关闭 对扩展开发---->便于扩展 这里要求我们定义对象的时候使用接口或者父类,创建具体对象的时候才使用子类 这样我们后期可以轻易修改子类(实现对象)

比如我们在Demo4升级到Demo5时 我们能够无缝切换引擎就是因为我们在HttpUtils不直接引用具体的实现类而是使用的接口IHttpRequest 这样后期我们可以灵活切换 后面如果我们想进一步扩展 比如用Volley Retrofit 增加IHttpRequest的实现类即可,而不是通过修改原有代码来实现新功能

3.里氏替换原则

里氏替换原则英文全称是Liskov Substitution Principle,简称LSP。我们知道,面向对象的语言的三大特点是继承、封装、多态,里氏替换原则就是依赖于继承、多态这两大特性。里氏替换原则简单来说就是,所有引用基类的地方必须能透明地使用其子类的对象。通俗点讲,只要父类能出现的地方子类就可以出现。但是,反过来就不行了,有子类出现的地方,父类未必就能适应。

该原则适用于接口的实现与基类的继承

    public static void initHttpRequest(IHttpRequest httpRequest) {//这里定义的传入的是接口mInitHttpRequest = httpRequest;}//实际使用 可以使用任意实现者替换HttpUtils.initHttpRequest(new OKHttpRequest());HttpUtils.initHttpRequest(new XUtilsRequest());// 又比如RecyclerView的方法 定义的时候参数为基类public void setLayoutManager(@Nullable LayoutManager layout) {......}// 使用的时候 可以用任意子类替换mRecyclerView.setLayoutManager(new LinearLayoutManager(this));mRecyclerView.setLayoutManager(new GridLayoutManager(this,3));        

4.依赖倒置原则

依赖倒置原则英文全称是Dependence Inversion Principle,简称DIP。依赖反转原则指代了一种特定的解耦形式,高层模块不依赖低层次模块的细节,说白了高层次就是不依赖细节而是依赖抽象。

比如demo5中

HttpUtils依赖接口就是这个原则的体现

5.接口隔离原则

接口隔离原则英文全称是InterfaceSegregation Principles,简称ISP。它的定义是:客户端不应该依赖它不需要的接口。另一种定义是:类间的依赖关系应该建立在最小的接口上。接口隔离原则将非常庞大、臃肿的接口拆分成为更小的和更具体的接口,这样客户将会只需要知道他们感兴趣的方法。接口隔离原则的目的是系统解开耦合,从而容易重构、更改和重新部署,让客户端依赖的接口尽可能地小。

即接口尽量小 当然极端情况是每个接口就一个方法 显然这是不合适的,那么我们可以结合职责单一原则 使接口尽量小

6.最少知识原则

参考链接

https://blog.csdn.net/qq_38844728/article/details/88739574

最少知识原则又称为迪米特原则英文全称为Law of Demeter,简称LOD,虽然名字不同,但描述的是同一个原则:一个对象应该对其他对象有最少的了解。通俗地讲,一个类应该对自己需要耦合或调用的类知道得最少,类的内部如何实现、如何复杂都与调用者或者依赖者没关系,调用者或者依赖者只需要知道他需要的方法即可,其他的我一概不关心。类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大。

迪米特法则还有一个英文解释是:Only talk to your immedate friends,翻译过来就是:只与直接的朋友通信。个对象应当对其它对象有尽可能少的了解,不要和陌生人说话。

强调只和朋友说话,不和陌生人说话。这里的朋友指的是:出现在成员变量,方法输入,输出参数中的类称为成员朋友类,而出现在方法体内部的类不属于朋友类。

迪米特法则初衷在于降低类之间的耦合。由于每个类尽量减少对其它类的依赖,因此。很容易使得系统的功能模块独立,相互之间不存在(或很少有)依赖关系。

Boss想从TeamLeader那里知道现有课程的总数。它们之间的调用关系应该为Boss—>TeamLeader—>Course。Boss与Course并无直接联系,所以在Boss类的方法中不应该出现Course类。

public class Boss {public void commandCheckNumber(TeamLeader teamLeader){teamLeader.checkNumberOfCourses();}
}public class TeamLeader {public void checkNumberOfCourses(){List<Course> courseList = new ArrayList<Course>();for(int i = 0 ;i < 20;i++){courseList.add(new Course());}System.out.println("在线课程的数量是:"+courseList.size());}
}

要遵守该法则 可以从以下方面入手

优先考虑将一个类设置成不变类

尽量降低一个类的访问权限

尽量降低成员的访问权限

红橙Darren视频笔记 网络请求优化方案 OOP面向对象 6大基本原则相关推荐

  1. 红橙Darren视频笔记 数据库操作优化 数据查询(数据库操作)中

    上一节仅仅是做到了有这个功能,这次我们对上一次的代码进行优化 主要有两个方面可以优化 1.利用数据库事务进行优化 我们作如下修改: IDaoSupport增加批量插入接口 // 批量插入数据publi ...

  2. 红橙Darren视频笔记 ViewGroup事件分发分析 基于API27

    本节目标,通过案例,先看程序运行结果,然后跟踪源码,理解为什么会有这样的输出,继而理解view group的分发机制,感觉和证明题很像呢. 考虑以下程序的运行结果: case1: public cla ...

  3. 红橙Darren视频笔记 类加载机制(API28) 自己写个热修复 查看源码网站

    第一部分 类加载机制 一个Activity是如何被Android虚拟机找到的? 在之前的文章 红橙Darren视频笔记 自定义View总集篇(https://blog.csdn.net/u011109 ...

  4. 红橙Darren视频笔记 利用阿里巴巴AndFix进行热修复

    注意 由于AndFix在2017年左右就停止更新了,在最新版本的apk上遇到很多问题,我最终也没有成功进行热修复.本节主要是学习热修复的原理 在上一篇 红橙Darren视频笔记 自己捕获异常并保存到本 ...

  5. 红橙Darren视频笔记 UML图简介

    整体架构复制自红橙原视频的课堂笔记 因为他这一课没有博客,所以没有转载链接,CSDN没有转载地址是无法作为转载类型的文章发表的,暂时标记为原创 参考链接 https://blog.csdn.net/r ...

  6. 红橙Darren视频笔记 代理模式 动态代理和静态代理

    红橙Darren视频笔记 代理模式 动态代理和静态代理(Android API 25) 关于代理模式我之前有过相关的介绍: https://blog.csdn.net/u011109881/artic ...

  7. 红橙Darren视频笔记 Behavior的工作原理源码分析

    主要coordinatorlayout的代码来自coordinatorlayout-1.0.0-sources.jar 本文从源码介绍 CoordinatorLayout 的 behavior 怎么工 ...

  8. 红橙Darren视频笔记 仿QQ侧滑效果

    这一篇没有什么新的内容 就是改写 红橙Darren视频笔记 仿酷狗侧滑效果 的侧滑的效果 1.去掉淡入淡出效果 2.加上黑色模板效果 效果: 去掉淡入淡出效果很简单 就是注释掉onScrollChan ...

  9. 红橙Darren视频笔记 view的绘制流程(上) onMeasure测量代码分析 基于API27

    一.准备工作Activity的onCreate和onResume调用过程 从ActivityThread的handleLaunchActivity开始进行代码跟踪 private void handl ...

最新文章

  1. 《OpenCV3编程入门》学习笔记9 直方图与匹配(五)模板匹配
  2. HTML的标签描述17
  3. 过程中存根的作用有_模温机的作用 模压过程中模温机的作用有哪些?
  4. VIT pytorch源码
  5. 网站的容错性设计原则
  6. 整理出一个比较实用的SqlHelper类 满足大多数情况的使用
  7. 安装 oracle-xe,CentOS上安装Oracle XE指南
  8. stm32 无符号整形_十进制字符串转化为无符号整数
  9. leetcode 227. Basic Calculator II | 227. 基本计算器 II(中缀表达式求值)
  10. 仿ISQL功能的实现,可以实现批处理功能
  11. DataSet and DataStream
  12. 可微偏导数一定存在_【数学】多元函数可微如何判断?
  13. python接口自动化登录_python 接口自动化--登录
  14. linux中用shell脚本对tomcat和nginx做日志切割
  15. Linux安装webmin
  16. ActiveMQ Windows部署
  17. SpringCloud-Zuul(二):自定义Filter及内部路由源码解析
  18. FFMpeg编译支持NVidia CODEC(成功)
  19. 在reader中勾选pdf复选框_Excel中设计具有可任意勾选复选框的操作方法
  20. 系统集成项目管理工程师(项目经理)笔记

热门文章

  1. php如何处理查询请求,PHP如何处理Web请求流程
  2. tinymce引入后未显示_讷河华宁电子QJZ1-1600/3300主控制及显示总成101系统用
  3. macos 编译php,TODO:macOS编译PHP7.1
  4. java 编译器重排序_Java编译器重新排序
  5. more指令和less指令使用的区别
  6. 【剑指offer】旋转数组的最小数字
  7. bzoj2819: Nim(博弈+树剖)
  8. 移除input框type=number在部分浏览器的默认上下按钮
  9. 常用linux terminal 命令
  10. java日志文件log4j.properties配置详解