Androif Rxjava+RxAndroid+ReTrofit2+okHttp3使用

在 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 
 */
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;
        }*/
    }
}

二:使用 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 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;
        }

    }
}

三: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    使用方法:   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));
        

   
    

扩展 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 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();
            }
        }
    }
}

你可能感兴趣的:(Android)