参考链接
https://www.jianshu.com/p/e1d9ddc86c7d
先看几个demo demo的目的很简单 就是发送一个Http请求
先给出几个工具类的代码 各个demo都依赖工具类或者公共类的代码
public class BaseApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 测试不同的demo时记得切换导包
PreferencesUtil.getInstance().init(this);
// 初始化xUtils
x.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";
@Override
protected 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() {
@Override
public void onFailure(Call call, final IOException e) {
// 失败
Log.e(TAG, "fail " + e.getMessage());
}
@Override
public 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的处理都集中放在工具类中
*/
@Override
protected 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() {
@Override
public void onFailure(Call call, final IOException e) {
// 失败
Log.e(TAG, "fail " + e.getMessage());
}
@Override
public 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() {
@Override
public void onFailure(Call call, final IOException e) {
// 失败
callback.onFailure(call, e);
}
@Override
public 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
*/
@Override
protected 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>() {
@Override
public 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);
}
}
@Override
public 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() {
@Override
public void onFailure(Call call, final IOException e) {
// 失败
callback.onFailure(e);
}
@Override
public 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分离
*/
@Override
protected 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>() {
@Override
public void onSuccess(DiscoverListResult result) {
if (result.isOK()) {
// 没有列表数据的情况, 打印 Toast 或者做一些其他处理
} else {
// 有数据列表的情况,显示列表
}
}
@Override
public 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();
}
// 参数还是很多
@Override
public <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() {
@Override
public void onFailure(Call call, final IOException e) {
// 失败
callback.onFailure(e);
}
@Override
public 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);
}
}
});
}
@Override
public <T> void post(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback, boolean cache) {
}
@Override
public <T> void download(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {
}
@Override
public <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>() {
@Override
public void onSuccess(String result) {
Log.e(TAG, "onSuccess: " + result);
}
@Override
public void onError(Throwable ex, boolean isOnCallback) {
Log.e(TAG, "onSuccess: " + ex.getMessage());
ex.printStackTrace();
}
@Override
public void onCancelled(CancelledException cex) {
Log.e(TAG, "onCancelled: " + cex.getMessage());
cex.printStackTrace();
}
@Override
public void onFinished() {
Log.e(TAG, "onFinished: ");
}
});
}
@Override
public <T> void post(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback, boolean cache) {
}
@Override
public <T> void download(Context context, String url, Map<String, Object> params, HttpCallBack<T> callback) {
}
@Override
public <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引擎 比如换成Xutils
private 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访问网络 可以灵活切换使用的网络引擎
*/
@Override
protected 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>() {
@Override
public void onSuccess(DiscoverListResult result) {
if (result.isOK()) {
// 没有列表数据的情况, 打印 Toast 或者做一些其他处理
} else {
// 有数据列表的情况,显示列表
}
}
@Override
public 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());
}
}
要遵守该法则 可以从以下方面入手
优先考虑将一个类设置成不变类
尽量降低一个类的访问权限
尽量降低成员的访问权限