Android网络篇(四)—— 自己动手封装一个属于自己的网络请
网络请求框架算是android体系当中一个比较重要的部分,在android历史中关于网络的演进也经历了几个阶段,到目前为止,比较通用的网络请求框架就是OkHttp + Retrofit +RxJava+Gson,当然,关于这个组合使用网上也有很多,但是,那个毕竟是别人的东西,大部分时候只有适合自己的才是最好的,所以,自己封装一个网络请求框架就显得比较重要了,废话不多说,直接动手开干。
网络请求框架封装大概流程如下:
(1)封装OkHttp实例
(2)封装OkHttp的拦截请求
(3)封装Retrofit实例
(4)封装Retrofit的注解通用类
(5)增加请求失败重试功能
(6)封装返回结果
(7)对上述封装进行组装形成一个完整的框架
第一步:封装OkHttp实例
public class OkHttpClientUtil {
// 是否开启拦截,默认情况下开启
private boolean mIsIntercept = true;
// 设置数据读取超时时间
private long mReadTimeOut = 20000;
// 设置网络连接超时时间
private long mConnectTimeOut = 20000;
// 设置写入服务器的超时时间
private long mWriteTimeOut = 20000;
private static volatile OkHttpClientUtil okHttpUtil;
static OkHttpClientUtil getOkHttpUtil() {
if (okHttpUtil == null) {
synchronized (OkHttpClientUtil.class) {
if (okHttpUtil == null) {
okHttpUtil = new OkHttpClientUtil();
}
}
}
return okHttpUtil;
}
/**
* 私有构造函数,保证全局唯一
*/
private OkHttpClientUtil(){
}
// 设置数据读取超时时间
OkHttpClientUtil setTimeOutTime(long timeout) {
mReadTimeOut = timeout;
return this;
}
// 设置网络连接超时时间
OkHttpClientUtil setConnectTime(long timeout) {
mConnectTimeOut = timeout;
return this;
}
// 设置写入服务器的超时时间
OkHttpClientUtil setWriteTime(long timeout) {
mWriteTimeOut = timeout;
return this;
}
// 设置拦截器
OkHttpClientUtil setIntercept(boolean isIntercept) {
this.mIsIntercept = isIntercept;
return this;
}
// 设置Build方法
public OkHttpClient build() {
OkHttpClient.Builder okHttpClient = new OkHttpClient.Builder();
okHttpClient.readTimeout(mReadTimeOut, TimeUnit.MILLISECONDS);
okHttpClient.connectTimeout(mConnectTimeOut, TimeUnit.MILLISECONDS);
okHttpClient.writeTimeout(mWriteTimeOut, TimeUnit.MILLISECONDS);
// 默认开启请求的打印信息数据,在每次发布版本的时候可以手动关闭
if (mIsIntercept) {
okHttpClient.addInterceptor(new HttpRequestInterceptor());
}
return okHttpClient.build();
}
}
第二步:封装OkHttp的拦截请求
/**
* author: zhoufan
* data: 2021/7/27 14:39
* content: 拦截每次发出的请求并打印出来
*/
public class HttpRequestInterceptor implements Interceptor {
@NotNull
@Override
public Response intercept(@NotNull Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.currentTimeMillis();
okhttp3.Response response = chain.proceed(chain.request());
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
okhttp3.MediaType mediaType = response.body().contentType();
String content = response.body().string();
Log.d("request", "请求地址:| " + request.toString());
if (request.body() != null) {
printParams(request.body());
}
Log.d("request", "请求体返回:| Response:" + content);
Log.d("request", "----------请求耗时:" + duration + "毫秒----------");
return response.newBuilder().body(okhttp3.ResponseBody.create(mediaType, content)).build();
}
private void printParams(RequestBody body) {
Buffer buffer = new Buffer();
try {
body.writeTo(buffer);
Charset charset = Charset.forName("UTF-8");
MediaType contentType = body.contentType();
if (contentType != null) {
charset = contentType.charset(UTF_8);
}
String params = buffer.readString(charset);
Log.d("request", "请求参数: | " + params);
} catch (IOException e) {
e.printStackTrace();
}
}
}
第三步:封装Retrofit实例
/**
* author: zhoufan
* data: 2021/7/27 14:45
* content: 对Retrofit进行封装
*/
public class RetrofitClientUtil {
// 网络请求的baseUrl
private String mBaseUrl;
// 设置数据解析器
private Converter.Factory mBaseFactory;
// 新增数据解析器
private Converter.Factory mAddFactory;
// 设置网络请求适配器
private CallAdapter.Factory mCallFactory;
// 设置OkHttpClient
private OkHttpClient mOkHttpClient;
private static volatile RetrofitClientUtil retrofitUtil;
public static RetrofitClientUtil getRetrofitUtil() {
if (retrofitUtil == null) {
synchronized (RetrofitClientUtil.class) {
if (retrofitUtil == null) {
retrofitUtil = new RetrofitClientUtil();
}
}
}
return retrofitUtil;
}
private RetrofitClientUtil() {
mBaseUrl = HttpRequestConstants.BASE_URL;
// 默认基础数据类型的解析
mBaseFactory = ScalarsConverterFactory.create();
// RxJava来处理Call返回值
mCallFactory = RxJava3CallAdapterFactory.create();
}
// 设置BaseUrl
public RetrofitClientUtil setBaseUrl(String baseUrl) {
this.mBaseUrl = baseUrl;
return this;
}
// 设置数据解析
public RetrofitClientUtil addConverterFactory(Converter.Factory factory) {
this.mAddFactory = factory;
return this;
}
// 设置网络请求适配器
public RetrofitClientUtil addCallAdapterFactory(CallAdapter.Factory factory) {
this.mCallFactory = factory;
return this;
}
// 设置写入服务器的超时时间
public RetrofitClientUtil setOkHttpClient(OkHttpClient okHttpClient) {
this.mOkHttpClient = okHttpClient;
return this;
}
// 设置Build方法
public Retrofit build() {
Retrofit.Builder builder = new Retrofit.Builder();
builder.baseUrl(mBaseUrl);
builder.addConverterFactory(mBaseFactory);
if (mAddFactory!=null){
builder.addConverterFactory(mAddFactory);
}
builder.addCallAdapterFactory(mCallFactory);
builder.client(mOkHttpClient);
return builder.build();
}
}
第四步:封装Retrofit的注解通用类
public interface ApiService {
// baseUrl = http://www.5mins-sun.com:8081/
// 例如:http://www.baidu.com不使用baseUrl
@GET
Observable<String> getPath(@Url String url);
// GET请求(无参) api = news
// http://www.5mins-sun.com:8081/news
@GET("{api}")
Observable<String> getData(@Path("api") String api);
// GET请求(带参) api = news
// http://www.5mins-sun.com:8081/news?name=admin&pwd=123456
@GET("{api}")
Observable<String> getData(@Path("api") String api, @QueryMap TreeMap<String, Object> map);
// POST请求(无参)
@POST("{api}")
Observable<String> postData(@Path(value = "api", encoded = true) String api);
// POST请求,以RequestBody方式提交 api = news
// http://www.5mins-sun.com:8081/news
// RequestBody:
// {
// "albumID": 2,
// "sectionID": 16
// }
@POST("{api}")
Observable<String> postData(@Path(value = "api", encoded = true) String api, @Body RequestBody requestBody);
// Post请求,以表单方式提交
// http://www.5mins-sun.com:8081/news
// user=admin
// pwd=123456
@FormUrlEncoded
@POST("{api}")
Observable<ResponseBody> postData(@Path("api") String api, @FieldMap Map<String, String> maps);
// Post请求(带数组)
@FormUrlEncoded
@POST("{api}")
Observable<ResponseBody> postData(@Path("api") String api, @FieldMap Map<String, String> maps, @Query("meta[]") String... linked);
// 上传单个文件
@Multipart
@POST("{api}/")
Observable<String> upload(@Path("api") String api, @PartMap Map<String, RequestBody> maps, @Part MultipartBody.Part file);
// 上传多个文件
@Multipart
@POST("{api}/")
Observable<String> uploadMultipart(@Path("api") String api, @PartMap Map<String, RequestBody> maps, @Part List<MultipartBody.Part> params);
}
第五步:增加请求失败重试功能
/**
* author: zhoufan
* data: 2021/7/29 11:39
* content: 设置重新连接
*/
public class RetryFunction implements Function<Observable<Throwable>, ObservableSource<?>> {
// 可重试次数
private int maxConnectCount = 3;
// 当前已重试次数
private int currentRetryCount = 0;
// 重试等待时间
private int waitRetryTime = 0;
@Override
public ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Throwable {
// 参数Observable<Throwable>中的泛型 = 上游操作符抛出的异常,可通过该条件来判断异常的类型
return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {
@Override
public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
// 输出异常信息
LogUtil.d(HttpRequestConstants.TAG, "发生异常 = " + throwable.toString());
/**
* 需求1:根据异常类型选择是否重试
* 即,当发生的异常 = 网络异常 = IO异常 才选择重试
*/
if (throwable instanceof IOException) {
Log.d(HttpRequestConstants.TAG, "属于IO异常,需重试");
/**
* 需求2:限制重试次数
* 即,当已重试次数 < 设置的重试次数,才选择重试
*/
if (currentRetryCount < maxConnectCount) {
// 记录重试次数
currentRetryCount++;
Log.d(HttpRequestConstants.TAG, "重试次数 = " + currentRetryCount);
/**
* 需求2:实现重试
* 通过返回的Observable发送的事件 = Next事件,从而使得retryWhen()重订阅,最终实现重试功能
*
* 需求3:延迟1段时间再重试
* 采用delay操作符 = 延迟一段时间发送,以实现重试间隔设置
*
* 需求4:遇到的异常越多,时间越长
* 在delay操作符的等待时间内设置 = 每重试1次,增多延迟重试时间1s
*/
// 设置等待时间
waitRetryTime = 1000 + currentRetryCount * 1000;
Log.d(HttpRequestConstants.TAG, "等待时间 =" + waitRetryTime);
return Observable.just(1).delay(waitRetryTime, TimeUnit.MILLISECONDS);
} else {
// 若重试次数已 > 设置重试次数,则不重试
// 通过发送error来停止重试(可在观察者的onError()中获取信息)
return Observable.error(new Throwable("重试次数已超过设置次数 = " + currentRetryCount + ",即 不再重试"));
}
}
// 若发生的异常不属于I/O异常,则不重试
// 通过返回的Observable发送的事件 = Error事件 实现(可在观察者的onError()中获取信息)
else {
return Observable.error(new Throwable("发生了非网络异常(非I/O异常)"));
}
}
});
}
}
第六步:封装返回结果
/**
* author: zhoufan
* data: 2021/7/27 16:59
* content: 处理接口返回的请求结果
*/
public class RxJavaObserver implements Observer<String> {
private HttpRequestCallback mCallBack;
private int mType;
private Context mContext;
RxJavaObserver(Context context, int type, HttpRequestCallback callBack) {
this.mCallBack = callBack;
this.mContext = context;
this.mType = type;
}
@Override
public void onSubscribe(@NonNull Disposable d) {
}
/**
* 接口请求成功
*/
@Override
public void onNext(@NonNull String s) {
try {
if (HttpTool.isJsonObject(s)) {
JSONObject jsonObject = new JSONObject(s);
if (jsonObject.has("resultStatus")) {
String response = jsonObject.getString("resultStatus");
if (response.toUpperCase().equals("SUCCESS")) {
if (jsonObject.has("returnData")) {
mCallBack.onRequestSuccess(jsonObject.getString("returnData"), mType);
} else {
if (jsonObject.has("errCode") && jsonObject.has("errMsg")) {
String code = jsonObject.getString("errCode");
String errMsg = jsonObject.getString("errMsg");
mCallBack.onRequestSuccess(errMsg, mType);
}
}
} else {
String errMsg = jsonObject.getString("errMsg");
String code = jsonObject.getString("errCode");
mCallBack.onRequestFail(errMsg, code, mType);
}
}
} else {
mCallBack.onRequestSuccess(s, mType);
}
} catch (JSONException e) {
e.printStackTrace();
}
}
/**
* 接口请求失败
*/
@Override
public void onError(@NonNull Throwable e) {
// 1.检查网络设置
if (!HttpTool.hasNetwork(mContext)) {
MyToast.showCenterSortToast(mContext, mContext.getResources().getString(R.string.connect_error));
onComplete();
mCallBack.onRequestNetFail(mType);
return;
}
// 2.非网络错误,接口请求错误
mCallBack.onRequestFail(e.getMessage(), "0000", mType);
}
/**
* 接口请求完成
*/
@Override
public void onComplete() {
}
}
第七步:对上述封装进行组装形成一个完整的框架
/**
* author: zhoufan
* data: 2021/7/27 16:41
* content: 对应用层暴露的调用类
*/
public class HttpRetrofitRequest extends HttpRetrofit implements IHttpRequest {
private ApiService apiService;
// 是否添加通用参数
private boolean mIsAddCommonParams;
private volatile static HttpRetrofitRequest INSTANCES;
public static HttpRetrofitRequest getInstances() {
if (INSTANCES == null) {
synchronized (HttpRetrofitRequest.class) {
if (INSTANCES == null) {
INSTANCES = new HttpRetrofitRequest();
}
}
}
return INSTANCES;
}
private HttpRetrofitRequest() {
apiService = getApiManager();
}
/**
* 设置通用参数
*/
public void isAddCommonParams(boolean isAddCommonParams) {
this.mIsAddCommonParams = isAddCommonParams;
}
// Get请求(使用Path形式)
@Override
public void mHttpGetPath(Context context, String url, int type, HttpRequestCallback callBack) {
Observable<String> observable = apiService.getPath(url);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
// GET请求(无参)
@Override
public void mHttpGet(Context context, String api, int type, HttpRequestCallback callBack) {
Observable<String> observable = apiService.getData(api);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
// Get请求(带参)
@Override
public void mHttpGet(Context context, String api, TreeMap map, int type, HttpRequestCallback callBack) {
if (mIsAddCommonParams) {
HttpTool.getTreeCrc(map);
}
Observable<String> observable = apiService.getData(api, map);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
// Post请求(无参)
@Override
public void mHttpPost(Context context, String api, int type, HttpRequestCallback callBack) {
Observable<String> observable = apiService.postData(api);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
// Post请求(带参)
// 以RequestBody方式提交
@Override
public void mHttpPost(Context context, String api, TreeMap map, int type, HttpRequestCallback callBack) {
if (mIsAddCommonParams) {
HttpTool.getTreeCrc(map);
}
Gson gson = new Gson();
String param = gson.toJson(map);
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), param);
Observable<String> observable = apiService.postData(api, requestBody);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
// Post请求(包含数组)
@Override
public void mHttpPost(Context context, String api, TreeMap map, String[] data, int type, HttpRequestCallback callBack) {
if (mIsAddCommonParams) {
HttpTool.getTreeCrc(map);
}
Observable<String> observable = apiService.postData(api, map, data);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
// 单文件上传
@Override
public void mHttpFile(Context context, String api, File file, String fileKey, TreeMap map, int type, HttpRequestCallback callBack) {
// 生成单个文件
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part body = MultipartBody.Part.createFormData(fileKey, file.getName(), requestFile);
if (mIsAddCommonParams) {
HttpTool.getTreeCrc(map);
}
Map<String, RequestBody> mapValue = new HashMap<>();
for (Object key : map.keySet()) {
mapValue.put(key.toString(), HttpTool.convertToRequestBody(map.get(key).toString()));
}
Observable<String> observable = apiService.upload(api, mapValue, body);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
// 多文件上传
@Override
public void mHttpMultiFile(Context context, String api, List<File> list, List<String> listFileName, TreeMap map, int type, HttpRequestCallback callBack) {
//生成多个文件并添加到集合中
List<MultipartBody.Part> params = new ArrayList<>();
for (int i = 0; i < list.size(); i++) {
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), list.get(i));
MultipartBody.Part body = MultipartBody.Part.createFormData(listFileName.get(i), list.get(i).getName(), requestFile);
params.add(body);
}
if (mIsAddCommonParams) {
HttpTool.getTreeCrc(map);
}
Map<String, RequestBody> mapValue = new HashMap<>();
for (Object key : map.keySet()) {
mapValue.put(key.toString(), HttpTool.convertToRequestBody(Objects.requireNonNull(map.get(key)).toString()));
}
// 发送异步请求
Observable<String> observable = apiService.uploadMultipart(api, mapValue, params);
observable.retryWhen(new RetryFunction()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new RxJavaObserver(context, type, callBack));
}
}
当然,在基本流程之外还有一些辅助类:
请求接口回调,用于应用层对返回结果做出对应的展示
/**
* author: zhoufan
* data: 2021/7/27 16:30
* content: 请求接口回调
*/
public interface HttpRequestCallback {
void onRequestNetFail(int type);
void onRequestSuccess(String result, int type);
void onRequestFail(String value, String failCode, int type);
}
网络框架封装的常量类,比如baseUrl等等的位置
/**
* author: zhoufan
* data: 2021/7/27 14:56
* content: 网络请求的常量类
*/
public class HttpRequestConstants {
public static final String BASE_URL = "http://www.5mins-sun.com:8081/";
public static final String SECRET = "5af012069a2fc4.49876009";
public static final String APP_KEY = "5af012069a2fa";
public static final String SIGN_METHOD_MD5 = "MD5";
public static final String SIGN_METHOD_HMAC = "HMAC";
public static final String CHARSET_UTF8 = "UTF-8";
public static final String TAG = "retrofit";
}
使用Retrofit作为我们的底层网络请求
/**
* author: zhoufan
* data: 2021/7/27 15:09
* content: 对外暴露方法供外面调用
*/
public class HttpRetrofit {
// 设置数据读取超时时间
public HttpRetrofit setTimeOutTime(long timeout) {
OkHttpClientUtil.getOkHttpUtil().setTimeOutTime(timeout);
return this;
}
// 设置网络连接超时时间
public HttpRetrofit setConnectTime(long timeout) {
OkHttpClientUtil.getOkHttpUtil().setConnectTime(timeout);
return this;
}
// 设置写入服务器的超时时间
public HttpRetrofit setWriteTime(long timeout) {
OkHttpClientUtil.getOkHttpUtil().setWriteTime(timeout);
return this;
}
// 设置拦截器
public HttpRetrofit setIntercept(boolean isIntercept) {
OkHttpClientUtil.getOkHttpUtil().setIntercept(isIntercept);
return this;
}
// 设置BaseUrl
public HttpRetrofit setBaseUrl(String baseUrl) {
RetrofitClientUtil.getRetrofitUtil().setBaseUrl(baseUrl);
return this;
}
// 设置数据解析
public HttpRetrofit addConverterFactory(Converter.Factory factory) {
RetrofitClientUtil.getRetrofitUtil().addConverterFactory(factory);
return this;
}
/**
* 生成请求接口的实例
* @return ApiService
*/
public ApiService getApiManager(){
return RetrofitClientUtil.getRetrofitUtil().setOkHttpClient(OkHttpClientUtil.getOkHttpUtil().build()).build().create(ApiService.class);
}
}
然后在我们的Application里面进行初始化设置
public class IApplication extends Application {
private static Context mContext;
public static Context getContext() {
return mContext;
}
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
HttpUtils.getInstance().httpRequest(HttpRetrofitRequest.getInstances());
}
}
网络请求框架的工具类
/**
* Created by zhoufan on 2018/1/3.
* 工具类 (添加通用参数、加密等等)
*/
public class HttpTool {
public static RequestBody convertToRequestBody(String param) {
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain"), param);
return requestBody;
}
public static String signTopRequest(Map<String, Object> params, String secret, String signMethod) throws IOException {
// 第一步:检查参数是否已经排序
String[] keys = params.keySet().toArray(new String[0]);
Arrays.sort(keys);
// 第二步:把所有参数名和参数值串在一起
StringBuilder query = new StringBuilder();
for (String key : keys) {
Object value = params.get(key);
if (value instanceof String) {
if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty((String) value)) {
query.append(key).append(value);
}
} else if (value instanceof File) {
String strVal = getFileContent((File) value);
query.append(key).append(strVal);
params.remove(key);
}
}
// 第三步:使用MD5/HMAC加密
byte[] bytes;
if (HttpRequestConstants.SIGN_METHOD_HMAC.equals(signMethod)) {
bytes = encryptHMAC(query.toString(), secret);
} else {
bytes = encryptMD5(query.toString() + secret);
}
// 第四步:把二进制转化为大写的十六进制
return byte2hex(bytes);
}
public static byte[] encryptHMAC(String data, String secret) throws IOException {
byte[] bytes = null;
try {
SecretKey secretKey = new SecretKeySpec(secret.getBytes(HttpRequestConstants.CHARSET_UTF8), "HmacMD5");
Mac mac = Mac.getInstance(secretKey.getAlgorithm());
mac.init(secretKey);
bytes = mac.doFinal(data.getBytes(HttpRequestConstants.CHARSET_UTF8));
} catch (GeneralSecurityException gse) {
throw new IOException(gse.toString());
}
return bytes;
}
public static byte[] encryptMD5(String data) throws IOException {
byte[] md5Byte = null;
try {
MessageDigest md = MessageDigest.getInstance("MD5"); // 创建一个md5算法对象
byte[] messageByte = data.getBytes("UTF-8");
md5Byte = md.digest(messageByte); // 获得MD5字节数组,16*8=128位
} catch (Exception e) {
e.printStackTrace();
}
return md5Byte;
}
public static String byte2hex(byte[] bytes) {
StringBuilder sign = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1) {
sign.append("0");
}
sign.append(hex.toLowerCase());
}
return sign.toString();
}
public static TreeMap getTreeCrc(TreeMap maps) {
try {
maps.put("app_key", HttpRequestConstants.APP_KEY);
maps.put("sign_method", "md5");
maps.put("format", "json");
maps.put("timestamp", timeStamp2Date());
maps.put("sign", signTopRequest(maps, HttpRequestConstants.SECRET, HttpRequestConstants.SIGN_METHOD_MD5));
} catch (Exception e) {
e.printStackTrace();
}
return maps;
}
// 时间戳转换
private static String timeStamp2Date() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));//设置TimeZone为上海时间
Date now = new Date();//获取本地时间
try {
now = sdf.parse(sdf.format(now));//将本地时间转换为转换时间为东八区
} catch (ParseException e) {
e.printStackTrace();
}
return sdf.format(now);
}
// 检测是否有网络
@SuppressLint("MissingPermission")
public static boolean hasNetwork(Context mContext) {
// 得到连接管理器对象
ConnectivityManager connectivityManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetworkInfo = connectivityManager.getActiveNetworkInfo();
if (activeNetworkInfo != null && activeNetworkInfo.isConnected()) {
if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_WIFI)) {
return true;
} else if (activeNetworkInfo.getType() == (ConnectivityManager.TYPE_MOBILE)) {
return true;
}
} else {
return false;
}
return false;
}
// 将文件进行SHA1加密
public static String getFileContent(File file) {
try {
StringBuffer sb = new StringBuffer();
MessageDigest digest = MessageDigest.getInstance("SHA-1");
FileInputStream fin = new FileInputStream(file);
int len = -1;
byte[] buffer = new byte[1024];//设置输入流的缓存大小 字节
//将整个文件全部读入到加密器中
while ((len = fin.read(buffer)) != -1) {
digest.update(buffer, 0, len);
}
//对读入的数据进行加密
byte[] bytes = digest.digest();
for (byte b : bytes) {
// 数byte 类型转换为无符号的整数
int n = b & 0XFF;
// 将整数转换为16进制
String s = Integer.toHexString(n);
// 如果16进制字符串是一位,那么前面补0
if (s.length() == 1) {
sb.append("0" + s);
} else {
sb.append(s);
}
}
return sb.toString();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 2 * 判断字符串是否可以转化为json对象
* 3 * @param content
* 4 * @return
* 5
*/
public static boolean isJsonObject(String content) {
if (content == null || TextUtils.isEmpty(content))
return false;
try {
JSONObject jsonObject = new JSONObject(content);
return true;
} catch (Exception e) {
return false;
}
}
}
最终对外暴露的类
public class HttpUtils {
private static IHttpRequest mHttpRequest;
/**
* 生成唯一实例
*/
private static volatile HttpUtils mInstance;
public static HttpUtils getInstance(){
if (mInstance==null){
synchronized (HttpUtils.class){
if (mInstance==null){
mInstance = new HttpUtils();
}
}
}
return mInstance;
}
/**
* 初始化决定使用哪一套网络请求框架
* @param httpRequest 默认为OkHttp + retrofit + rxJava
*/
public static void setInitHttpRequest(IHttpRequest httpRequest){
mHttpRequest = httpRequest;
}
/**
* 更换使用的框架
* @param httpRequest 重新设置的网络框架
* @return
*/
public HttpUtils httpRequest(IHttpRequest httpRequest){
mHttpRequest = httpRequest;
return this;
}
/**
* Get请求(使用Path形式)
* @param context 上下文
* @param url 请求的url地址,不使用baseUrl
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executePathGet(Context context, String url, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpGetPath(context,url,type,callBack);
}
/**
* GET请求(无参)
* @param context 上下文
* @param api 请求的api
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executeGet(Context context, String api, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpGet(context,api,type,callBack);
}
/**
* Get请求(带参)
* @param context 上下文
* @param api 请求的api
* @param map 请求的参数
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executeGet(Context context, String api, TreeMap map, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpGet(context,api,map,type,callBack);
}
/**
* Post请求(无参)
* @param context 上下文
* @param api 请求的api
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executePost(Context context, String api, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpPost(context,api,type,callBack);
}
/**
* Post请求(带参)
* 以RequestBody方式提交
* @param context 上下文
* @param api 请求的api
* @param map 请求的参数
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executePost(Context context, String api, TreeMap map, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpPost(context,api,map,type,callBack);
}
/**
* Post请求(包含数组)
* @param context 上下文
* @param api 请求的api
* @param treeMap 请求的参数
* @param data 请求的数组
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executePost(Context context, String api, TreeMap treeMap, String[] data, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpPost(context,api,treeMap,data,type,callBack);
}
/**
* 单文件上传
* @param context 上下文
* @param api 请求的api
* @param file 上传的文件
* @param fileKey 上传的文件对应的key
* @param map 请求的参数
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executeFile(Context context, String api, File file, String fileKey, TreeMap map, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpFile(context, api, file, fileKey, map, type, callBack);
}
/**
* 多文件上传
* @param context 上下文
* @param api 请求的api
* @param list 上传的文件集合
* @param fileList 上传的文件集合对应的key
* @param map 请求的参数
* @param type 请求的标识
* @param callBack 结果返回接口
*/
public void executeMultiFile(Context context, String api, List<File> list, List<String> fileList, TreeMap map, int type, HttpRequestCallback callBack){
mHttpRequest.mHttpMultiFile(context, api, list, fileList, map, type, callBack);
}
}
请求方式的通用接口
/**
* author: zhoufan
* data: 2021/7/27 16:29
* content: 请求接口的方式
*/
public interface IHttpRequest {
// Get请求(使用Path形式)
void mHttpGetPath(Context context, String url, int type, HttpRequestCallback mCallBack);
// GET请求(无参)
void mHttpGet(Context context, String api, int type, HttpRequestCallback mCallBack);
// Get请求(带参)
void mHttpGet(Context context, String api, TreeMap map, int type, HttpRequestCallback mCallBack);
// Post请求(无参)
void mHttpPost(Context context, String api, int type, HttpRequestCallback mCallBack);
// Post请求(带参)
// 以RequestBody方式提交
void mHttpPost(Context context, String api, TreeMap map, int type, HttpRequestCallback mCallBack);
// Post请求(包含数组)
void mHttpPost(Context context, String api, TreeMap treeMap, String[] data, int type, HttpRequestCallback mCallBack);
// 单文件上传
void mHttpFile(Context context, String api, File file, String fileKey, TreeMap map, int type, HttpRequestCallback mCallBack);
// 多文件上传
void mHttpMultiFile(Context context, String api, List<File> list, List<String> fileList, TreeMap map, int type, HttpRequestCallback mCallBack);
}
完整的目录结构为:
-- ApiService
-- HttpRequestCallback
-- HttpRequestConstants
-- HttpRequestInterceptor
-- HttpRetrofit
-- HttpRetrofitRequest
-- HttpTool
-- HttpUtils
-- IHttpRequest
-- OkHttpClientUtil
-- RetrofitClientUtil
-- RetryFunction
-- RxJavaObserver
当然,别忘记在清单文件里面设置网络权限哦
<uses-permission android:name="android.permission.INTERNET" />
最后需要注意的是,千万别导入错误的包,我引入的依赖为
// okHttp网络请求框架
// define a BOM and its version
api(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))
// define any required OkHttp artifacts without version
api("com.squareup.okhttp3:okhttp")
api("com.squareup.okhttp3:logging-interceptor")
//retrofit网络请求框架
api 'com.squareup.retrofit2:retrofit:2.9.0'
//retrofit添加Json解析返回数据
api 'com.squareup.retrofit2:converter-scalars:2.9.0'
api 'com.squareup.retrofit2:converter-gson:2.9.0'
//retrofit添加RxJava支持
api 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
api "io.reactivex.rxjava3:rxjava:3.0.13"
api 'io.reactivex.rxjava3:rxandroid:3.0.0'
最后看下效果
/**
* 网络测试类
*/
class NetWorkActivity : AppCompatActivity(), HttpRequestCallback {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_net_work)
}
// Get请求(使用Path形式)
fun requestOne(view: View) {
HttpUtils.getInstance().executePathGet(this, "http://www.baidu.com/", 0, this)
}
// Post请求(带参以RequestBody方式提交)
fun requestFive(view: View) {
val treeMap = TreeMap<String,Any>()
HttpUtils.getInstance().executePost(this, "/app/get_version_info", treeMap,0, this)
}
// 网络连接失败
override fun onRequestNetFail(type: Int) {
}
// 接口请求成功
override fun onRequestSuccess(result: String?, type: Int) {
textView.text = result
}
// 接口请求失败
override fun onRequestFail(value: String?, failCode: String?, type: Int) {
}
}
网络请求.gif
在这里我只测试了Get请求(使用Path形式)和Post请求(带参以RequestBody方式提交),其他的大家可以根据自己的需求自行测试。