网上有很多类似的封装实例,比这个好的也有很多,为什么还要在写一下呢?
主要是因为在经历了几个别人的项目架构后发现,很严重的业务耦合全部都放到网络框架上了,所以觉得 还是有必要记录一下, 大神勿喷!!!
本文主要说的是 post get 请求封装的一些东西 下面直接上代码
需要引入的包
api 'com.squareup.okhttp3:okhttp:3.10.0'
api 'com.squareup.okio:okio:1.11.0'
api 'com.squareup.okhttp3:logging-interceptor:4.0.1'
// retorfit
api 'com.squareup.retrofit2:retrofit:2.2.0'
api 'com.squareup.retrofit2:adapter-rxjava2:2.6.0'
api 'com.squareup.retrofit2:converter-gson:2.6.0'
api 'com.google.code.gson:gson:2.8.2'
/*rx-android-java*/
api 'io.reactivex.rxjava2:rxjava:2.2.10'
api 'io.reactivex.rxjava2:rxandroid:2.1.1'
api 'com.squareup.retrofit2:converter-scalars:2.6.0'
api 'com.trello.rxlifecycle2:rxlifecycle-components:2.1.0'
首先定义一个RetorfitManager管理类
package com.wmy.lib.mvp.http;
import com.google.gson.Gson;
import com.wmy.lib.mvp.base.BaseApplication;
import com.wmy.lib.mvp.common.Constant;
import com.wmy.lib.mvp.http.listener.DownloadCallBack;
import com.wmy.lib.mvp.http.listener.FileDownLoadObserver;
import com.wmy.lib.mvp.http.listener.ProgressListener;
import com.wmy.lib.mvp.http.listener.ResponseCallBack;
import com.wmy.lib.mvp.http.okhttp.ProgressRequestBody;
import com.wmy.lib.mvp.http.okhttp.ProgressResponseBody;
import com.wmy.lib.mvp.utils.FileUtils;
import com.wmy.lib.mvp.utils.LogUtils;
import com.wmy.lib.mvp.utils.NetworkUtils;
import com.wmy.lib.mvp.utils.SPDownloadUtil;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.ConnectionPool;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.converter.scalars.ScalarsConverterFactory;
/**
* retrofit 管理类
* 使用单利模式 实现( 后期可能会调整 使用构造者 如果有此需求的话)
* 所有请求底层都需要走这个类实现
* 本类主要封装 各种请求方式 get post 等、、 根据业务需求可增加调整
* 封装网络 可 增加请求拦截 和返回拦截处理
* 回调使用rxjava 转换observer 使用gson 通过泛型类型转会 业务层需要的bean
*
* @author:wmyas
* @date:2019/7/24 19:21
*/
public class RetrofitManager<T> {
private static final int CONNEC_TIMEOUT = 5;
private static final int READ_TIMEOUT = 10;
private static ApiService apiServer;
private static RetrofitManager mInstance;
private static OkHttpClient mOkhttp;
private static Retrofit mRetrofit;
private boolean isCach;
//构造retrofit 管理
private RetrofitManager() {
mRetrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl("https://easydoc.xyz/mock/")
.addConverterFactory(GsonConverterFactory.create())
.client(getOkhttpClient())
.build();
apiServer = mRetrofit.create(ApiService.class);
}
//定义okhttp 客户端
private OkHttpClient getOkhttpClient() {
//启用Log日志
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
mOkhttp = new OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.connectTimeout(CONNEC_TIMEOUT, TimeUnit.SECONDS)
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.writeTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
.connectionPool(new ConnectionPool())
.build();
return mOkhttp;
}
/**
* 是否添加缓存
* @param isCach
*/
public void isCach(boolean isCach){
this.isCach=isCach;
}
private OkHttpClient.Builder setCach() {
Interceptor cacheIntercepter = new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
//对request的设置用来指定有网/无网下所走的方式
//对response的设置用来指定有网/无网下的缓存时长
Request request = chain.request();
if (!NetworkUtils.isConnected()) {
//无网络下强制使用缓存,无论缓存是否过期,此时该请求实际上不会被发送出去。
//有网络时则根据缓存时长来决定是否发出请求
request = request.newBuilder()
.cacheControl(CacheControl.FORCE_CACHE).build();
}
okhttp3.Response response = chain.proceed(request);
if (NetworkUtils.isConnected()) {
//有网络情况下,超过1分钟,则重新请求,否则直接使用缓存数据
int maxAge = 60; //缓存一分钟
String cacheControl = "public,max-age=" + maxAge;
//当然如果你想在有网络的情况下都直接走网络,那么只需要
//将其超时时间maxAge设为0即可
return response.newBuilder()
.header("Cache-Control", cacheControl)
.removeHeader("Pragma").build();
} else {
//无网络时直接取缓存数据,该缓存数据保存1周
int maxStale = 60 * 60 * 24 * 7 * 1; //1周
return response.newBuilder()
.header("Cache-Control", "public,only-if-cached,max-stale=" + maxStale)
.removeHeader("Pragma").build();
}
}
};
File cacheFile = new File(BaseApplication.getIns().getExternalCacheDir(), "HttpCache");//缓存地址
Cache cache = new Cache(cacheFile, 1024 * 1024 * 50); //大小50Mb
//设置缓存方式、时长、地址
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
okHttpClientBuilder.addNetworkInterceptor(cacheIntercepter);
okHttpClientBuilder.addInterceptor(cacheIntercepter);
okHttpClientBuilder.cache(cache);
return okHttpClientBuilder;
}
//通过单利模式初始化
public static RetrofitManager getInstance() {
if (mInstance == null) {
synchronized (RetrofitManager.class) {
if (mInstance == null) {
mInstance = new RetrofitManager();
}
}
}
return mInstance;
}
public ApiService getApiServer() {
return apiServer;
}
/**
* 公用的get 请求
*
* @param url 请求地址
* @param maps 请求参数
* @param responseCallBack 回调监听
*/
public void get(String url, Map<String, String> maps, ResponseCallBack responseCallBack) {
Map map = maps;
if (map == null) {
map = new HashMap();
}
getApiServer().get(url, map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserver(responseCallBack));
}
/**
* 公用的post 请求
*
* @param url 请求地址
* @param maps 请求参数
* @param responseCallBack 回调监听
*/
public void post(String url, Map<String, String> maps, ResponseCallBack responseCallBack) {
Map map = maps;
if (map == null) {
map = new HashMap();
}
getApiServer().post(url, map)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserver(responseCallBack));
}
/**
* 单文件上传
*
* @param url 请求地址
* @param filepath 文件地址
* @param responseCallBack 返回回调
*/
public void uploadFile(String url, String filepath, ProgressListener responseCallBack) {
File file = new File(filepath);
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("headimg", file.getName(), new ProgressRequestBody(requestFile, new ProgressRequestBody.Listener() {
@Override
public void onProgress(long bytesWritten, long contentLength) {
responseCallBack.onProgress(bytesWritten,contentLength);
}
}));
getApiServer().uploadFile(url, filePart)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserver(responseCallBack));
}
/**
* 多文件上传
*
* @param url
* @param map
* @param responseCallBack
*/
public void uploadFiles(String url, Map<String, String> map, ProgressListener responseCallBack) {
MultipartBody.Part[] parts = new MultipartBody.Part[map.size()];
int cnt = 0;
for (String key : map.keySet()) {
System.out.println("key= " + key + " and value= " + map.get(key));
//创建文件
File file = new File(map.get(key));
RequestBody requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), file);
MultipartBody.Part filePart = MultipartBody.Part.createFormData("headimg[]", file.getName(), new ProgressRequestBody(requestFile, new ProgressRequestBody.Listener() {
@Override
public void onProgress(long bytesWritten, long contentLength) {
responseCallBack.onProgress(bytesWritten,contentLength);
}
}));
parts[cnt] = filePart;
cnt++;
}
getApiServer().uploadFile(url, parts, map).subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(getObserver(responseCallBack));
}
/**
* 文件下载
* 使用前请确保有文件读取权限
*
* @param url 请求地址
* @param filePath 文件地址
* @param fileName 文件名成
* @param listener 下载监听
*/
public void downloadFile(String url, String filePath, String fileName, FileDownLoadObserver listener) {
if (listener == null) {
}
OkHttpClient client = new OkHttpClient.Builder()
.addNetworkInterceptor(new Interceptor() {
@Override
public okhttp3.Response intercept(Chain chain) throws IOException {
okhttp3.Response response = chain.proceed(chain.request());
return response.newBuilder().body(new ProgressResponseBody(response.body(),
new ProgressResponseBody.ProgressListener() {
@Override
public void onProgress(long totalSize, long downSize) {
onProgress(totalSize, downSize);
}
})).build();
}
}).build();
Retrofit retrofit = new Retrofit.Builder().client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.baseUrl("https://wawa-api.vchangyi.com/").build();
ApiService apiServer = retrofit.create(ApiService.class);
apiServer.downloadFile(url)
.map(new Function<ResponseBody, String>() {
@Override
public String apply(ResponseBody body) throws Exception {
File file = FileUtils.saveFile(body ,filePath, fileName);
return file.getPath();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(listener);
// getApiServer().downloadFile(url)
// .subscribeOn(Schedulers.io())//subscribeOn和ObserOn必须在io线程,如果在主线程会出错
// .observeOn(Schedulers.io())
// .observeOn(Schedulers.computation())//需要
// .map(new Function<ResponseBody, File>() {
// @Override
// public File apply(@NonNull ResponseBody responseBody) throws Exception {
// return listener.writeResponseBodyToDisk(responseBody, filePath, fileName);
// }
// })
// .observeOn(AndroidSchedulers.mainThread())
// .subscribe(listener);
}
public void downloadFile(final long range, final String url, final String fileName, final DownloadCallBack downloadCallback) {
//断点续传时请求的总长度
File file = new File(Constant.APP_ROOT_PATH + Constant.DOWNLOAD_DIR, fileName);
String totalLength = "-";
if (file.exists()) {
totalLength += file.length();
}
getApiServer().downloadFile("bytes=" + Long.toString(range) + totalLength, url)
.subscribe(new Observer<ResponseBody>() {
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(ResponseBody responseBody) {
RandomAccessFile randomAccessFile = null;
InputStream inputStream = null;
long total = range;
long responseLength = 0;
try {
byte[] buf = new byte[2048];
int len = 0;
responseLength = responseBody.contentLength();
inputStream = responseBody.byteStream();
String filePath = Constant.APP_ROOT_PATH + Constant.DOWNLOAD_DIR;
File file = new File(filePath, fileName);
File dir = new File(filePath);
if (!dir.exists()) {
dir.mkdirs();
}
randomAccessFile = new RandomAccessFile(file, "rwd");
if (range == 0) {
randomAccessFile.setLength(responseLength);
}
randomAccessFile.seek(range);
int progress = 0;
int lastProgress = 0;
while ((len = inputStream.read(buf)) != -1) {
randomAccessFile.write(buf, 0, len);
total += len;
lastProgress = progress;
progress = (int) (total * 100 / randomAccessFile.length());
if (progress > 0 && progress != lastProgress) {
downloadCallback.onProgress(progress);
}
}
downloadCallback.onCompleted();
} catch (Exception e) {
// Log.d(TAG, e.getMessage());
downloadCallback.onError(e.getMessage());
e.printStackTrace();
} finally {
try {
SPDownloadUtil.getInstance().save(url, total);
if (randomAccessFile != null) {
randomAccessFile.close();
}
if (inputStream != null) {
inputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onError(Throwable e) {
downloadCallback.onError(e.toString());
}
@Override
public void onComplete() {
}
});
}
/**
* 上传/下载文件 订阅监听
*
* @param listener
* @return
*/
private Observer getObserver(final ProgressListener listener) {
Observer observer = new Observer<ResponseBody>() {
@Override
public void onError(Throwable e) {
LogUtils.d("onError");
e.printStackTrace();
if (listener != null) {
listener.onUpLoadFail(e);
}
}
/**
* Notifies the Observer that the {@link Observable} has finished sending push-based notifications.
* <p>
* The {@link Observable} will not call this method if it calls {@link #onError}.
*/
@Override
public void onComplete() {
}
/**
* Provides the Observer with the means of cancelling (disposing) the
* connection (channel) with the Observable in both
* synchronous (from within {@link #onNext(ResponseBody)}) and asynchronous manner.
*
* @param d the Disposable instance whose {@link Disposable#dispose()} can
* be called anytime to cancel the connection
* @since 2.0
*/
@Override
public void onSubscribe(Disposable d) {
LogUtils.d("onSubscribe");
}
/**
* Provides the Observer with a new item to observe.
* <p>
* The {@link Observable} may call this method 0 or more times.
* <p>
* The {@code Observable} will not call this method again after it calls either {@link #onComplete} or
* {@link #onError}.
*
* @param response the item emitted by the Observable
*/
@Override
public void onNext(ResponseBody response) {
LogUtils.d("onNext :-----------");
//处理监听为空的逻辑
if (listener == null) return;
listener.onUpLoadSuccess(response);
System.out.println("--------------------------");
}
};
return observer;
}
/**
* 正常请求 订阅监听及拦截
*
* @param listener
* @return
*/
private Observer getObserver(final ResponseCallBack listener) {
Observer observer = new Observer<Response>() {
@Override
public void onError(Throwable e) {
LogUtils.d("onError");
e.printStackTrace();
if (listener != null) {
listener.onError(e);
}
}
/**
* Notifies the Observer that the {@link Observable} has finished sending push-based notifications.
* <p>
* The {@link Observable} will not call this method if it calls {@link #onError}.
*/
@Override
public void onComplete() {
}
/**
* Provides the Observer with the means of cancelling (disposing) the
* connection (channel) with the Observable in both
* synchronous (from within {@link #onNext(Response)}) and asynchronous manner.
*
* @param d the Disposable instance whose {@link Disposable#dispose()} can
* be called anytime to cancel the connection
* @since 2.0
*/
@Override
public void onSubscribe(Disposable d) {
LogUtils.d("onSubscribe");
}
/**
* Provides the Observer with a new item to observe.
* <p>
* The {@link Observable} may call this method 0 or more times.
* <p>
* The {@code Observable} will not call this method again after it calls either {@link #onComplete} or
* {@link #onError}.
*
* @param response the item emitted by the Observable
*/
@Override
public void onNext(Response response) {
LogUtils.d("onNext :-----------");
//处理监听为空的逻辑
if (listener == null) return;
if (response.code != 200) {
onError(new Throwable("业务错误"));
return;
}
//这里获得到的是类的泛型的类型
LogUtils.d("onNext rawType length :" + listener.getClass().getGenericInterfaces().length);
LogUtils.d("onNext rawType :" + listener.getClass().getGenericInterfaces()[0]);
try {
converJson(response, listener);
} catch (Exception e) {
onError(new Throwable("转换异常 :", e));
}
System.out.println("--------------------------");
}
};
return observer;
}
/**
* 转换返回对象 根据泛型类型处理转换
* @param response
* @param listener
*/
private void converJson(Response response, ResponseCallBack listener) {
String myJson="";
if(response.data==null){
myJson= new Gson().toJson(response.result);
}else
myJson= new Gson().toJson(response.data);
if (listener.getClass().getGenericInterfaces().length > 0) {
Type type = listener.getClass().getGenericInterfaces()[0];
// 泛型的实际类型
Type typeArgument = ((ParameterizedType) type).getActualTypeArguments()[0]; // 泛型的参数
LogUtils.d("onNext typeArgument :" + typeArgument);
listener.onSuccess(new Gson().fromJson(myJson, typeArgument));
} else {
listener.onSuccess(myJson);
}
}
}
数据回调接口 ResponseCallBack ,上传下载的 接口实现类似,这里就不贴了
package com.wmy.lib.mvp.http.listener;
//通过泛型类型对数据进行转换
public interface ResponseCallBack<T> {
/**
* 加载数据成功
*
* @param response 返回的数据
*/
void onSuccess(T response);
/**
* 加载数据失败
*
* @param throwable 错误信息
*/
void onError(Throwable throwable);
}
返回消息体解析 Response 这里说明一下,本人写的demo 由于接入不同接口测试 返回结构有些许的区别,故接受比较多一些,
标准 一般 msg ,data ,code 之类的就可以
package com.wmy.lib.mvp.http;
import java.io.Serializable;
/**
* 标准数据格式
* @author:wmyas
* @date:2019/7/25 11:07
*/
public class Response<T> implements Serializable {
public String msg;
public String message;
public T data;
public T result;
public int code;
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getResult() {
return result;
}
public void setResult(T result) {
this.result = result;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
@Override
public String toString() {
return "Response{" +
"msg='" + msg + '\'' +
", data=" + data +
", code=" + code +
'}';
}
}
ApiService
package com.wmy.lib.mvp.http;
import java.util.Map;
import io.reactivex.Observable;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.Call;
import retrofit2.http.Body;
import retrofit2.http.FieldMap;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.GET;
import retrofit2.http.Header;
import retrofit2.http.Multipart;
import retrofit2.http.POST;
import retrofit2.http.Part;
import retrofit2.http.PartMap;
import retrofit2.http.QueryMap;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
/**
* 公共接口定义
*
* @author:wmyas
* @date:2019/7/24 17:36
*/
public interface ApiService {
@GET
Observable<Response> get(@Url String url, @QueryMap Map<String, String> maps);
@FormUrlEncoded
@POST
Observable<Response> post(@Url String url, @FieldMap Map<String, String> maps);
@POST()
Observable<Response> postBody(@Url String url, @QueryMap Map<String, String> urlMaps, @Body RequestBody body);
@POST
@Multipart
Observable<ResponseBody> uploadFile(@Url String url, @Part MultipartBody.Part file);
@Multipart
@POST
Observable<ResponseBody> uploadFile(@Url String url, @Part MultipartBody.Part[] parts, @FieldMap Map<String, String> maps);
@Streaming
@GET
Observable<ResponseBody> downloadFile(@Url String fileUrl);
/**
* 支持断点续传
* @param range 总长度
* @param url
* @return
*/
@Streaming
@GET
Observable<ResponseBody> downloadFile(@Header("Range") String range, @Url() String url);
}
需要注意的是该网络框架的简单封装,是为了弱化业务耦合,也是为了开发时不用处理json 解析和 bean 之间来回转换
实际调用通过请求回调 传入的泛型,解析拦截, 开发时候 不会关心 外层的 msg ,code。 只需要关心data返回的bean 然后回调到ui层
RetrofitManager.getInstance().get("https://www.apiopen.top/novelApi", null, new ResponseCallBack<List<NovelBean>>() {
/**
* 加载数据成功
*
* @param response 返回的数据
*/
@Override
public void onSuccess(List<NovelBean> response) {
getView().showRecommendNovel(response);
}
@Override
public void onError(Throwable throwable) {
}
});