在 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'
}
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
*/
public abstract class BaseObserver implements Observer {
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;
}*/
}
}
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 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 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;
}
}
}
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 使用方法: mMovieService = RetrofitServiceManager.getInstance().create(MovieService.class);
* @return
*/
public T create(Class 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 imgs;
public List getImgs() {
return imgs;
}
public void setImgs(List 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 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() {
@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 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 getUserInfoLogin2(@Query("userphone") String userphone,
@Query("userpwd") String useerpwd);
/**
* 添加(传参方法3)
* @param map
* @return
* (@Querymap)多个参数在URL问号之后,且个数不确定;
*/
@GET("user/insert/")
Observable insertUserInfo(@QueryMap Map map);
/**
* 添加(传参方法4)
* @param userphone
* @param map
* @return
* (@Querymap)多个参数在URL问号之后,且个数不确定;混合传参
*/
@GET("user/insert/")
Observable insertUserInfo2(@Query("userphone") String userphone, @QueryMap Map 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 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 postAgencyLogin(@Body RequestBody route);//实现json格式的传输,实例下面会有介绍
/**
* 请求登录(传参方法Post请求3)
* @return
* 键值对方式传输,参数组合一个 Map 集合传递就行
*/
@POST("user/login/")
@FormUrlEncoded
Observable doLogin(@FieldMap Map params);
/**
* method:网络请求的方法(区分大小写)
* path:网络请求地址路径
* hasBody:是否有请求体.。
* 作用:替换@GET、@POST、@PUT、@DELETE、@HEAD注解的作用 及 更多功能拓展
* 具体使用:通过属性method、path、hasBody进行设置
*/
@HTTP(method = "GET", path = "user/login/{userphone}/{userpwd}", hasBody = false)
Call 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
* @return
*/
public static T fromJson(String json, Class 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 postAgencyLogin(@Body RequestBody route);
//组合参数
Map 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));
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 mUIProgressListenerWeakReference;
public ProgressHandler(UIProgressListener uiProgressListener) {
super(Looper.getMainLooper());
mUIProgressListenerWeakReference = new WeakReference(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 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 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 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 map, List 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 map) {
FormBody.Builder builder = new FormBody.Builder();
for (HashMap.Entry 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 map, List fileNames) {
MultipartBody.Builder builder = new MultipartBody.Builder(); //创建MultipartBody.Builder,用于添加请求的数据
for (HashMap.Entry 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 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 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 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 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 map, List 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();
}
}
}
}