網上有很多類似的封裝實例,比這個好的也有很多,爲什麼還要在寫一下呢?
主要是因爲在經歷了幾個別人的項目架構後發現,很嚴重的業務耦合全部都放到網絡框架上了,所以覺得 還是有必要記錄一下, 大神勿噴!!!
本文主要說的是 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) {
}
});