Androif Rxjava+RxAndroid+ReTrofit2+okHttp3使用
阿新 • • 發佈:2018-12-19
在 app 的 build.gradle 中
dependencies{ / /網路請求框架Rxjava+RxAndroid+ReTrofit2+okHttp3+RxBinding //匯入retrofit implementation 'com.squareup.retrofit2:retrofit:2.4.0' //轉換器,請求結果轉換成Model implementation 'com.squareup.retrofit2:converter-gson:2.4.0' //配合Rxjava 使用 implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0' //RxJava implementation "io.reactivex.rxjava2:rxjava:2.2.3" implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' // 必要rxandrroid依賴,切執行緒時需要用到 //Gson 庫 //implementation 'com.google.code.gson:gson:2.8.5' //日誌攔截器 implementation 'com.squareup.okhttp3:logging-interceptor:3.5.0' //RxBinding implementation 'com.jakewharton.rxbinding3:rxbinding:3.0.0-alpha1' }
一:Observer 返回的請求結果統一處理類**
package io.dcloud.H56580E2E.util; import android.accounts.NetworkErrorException; import java.net.ConnectException; import java.net.UnknownHostException; import java.util.concurrent.TimeoutException; import io.reactivex.Observer; import io.reactivex.disposables.Disposable; /** * Observer 返回的請求結果統一處理類 * @param <T> */ public abstract class BaseObserver<T> implements Observer<T> { protected String errMsg = ""; //訂閱器 protected Disposable disposable; //開始 @Override public void onSubscribe(Disposable d) { disposable = d; //請求開始 onRequestStart(); } //獲取資料 @Override public void onNext(T t) { try { //請求成功,獲取資料 onSuccees(t); } catch (Exception e) { e.printStackTrace(); } } //失敗 @Override public void onError(Throwable e) { onRequestEnd(); try { if (e instanceof ConnectException || e instanceof TimeoutException || e instanceof NetworkErrorException || e instanceof UnknownHostException) { onFailure(e, true); //網路錯誤 } else { onFailure(e, false); } } catch (Exception e1) { e1.printStackTrace(); } } //結束 @Override public void onComplete() { onRequestEnd(); } /** * 返回失敗,外部類要繼承實現的 * * @param e * @param isNetWorkError 是否是網路錯誤 * @throws Exception */ protected abstract void onFailure(Throwable e, boolean isNetWorkError) throws Exception; /** * 返回成功 外部類要繼承實現的 * * @param t * @throws Exception */ protected abstract void onSuccees(T t) throws Exception; /** * 請求開始 如果外部類要繼承實現,就加上修飾符 abstract */ protected void onRequestStart() { //開始進度條 /*if (progressHUD != null) { progressHUD.setLabel(labelTxt); }*/ } /** * 請求結束 如果外部類要繼承實現,就加上修飾符 abstract */ protected void onRequestEnd() { //取消訂閱 if (disposable != null && !disposable.isDisposed()) { disposable.dispose(); } //結束進度條 /* if (progressHUD != null) { progressHUD.dismiss(); progressHUD = null; }*/ } }
二:使用 okhttp3 配置請求攔截器,
package io.dcloud.H56580E2E.util; import android.util.Log; import java.io.IOException; import java.util.HashMap; import java.util.Map; import okhttp3.Interceptor; import okhttp3.Request; import okhttp3.Response; /** * 請求攔截器 * 專案中,每個介面都有一些基本的相同的引數 * 我們不必每個介面都去寫,可以寫一個攔截器,在攔截器裡面攔截請求,為每個請求都新增相同的公共引數。 */ public class HttpCommonInterceptor implements Interceptor { private Map<String,String> mHeaderParamsMap = new HashMap<>(); public HttpCommonInterceptor() { } @Override public Response intercept(Chain chain) throws IOException { Log.d("HttpCommonInterceptor","add common params"); Request oldRequest = chain.request(); // 新增新的引數,新增到url 中 /*HttpUrl.Builder authorizedUrlBuilder = oldRequest.url().newBuilder() .scheme(oldRequest.url().scheme()) .host(oldRequest.url().host());*/ // 新的請求 Request.Builder requestBuilder = oldRequest.newBuilder(); requestBuilder.method(oldRequest.method(), oldRequest.body()); //新增公共引數,新增到header中 if(mHeaderParamsMap.size() > 0){ for(Map.Entry<String,String> params:mHeaderParamsMap.entrySet()){ requestBuilder.header(params.getKey(),params.getValue()); } } Request newRequest = requestBuilder.build(); return chain.proceed(newRequest); } public static class Builder{ HttpCommonInterceptor mHttpCommonInterceptor; public Builder(){ mHttpCommonInterceptor = new HttpCommonInterceptor(); } public Builder addHeaderParams(String key, String value){ mHttpCommonInterceptor.mHeaderParamsMap.put(key,value); return this; } public Builder addHeaderParams(String key, int value){ return addHeaderParams(key, String.valueOf(value)); } public Builder addHeaderParams(String key, float value){ return addHeaderParams(key, String.valueOf(value)); } public Builder addHeaderParams(String key, long value){ return addHeaderParams(key, String.valueOf(value)); } public Builder addHeaderParams(String key, double value){ return addHeaderParams(key, String.valueOf(value)); } public HttpCommonInterceptor build(){ return mHttpCommonInterceptor; } } }
三:Retrofit單例初始化類
package io.dcloud.H56580E2E.util;
import java.util.concurrent.TimeUnit;
import io.dcloud.H56580E2E.service.MainInterfaceService;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
/**
* Retrofit單例初始化類
* Created by Administrator on 2017/10/7.
* 用於Retrofit的初始化
* 該類為單例模式
*/
public class RetrofitHelper {
private static final int DEFAULT_TIME_OUT = 5;//超時時間 5s
private static final int DEFAULT_READ_TIME_OUT = 10;
private Retrofit mRetrofit;
private RetrofitHelper() {
// 建立 OKHttpClient
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//連線超時時間
builder.writeTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//寫操作 超時時間
builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//讀操作超時時間
// 新增公共引數攔截器,將每次請求的公共引數寫到這裡面
HttpCommonInterceptor commonInterceptor = new HttpCommonInterceptor.Builder()
.addHeaderParams("", "")
.build();
builder.addInterceptor(commonInterceptor);
// 建立Retrofit
mRetrofit = new Retrofit.Builder()
.client(builder.build())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create()) //配置支援RJava2的 Observable
.addConverterFactory(GsonConverterFactory.create()) //設定資料解析器,會將返回的資料自動轉換為 對應的 class
.baseUrl(APIURL.bangni)
.build();
}
private static class SingletonHolder {
private static final RetrofitHelper INSTANCE = new RetrofitHelper();
}
/**
* 獲取RetrofitServiceManager
*
* @return
*/
public static RetrofitHelper getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* 獲取對應的Service
*
* @param service Service 的 class
* @param <T> 使用方法: mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
* @return
*/
public <T> T create(Class<T> service) {
return mRetrofit.create(service);
}
/**
* 首頁資料的服務
*
* @return
*/
/*public MainInterfaceService getMain_interfaceService() {
return mRetrofit.create(MainInterfaceService.class);
}*/
}
使用
mvp 模式
請求所用的實體類
package io.dcloud.H56580E2E.info;
import java.util.List;
/**
* Created by Administrator on 2018/1/25.
* 首頁輪播圖的的類
*/
public class ImageInfo {
private List<ImgsBean> imgs;
public List<ImgsBean> getImgs() {
return imgs;
}
public void setImgs(List<ImgsBean> imgs) {
this.imgs = imgs;
}
public static class ImgsBean {
@Override
public String toString() {
return "ImgsBean{" +
"src='" + src + '\'' +
", href='" + href + '\'' +
", id='" + id + '\'' +
", roles='" + roles + '\'' +
", verify='" + verify + '\'' +
'}';
}
/**
* src : /static/banner/c.png
* href : custom-service.html
* id : spring-festival
* roles :
* verify :
*/
private String src;
private String href;
private String id;
private String roles;
private String verify;
public String getSrc() {
return src;
}
public void setSrc(String src) {
this.src = src;
}
public String getHref() {
return href;
}
public void setHref(String href) {
this.href = href;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getRoles() {
return roles;
}
public void setRoles(String roles) {
this.roles = roles;
}
public String getVerify() {
return verify;
}
public void setVerify(String verify) {
this.verify = verify;
}
}
@Override
public String toString() {
return "ImageInfo{" +
"imgs=" + imgs +
'}';
}
}
一:建立 Service 服務介面
package io.dcloud.H56580E2E.service;
import io.dcloud.H56580E2E.info.ImageInfo;
import io.reactivex.Observable;
import retrofit2.http.GET;
/**
* Created by Administrator on 2018/2/23.
*/
public interface MainInterfaceService {
/**
* 獲取首頁的圖片輪播資料
* @return
*/
@GET("/static/lunbo")
Observable<ImageInfo> getShufflingImage();
}
二:建立 View 檢視介面
package io.dcloud.H56580E2E.view;
import io.dcloud.H56580E2E.info.ImageInfo;
/**
* Created by Administrator on 2018/2/23.
* 首頁資料載入
*/
public interface MainListenerView extends View{
/**
* 請求輪播的圖片成功返回的資料
* @param datas
*/
void request_ShufflingImageSuccess(ImageInfo datas);
/**
* 請求輪播的圖片異常返回的資料
* @param datas
*/
void requeste_ShufflingImageError(String datas);
}
三:建立 業務處理 Presenter 類
package io.dcloud.H56580E2E.presenter;
import android.content.Context;
import android.util.Log;
import io.dcloud.H56580E2E.info.ImageInfo;
import io.dcloud.H56580E2E.service.MainInterfaceService;
import io.dcloud.H56580E2E.util.BaseObserver;
import io.dcloud.H56580E2E.util.RetrofitHelper;
import io.dcloud.H56580E2E.view.MainListenerView;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* Created by Administrator on 2018/2/23.
* 首頁的資料請求
*/
public class MainInterfacePresenter implements Presenter{
private MainInterfaceService main_interfaceService;
private Context mcontext;
private MainListenerView main_interfaceView;
public MainInterfacePresenter(Context mcontext, MainListenerView main_interfaceView) {
this.mcontext = mcontext;
this.main_interfaceView = main_interfaceView;
//通過 RetrofitHelper 單例類獲取 服務介面
main_interfaceService= RetrofitHelper.getInstance().create(MainInterfaceService.class);
}
/**
* 獲取首頁圖片輪播的資料
*/
public void getShufflingImage(){
main_interfaceService.getShufflingImage()
.subscribeOn(Schedulers.io()) //在io執行緒中請求
.observeOn(AndroidSchedulers.mainThread())//請求完成後在主線成請求
.subscribe(new BaseObserver<ImageInfo>() {
@Override
protected void onFailure(Throwable e, boolean isNetWorkError) throws Exception {
if(isNetWorkError){
//網路錯誤
}else{
//其他錯誤
}
}
@Override
protected void onSuccees(ImageInfo imageInfo) throws Exception {
main_interfaceView.request_ShufflingImageSuccess(imageInfo);
}
});
}
}
四:在 Activity 中使用
package io.dcloud.H56580E2E;
import androidx.appcompat.app.AppCompatActivity;
import io.dcloud.H56580E2E.info.ImageInfo;
import io.dcloud.H56580E2E.presenter.MainInterfacePresenter;
import io.dcloud.H56580E2E.view.MainListenerView;
import android.os.Bundle;
//實現 服務請求完成的View操作介面(MainListenerView)
public class MainActivity extends AppCompatActivity implements MainListenerView {
//建立 Presenter 業務類物件
private MainInterfacePresenter mainInterfacePresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//例項化物件
mainInterfacePresenter=new MainInterfacePresenter(this,this);
//開始請求獲取首頁圖片輪播的資料
mainInterfacePresenter.getShufflingImage();
}
//請求輪播圖成功的
@Override
public void request_ShufflingImageSuccess(ImageInfo datas) {
}
//請求輪播圖失敗的
@Override
public void requeste_ShufflingImageError(String datas) {
}
}
幾種請求的方式:
/**
* 請求登入(傳參方法1)
* @param userphone
* @param useerpwd
* @return
* (@Query)相當於 ?a=1&b=2;
* http://102.10.10.132/user/login?userphone=123&userpwd=123
*/
@GET("user/login/")
Observable<UserInfo> getUserInfoLogin2();
/**
* 請求登入(傳參方法2)引數動態的
* @param userphone
* @param useerpwd
* @return
* (@Query)相當於 ?a=1&b=2;
* http://102.10.10.132/user/login?userphone={userphone}&userpwd={userpwd}
*/
@GET("user/login/")
Observable<UserInfo> getUserInfoLogin2(@Query("userphone") String userphone,
@Query("userpwd") String useerpwd);
/**
* 新增(傳參方法3)
* @param map
* @return
* (@Querymap)多個引數在URL問號之後,且個數不確定;
*/
@GET("user/insert/")
Observable<UserInfo> insertUserInfo(@QueryMap Map<String, String> map);
/**
* 新增(傳參方法4)
* @param userphone
* @param map
* @return
* (@Querymap)多個引數在URL問號之後,且個數不確定;混合傳參
*/
@GET("user/insert/")
Observable<UserInfo> insertUserInfo2(@Query("userphone") String userphone, @QueryMap Map<String, String> map);
/**
* 請求登入(傳參方法Post請求1)
* @param userphone
* @param useerpwd
* @return
* @Field,@FieldMap:Post方式傳遞簡單的鍵值對,@FieldMap用法和@QueryMap的格式相同(用於POST請求,提交單個數據)
* 新增@FormUrlEncoded表示表單提交 (Content-Type:application/x-www-form-urlencoded)
*/
@FormUrlEncoded
@POST("user/login/")
Observable<UserInfo> getUserInfoLogin3(@Field("userphone") String userphone, @Field("userpwd") String useerpwd);
/**
* 請求登入(傳參方法Post請求2)
* @return
* @Body:用於POST請求體,將例項物件根據轉換方式轉換為對應的json字串引數,
* 提交到後臺,傳遞的類中的屬性名必須和伺服器端接收的引數名要相同
* 這個轉化方式是GsonConverterFactory定義的。
*/
@Headers({"Content-Type:application/json;charset=utf-8", "Accept:application/json;"})
@POST("user/login/")
Observable<UserInfo> postAgencyLogin(@Body RequestBody route);//實現json格式的傳輸,例項下面會有介紹
/**
* 請求登入(傳參方法Post請求3)
* @return
* 鍵值對方式傳輸,引數組合一個 Map 集合傳遞就行
*/
@POST("user/login/")
@FormUrlEncoded
Observable<UserInfo> doLogin(@FieldMap Map<String,String> params);
/**
* method:網路請求的方法(區分大小寫)
* path:網路請求地址路徑
* hasBody:是否有請求體.。
* 作用:替換@GET、@POST、@PUT、@DELETE、@HEAD註解的作用 及 更多功能拓展
* 具體使用:通過屬性method、path、hasBody進行設定
*/
@HTTP(method = "GET", path = "user/login/{userphone}/{userpwd}", hasBody = false)
Call<ResponseBody> getCall(@Path("userphone") String userphone, @Path("userpwd") String useerpwd);
post服務請求傳遞 json型別資料
import com.google.gson.Gson;
/**
* Created by Administrator on 2016/4/15.
*
* Gson封裝類
*/
public class GsonUtils {
private static Gson gson;
static {
if (gson == null) {
gson = new Gson();
}
}
/**
* 物件轉Json字串
*
* @param object
* @return
*/
public static String toJson(Object object) {
checkGson();
return gson.toJson(object);
}
/**
* 字串轉Json物件
*
* @param json
* @param clazz
* @param <T>
* @return
*/
public static <T> T fromJson(String json, Class<T> clazz) {
checkGson();
return gson.fromJson(json, clazz);
}
private static void checkGson() {
if (gson == null) {
gson = new Gson();
}
}
}
post請求 json 型別資料傳遞
@Headers({"Content-Type:application/json;charset=utf-8", "Accept:application/json;"})
@POST("user/login/")
Observable<LoginBean> postAgencyLogin(@Body RequestBody route);
//組合引數
Map<String, String> map = new HashMap<>();
map.put("phone", "15888888888");
map.put("password", CommonUtils.encodeMD5("123456").toUpperCase());
RequestBody requestBody = RequestBody.create(MediaType.parse("application/json; charset=utf-8"), CommonUtils.convertPostJson(map));
擴充套件 OKHttp工具類(上傳,下載檔案)
package io.dcloud.H56580E2E.upload.listener.impl.handler;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import io.dcloud.H56580E2E.upload.listener.impl.UIProgressListener;
import io.dcloud.H56580E2E.upload.listener.impl.model.ProgressModel;
import java.lang.ref.WeakReference;
public abstract class ProgressHandler extends Handler {
public static final int UPDATE = 0x01;
public static final int START = 0x02;
public static final int FINISH = 0x03;
//弱引用
private final WeakReference<UIProgressListener> mUIProgressListenerWeakReference;
public ProgressHandler(UIProgressListener uiProgressListener) {
super(Looper.getMainLooper());
mUIProgressListenerWeakReference = new WeakReference<UIProgressListener>(uiProgressListener);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case UPDATE: {
UIProgressListener uiProgessListener = mUIProgressListenerWeakReference.get();
if (uiProgessListener != null) {
//獲得進度實體類
ProgressModel progressModel = (ProgressModel) msg.obj;
//回撥抽象方法
progress(uiProgessListener, progressModel.getCurrentBytes(), progressModel.getContentLength(), progressModel.isDone());
}
break;
}
case START: {
UIProgressListener uiProgressListener = mUIProgressListenerWeakReference.get();
if (uiProgressListener != null) {
//獲得進度實體類
ProgressModel progressModel = (ProgressModel) msg.obj;
//回撥抽象方法
start(uiProgressListener, progressModel.getCurrentBytes(), progressModel.getContentLength(), progressModel.isDone());
}
break;
}
case FINISH: {
UIProgressListener uiProgressListener = mUIProgressListenerWeakReference.get();
if (uiProgressListener != null) {
//獲得進度實體類
ProgressModel progressModel = (ProgressModel) msg.obj;
//回撥抽象方法
finish(uiProgressListener, progressModel.getCurrentBytes(), progressModel.getContentLength(), progressModel.isDone());
}
break;
}
default:
super.handleMessage(msg);
break;
}
}
public abstract void start(UIProgressListener uiProgressListener,long currentBytes, long contentLength, boolean done);
public abstract void progress(UIProgressListener uiProgressListener,long currentBytes, long contentLength, boolean done);
public abstract void finish(UIProgressListener uiProgressListener,long currentBytes, long contentLength, boolean done);
}
package io.dcloud.H56580E2E.upload.listener.impl.model;
import java.io.Serializable;
/**
* UI進度回撥實體類
*/
public class ProgressModel implements Serializable {
//當前讀取位元組長度
private long currentBytes;
//總位元組長度
private long contentLength;
//是否讀取完成
private boolean done;
public ProgressModel(long currentBytes, long contentLength, boolean done) {
this.currentBytes = currentBytes;
this.contentLength = contentLength;
this.done = done;
}
public long getCurrentBytes() {
return currentBytes;
}
public void setCurrentBytes(long currentBytes) {
this.currentBytes = currentBytes;
}
public long getContentLength() {
return contentLength;
}
public void setContentLength(long contentLength) {
this.contentLength = contentLength;
}
public boolean isDone() {
return done;
}
public void setDone(boolean done) {
this.done = done;
}
@Override
public String toString() {
return "ProgressModel{" +
"currentBytes=" + currentBytes +
", contentLength=" + contentLength +
", done=" + done +
'}';
}
}
package io.dcloud.H56580E2E.upload.listener.impl;
import android.os.Handler;
import android.os.Message;
import io.dcloud.H56580E2E.upload.listener.ProgressListener;
import io.dcloud.H56580E2E.upload.listener.impl.handler.ProgressHandler;
import io.dcloud.H56580E2E.upload.listener.impl.model.ProgressModel;
/**
* 請求體回撥實現類,用於UI層回撥,下載上傳進度類
*/
public abstract class UIProgressListener implements ProgressListener {
private boolean isFirst = false;
//處理UI層的Handler子類
private static class UIHandler extends ProgressHandler {
public UIHandler(UIProgressListener uiProgressListener) {
super(uiProgressListener);
}
@Override
public void start(UIProgressListener uiProgressListener, long currentBytes, long contentLength, boolean done) {
if (uiProgressListener!=null) {
uiProgressListener.onUIStart(currentBytes, contentLength, done);
}
}
@Override
public void progress(UIProgressListener uiProgressListener, long currentBytes, long contentLength, boolean done) {
if (uiProgressListener!=null){
uiProgressListener.onUIProgress(currentBytes, contentLength, done);
}
}
@Override
public void finish(UIProgressListener uiProgressListener, long currentBytes, long contentLength, boolean done) {
if (uiProgressListener!=null){
uiProgressListener.onUIFinish(currentBytes, contentLength,done);
}
}
}
//主執行緒Handler
private final Handler mHandler = new UIHandler(this);
@Override
public void onProgress(long bytesWrite, long contentLength, boolean done) {
//如果是第一次,傳送訊息
if (!isFirst) {
isFirst = true;
Message start = Message.obtain();
start.obj = new ProgressModel(bytesWrite, contentLength, done);
start.what = ProgressHandler.START;
mHandler.sendMessage(start);
}
//通過Handler傳送進度訊息
Message message = Message.obtain();
message.obj = new ProgressModel(bytesWrite, contentLength, done);
message.what = ProgressHandler.UPDATE;
mHandler.sendMessage(message);
if(done) {
Message finish = Message.obtain();
finish.obj = new ProgressModel(bytesWrite, contentLength, done);
finish.what = ProgressHandler.FINISH;
mHandler.sendMessage(finish);
}
}
/**
* UI層回撥抽象方法
*
* @param currentBytes 當前的位元組長度
* @param contentLength 總位元組長度
* @param done 是否寫入完成
*/
public abstract void onUIProgress(long currentBytes, long contentLength, boolean done);
/**
* UI層開始請求回撥方法
* @param currentBytes 當前的位元組長度
* @param contentLength 總位元組長度
* @param done 是否寫入完成
*/
public void onUIStart(long currentBytes, long contentLength, boolean done) {
}
/**
* UI層結束請求回撥方法
* @param currentBytes 當前的位元組長度
* @param contentLength 總位元組長度
* @param done 是否寫入完成
*/
public void onUIFinish(long currentBytes, long contentLength, boolean done) {
}
}
package io.dcloud.H56580E2E.upload.listener;
/**
* 進度回撥介面,比如用於檔案上傳與下載
*/
public interface ProgressListener {
void onProgress(long currentBytes, long contentLength, boolean done);
}
package io.dcloud.H56580E2E.upload.progress;
import io.dcloud.H56580E2E.upload.listener.ProgressListener;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okio.Buffer;
import okio.BufferedSink;
import okio.ForwardingSink;
import okio.Okio;
import okio.Sink;
/**
* 包裝的請求體,處理進度
*/
public class ProgressRequestBody extends RequestBody {
//實際的待包裝請求體
private final RequestBody requestBody;
//進度回撥介面
private final ProgressListener progressListener;
//包裝完成的BufferedSink
private BufferedSink bufferedSink;
/**
* 建構函式,賦值
* @param requestBody 待包裝的請求體
* @param progressListener 回撥介面
*/
public ProgressRequestBody(RequestBody requestBody, ProgressListener progressListener) {
this.requestBody = requestBody;
this.progressListener = progressListener;
}
/**
* 重寫呼叫實際的響應體的contentType
* @return MediaType
*/
@Override
public MediaType contentType() {
return requestBody.contentType();
}
/**
* 重寫呼叫實際的響應體的contentLength
* @return contentLength
* @throws IOException 異常
*/
@Override
public long contentLength() throws IOException {
return requestBody.contentLength();
}
/**
* 重寫進行寫入
* @param sink BufferedSink
* @throws IOException 異常
*/
@Override
public void writeTo(BufferedSink sink) throws IOException {
if (bufferedSink == null) {
//包裝
bufferedSink = Okio.buffer(sink(sink));
}
//寫入
requestBody.writeTo(bufferedSink);
//必須呼叫flush,否則最後一部分資料可能不會被寫入
bufferedSink.flush();
}
/**
* 寫入,回撥進度介面
* @param sink Sink
* @return Sink
*/
private Sink sink(Sink sink) {
return new ForwardingSink(sink) {
//當前寫入位元組數
long bytesWritten = 0L;
//總位元組長度,避免多次呼叫contentLength()方法
long contentLength = 0L;
@Override
public void write(Buffer source, long byteCount) throws IOException {
super.write(source, byteCount);
if (contentLength == 0) {
//獲得contentLength的值,後續不再呼叫
contentLength = contentLength();
}
//增加當前寫入的位元組數
bytesWritten += byteCount;
//回撥
if (progressListener!=null) {
progressListener.onProgress(bytesWritten, contentLength, bytesWritten == contentLength);
}
}
};
}
}
package io.dcloud.H56580E2E.upload.progress;
import io.dcloud.H56580E2E.upload.listener.ProgressListener;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
/**
* 包裝的響體,處理進度
*/
public class ProgressResponseBody extends ResponseBody {
//實際的待包裝響應體
private final ResponseBody responseBody;
//進度回撥介面
private final ProgressListener progressListener;
//包裝完成的BufferedSource
private BufferedSource bufferedSource;
/**
* 建構函式,賦值
* @param responseBody 待包裝的響應體
* @param progressListener 回撥介面
*/
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
this.responseBody = responseBody;
this.progressListener = progressListener;
}
/**
* 重寫呼叫實際的響應體的contentType
* @return MediaType
*/
@Override public MediaType contentType() {
return responseBody.contentType();
}
/**
* 重寫呼叫實際的響應體的contentLength
* @return contentLength
* @throws IOException 異常
*/
@Override public long contentLength() {
return responseBody.contentLength();
}
/**
* 重寫進行包裝source
* @return BufferedSource
* @throws IOException 異常
*/
@Override public BufferedSource source() {
if (bufferedSource == null) {
//包裝
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
/**
* 讀取,回撥進度介面
* @param source Source
* @return Source
*/
private Source source(Source source) {
return new ForwardingSource(source) {
//當前讀取位元組數
long totalBytesRead = 0L;
@Override public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
//增加當前讀取的位元組數,如果讀取完成了bytesRead會返回-1
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
//回撥,如果contentLength()不知道長度,會返回-1
if (progressListener!=null) {
progressListener.onProgress(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
}
return bytesRead;
}
};
}
}
package io.dcloud.H56580E2E.upload.helper;
import io.dcloud.H56580E2E.upload.listener.ProgressListener;
import io.dcloud.H56580E2E.upload.progress.ProgressRequestBody;
import io.dcloud.H56580E2E.upload.progress.ProgressResponseBody;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.Response;
/**
* 進度回撥輔助類
*/
public class ProgressHelper {
/**
* 包裝OkHttpClient,用於下載檔案的回撥
*
* @param client 待包裝的OkHttpClient
* @param progressListener 進度回撥介面
* @param storePath 下載的檔案的儲存路徑
* @return 包裝後的OkHttpClient,使用clone方法返回
*/
public static OkHttpClient addProgressResponseListener(OkHttpClient client, final ProgressListener progressListener, String storePath) {
Interceptor interceptor = new Interceptor() {
@Override
public Response intercept(Chain chain) throws IOException {
//攔截
Response originalResponse = chain.proceed(chain.request());
//包裝響應體並返回
return originalResponse.newBuilder()
.body(new ProgressResponseBody(originalResponse.body(), progressListener))
.build();
}
};
return client.newBuilder()
.addInterceptor(interceptor)
.build();
}
/**
* 包裝請求體用於上傳檔案的回撥
*
* @param requestBody 請求體RequestBody
* @param progressRequestListener 進度回撥介面
* @return 包裝後的進度回撥請求體
*/
public static ProgressRequestBody addProgressRequestListener(RequestBody requestBody, ProgressListener progressRequestListener) {
//包裝請求體
return new ProgressRequestBody(requestBody, progressRequestListener);
}
}
package io.dcloud.H56580E2E.upload.utils;
import android.app.Activity;
import android.util.Log;
import android.widget.Toast;
import io.dcloud.H56580E2E.upload.helper.ProgressHelper;
import io.dcloud.H56580E2E.upload.listener.ProgressListener;
import io.dcloud.H56580E2E.upload.listener.impl.UIProgressListener;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.FileNameMap;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.TimeUnit;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
/**
* OKHttp工具類(上傳,下載檔案)
*/
public class OKHttpUtils {
private static OkHttpClient client;
/**
* 建立一個OkHttpClient的物件的單例
*
* @return
*/
public synchronized static OkHttpClient getOkHttpClientInstance() {
if (client == null) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
//設定連線超時等屬性,不設定可能會報異常
.connectTimeout(120, TimeUnit.SECONDS)
.readTimeout(120, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS);
client = builder.build();
}
return client;
}
/**
* 獲取檔案MimeType
*
* @param filename 檔名
* @return
*/
private static String getMimeType(String filename) {
FileNameMap filenameMap = URLConnection.getFileNameMap();
String contentType = filenameMap.getContentTypeFor(filename);
if (contentType == null) {
contentType = "application/octet-stream"; //* exe,所有的可執行程式
}
return contentType;
}
/**
* 上傳檔案
* 獲得Request例項(不帶進度)
*
* @param url 上傳檔案到伺服器的地址
* @param fileNames 完整的檔名(帶完整路徑)
* @return
*/
private static Request getRequest(String url, List<String> fileNames) {
Request.Builder builder = new Request.Builder();
builder.url(url)
.post(getRequestBody(fileNames))
.tag(url) //設定請求的標記,可在取消時使用
;
return builder.build();
}
/**
* 上傳檔案
* 獲得Request例項(帶進度)
*
* @param url 上傳檔案到伺服器的地址
* @param fileNames 完整的檔名(帶完整路徑)
* @param uiProgressRequestListener 上傳進度的監聽器
* @return
*/
private static Request getRequest(String url, List<String> fileNames, ProgressListener uiProgressRequestListener) {
Request.Builder builder = new Request.Builder();
builder.url(url)
.post(ProgressHelper.addProgressRequestListener(
OKHttpUtils.getRequestBody(fileNames),
uiProgressRequestListener));
return builder.build();
}
/**
* 通過Url地址和表單的鍵值對來建立Request例項
*
* @param url 上傳表單資料到伺服器的地址
* @param map 由提交的表單的每一項組成的HashMap
* (如使用者名稱,key:username,value:zhangsan)
* @return
*/
private static Request getRequest(String url, HashMap<String, String> map) {
Request.Builder builder = new Request.Builder();
builder.url(url)
.post(getRequestBody(map))
.tag(url) //設定請求的標記,可在取消時使用
;
return builder.build();
}
/**
* 通過Url地址和表單的鍵值對來建立Request例項
*
* @param url 上傳表單資料到伺服器的地址
* @param map 由提交的表單的每一項組成的HashMap
* (如使用者名稱,key:username,value:zhangsan)
* @param fileNames 完整的檔案路徑名
* @return
*/
private static Request getRequest(String url, HashMap<String, String> map, List<String> fileNames) {
Request.Builder builder = new Request.Builder();
builder.url(url)
.post(getRequestBody(map, fileNames))
.tag(url) //設定請求的標記,可在取消時使用
;
return builder.build();
}
/**
* 通過下載的URL地址構建equest例項
*
* @param downloadUrl 檔案下載的地址
* @return
*/
private static Request getRequest(String downloadUrl) {
Request.Builder builder = new Request.Builder();
builder.url(downloadUrl).tag(downloadUrl);
return builder.build();
}
/**
* 通過鍵值對(表單中的name-value)建立RequestBody
*
* @param map 由提交的表單的每一項組成的HashMap
* (如使用者名稱,key:username,value:zhangsan)
* @return
*/
private static RequestBody getRequestBody(HashMap<String, String> map) {
FormBody.Builder builder = new FormBody.Builder();
for (HashMap.Entry<String, String> entry : map.entrySet()) {
builder.add(entry.getKey(), entry.getValue());
}
return builder.build();
}
/**
* 根據表單的鍵值對和上傳的檔案生成RequestBody
*
* @param map 由提交的表單的每一項組成的HashMap
* (如使用者名稱,key:username,value:zhangsan)
* @param fileNames 完整的檔案路徑名
* @return
*/
private static RequestBody getRequestBody(HashMap<String, String> map, List<String> fileNames) {
MultipartBody.Builder builder = new MultipartBody.Builder(); //建立MultipartBody.Builder,用於新增請求的資料
for (HashMap.Entry<String, String> entry : map.entrySet()) { //對鍵值對進行遍歷
builder.addFormDataPart(entry.getKey(), entry.getValue()); //把鍵值對新增到Builder中
}
for (int i = 0; i < fileNames.size(); i++) { //對檔案進行遍歷
File file = new File(fileNames.get(i)); //生成檔案
String fileType = getMimeType(file.getName()); //根據檔案的字尾名,獲得檔案型別
builder.addFormDataPart( //給Builder新增上傳的檔案
"image", //請求的名字
file.getName(), //檔案的文字,伺服器端用來解析的
RequestBody.create(MediaType.parse(fileType), file) //建立RequestBody,把上傳的檔案放入
);
}
return builder.build(); //根據Builder建立請求
}
/**
* 通過上傳的檔案的完整路徑生成RequestBody
*
* @param fileNames 完整的檔案路徑
* @return
*/
private static RequestBody getRequestBody(List<String> fileNames) {
//建立MultipartBody.Builder,用於新增請求的資料
MultipartBody.Builder builder = new MultipartBody.Builder();
for (int i = 0; i < fileNames.size(); i++) { //對檔案進行遍歷
File file = new File(fileNames.get(i)); //生成檔案
//根據檔案的字尾名,獲得檔案型別
String fileType = getMimeType(file.getName());
builder.addFormDataPart( //給Builder新增上傳的檔案
"image", //請求的名字
file.getName(), //檔案的文字,伺服器端用來解析的
RequestBody.create(MediaType.parse(fileType), file) //建立RequestBody,把上傳的檔案放入
);
}
return builder.build(); //根據Builder建立請求
}
/**
* 只上傳檔案
* 根據url,傳送非同步Post請求(帶進度)
*
* @param url 提交到伺服器的地址
* @param fileNames 完整的上傳的檔案的路徑名
* @param uiProgressRequestListener 上傳進度的監聽器
* @param callback OkHttp的回撥介面
*/
public static void doPostRequest(String url, List<String> fileNames, ProgressListener uiProgressRequestListener, Callback callback) {
Call call = getOkHttpClientInstance().newCall(getRequest(url, fileNames, uiProgressRequestListener));
call.enqueue(callback);
}
/**
* 只上傳檔案
* 根據url,傳送非同步Post請求(不帶進度)
*
* @param url 提交到伺服器的地址
* @param fileNames 完整的上傳的檔案的路徑名
* @param callback OkHttp的回撥介面
*/
public static void doPostRequest(String url, List<String> fileNames, Callback callback) {
Call call = getOkHttpClientInstance().newCall(getRequest(url, fileNames));
call.enqueue(callback);
}
/**
* 只提交表單
* 根據url和鍵值對,傳送非同步Post請求
*
* @param url 提交到伺服器的地址
* @param map 提交的表單的每一項組成的HashMap
* (如使用者名稱,key:username,value:zhangsan)
* @param callback OkHttp的回撥介面
*/
public static void doPostRequest(String url, HashMap<String, String> map, Callback callback) {
Call call = getOkHttpClientInstance().newCall(getRequest(url, map));
call.enqueue(callback);
}
/**
* 可同時提交表單,和多檔案
* 根據url和鍵值對,傳送非同步Post請求
*
* @param url 提交到伺服器的地址
* @param map 提交的表單的每一項組成的HashMap
* (如使用者名稱,key:username,value:zhangsan)
* @param fileNames 完整的上傳的檔案的路徑名
* @param callback OkHttp的回撥介面
*/
public static void doPostRequest(String url, HashMap<String, String> map, List<String> fileNames, Callback callback) {
Call call = getOkHttpClientInstance().newCall(getRequest(url, map, fileNames));
call.enqueue(callback);
}
/**
* 檔案下載(帶進度)
*
* @param downloadUrl 檔案的下載地址
* @param savePath 下載後的檔案的儲存路徑
* @param uiProgressResponseListener 下載進度的監聽器
*/
public static void downloadAndSaveFile(final Activity activity, String downloadUrl, final String savePath, UIProgressListener uiProgressResponseListener) {
//包裝Response使其支援進度回撥
ProgressHelper.addProgressResponseListener(OKHttpUtils.getOkHttpClientInstance(), uiProgressResponseListener, savePath)
.newCall(getRequest(downloadUrl))
.enqueue(new Callback() {
@Override
public void onFailure(Call call, final IOException e) {
Log.i("TAG", "下載錯誤: " + e.getMessage());
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(activity, "下載錯誤"+e.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onResponse(Call call, Response response) throws IOException {
Log.i("TAG", "伺服器響應成功");
//在本地儲存檔案
OKHttpUtils.saveDownloadFile(response, savePath);
}
});
}
//在本地儲存下載的檔案
private static void saveDownloadFile(Response response, String savePath) throws IOException {
InputStream inputStream = getInputStreamFromResponse(response);
BufferedInputStream bis = new BufferedInputStream(inputStream);
FileOutputStream fos = new FileOutputStream(savePath);
byte[] data = new byte[10 * 1024];
int len;
while ((len = bis.read(data)) != -1) {
fos.write(data, 0, len);
}
Log.i("TAG", "儲存檔案"+savePath+"成功");
fos.flush();
fos.close();
bis.close();
}
//獲取字串
public static String getString(Response response) throws IOException {
if (response != null && response.isSuccessful()) {
return response.body().string();
}
return null;
}
/**
* 根據響應獲得位元組陣列
*
* @param response
* @return
* @throws IOException
*/
public static byte[] getBytesFromResponse(Response response) throws IOException {
if (response != null && response.isSuccessful()) {
ResponseBody responseBody = response.body();
if (responseBody != null) {
return responseBody.bytes();
}
}
return null;
}
/**
* 根據響應獲得輸入流
*
* @param response
* @return
* @throws IOException
*/
public static InputStream getInputStreamFromResponse(Response response) throws IOException {
if (response != null && response.isSuccessful()) {
ResponseBody responseBody = response.body();
if (responseBody != null) {
return responseBody.byteStream();
}
}
return null;
}
/**
* 取消所有為tag的Call
*
* @param tag 請求的標記
*/
public static void cancelCallsWithTag(Object tag) {
if (tag == null) {
return;
}
synchronized (client.dispatcher().getClass()) {
for (Call call : client.dispatcher().queuedCalls()) {
if (tag.equals(call.request().tag())) call.cancel();
}
for (Call call : client.dispatcher().runningCalls()) {
if (tag.equals(call.request().tag())) call.cancel();
}
}
}
}