1. 程式人生 > >RxJava2+Retrofit2+RxLifecycle3+OkHttp3網路請求封裝

RxJava2+Retrofit2+RxLifecycle3+OkHttp3網路請求封裝

入職公司後,公司要求元件化開發,經過討論後我將網路請求框架單獨進行了封裝,不過當時框架裡將常用的util和ui均放入到了共同的Common包下,導致裡面部分程式碼耦合,後來為了降低耦合性又將Common拆分為了lib_common和lib_ui,但是lib_ui依賴了lib_common,還是導致部分程式碼耦合,最新一期為了降低元件之間的耦合性,所以單獨將lib_common中的網路請求單獨拆分,並且我又做了新的封裝和完善,總之網路框架經過3次大的改造後,使用已經非常穩定了。

使用步驟

1.在Application類中進行初始化操作

ApiConfig build = new ApiConfig.Builder()
				.setBaseUrl(baseUrl)//BaseUrl,這個地方加入後項目中預設使用該url
               .setInvalidateToken(0)//Token失效碼
               .setSucceedCode(200)//成功返回碼
               .setFilter("com.mp5a5.quit.broadcastFilter")//失效廣播Filter設定
               //.setDefaultTimeout(2000)//響應時間,可以不設定,預設為2000毫秒
               //.setHeads(headMap)//動態新增的header,也可以在其他地方通過ApiConfig.setHeads()設定
               //.setOpenHttps(true)//開啟HTTPS驗證
               //.setSslSocketConfigure(sslSocketConfigure)//HTTPS認證配置
               .build();
build.init(this);

2.定義介面

public interface UploadApi {

	@Multipart

	@POST("ues/app/upload/pictures")

   Observable postUpload(@Part List partList);
}

3.建立請求例項

public class UploadService {

private final UploadApi mUploadApi;

private UploadService() {
//推薦使用這種,因為BaseUrl已經初始化了
mUploadApi = RetrofitFactory.getInstance().create(UploadApi.class);
}

public static UploadService getInstance() {

return UploadServiceHolder.S_INSTANCE;

}

private static class UploadServiceHolder {

private static final UploadService S_INSTANCE = new UploadService();

}

public Observable uploadPic(List picList) {

return mUploadApi.postUpload(picList);

}

}

4.傳送請求

UploadService.getInstance()
                    .uploadPic(t)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .compose([email protected]())
                    .subscribe(object : BaseObserver<UploadEntity>(this, true) {
                        override fun onSuccess(response: UploadEntity?) {
                            toast(response?.msg!!)
                        }
                    })

5.效果展示
在這裡插入圖片描述

封裝思想

JDK1.8的使用

由於JDK1.8中介面可以有不需要實現的方法,所以我採用了1.8的新特新封裝了網路請求返回引數的回撥。1.8還有許多新特性,沒有升級的同學可以自己查詢資料學習一波。

public interface OnBaseResponseListener {

	/**
	
	* 成功
	
	*
	
	* @param response 成功引數
	
	*/

  void onSuccess(R response);

	/**
	
	* 失敗
	
	*
	
	* @param response 失敗引數
	
	*/

  default void onFailing(R response) {}

	/**
	
	* 錯誤
	
	*/

  default void onError() {}

}

其中onSuccess()方法是必須實現的,onFailing(R response)和onError()可以不用實現,如果專案中想處理網路請求失敗和錯誤,則需要重寫onFailing(R response)和onError()方法,如果用到了封裝的loading框和Toast,則需要super.onFailing(response)和super.onError(e),否則則可以不用super。

網路請求返回的Bean

public class BaseResponseEntity implements Serializable {

  private static final long serialVersionUID = 1L;

  public int code;

  public String msg;

  public boolean success() {
    return ApiConfig.getSucceedCode() == code;
  }

  public int getTokenInvalid() {
    return ApiConfig.getInvalidateToken();
  }

}

這個是專案中所有用到網路請求返回bean的父類,其中code是返回碼,根據code判斷網路請求狀態,例如我有一個請求NBA資料的例項,我返回的bean只需要這樣寫。

public class NBAEntity extends BaseResponseEntity {


    @SerializedName("error_code")
    public int code;
    public String reason;
    public ResultBean result;


    public static class ResultBean {
      public String title;
      }
}

由於我的專案返回的code碼這個欄位並不是code,所以可以採用起別名的方式,

	@SerializedName("error_code")
    public int code;

這樣就可以解決公司返回的code碼欄位和我封裝的欄位不相同的問題,當然每個bean都寫這段程式碼顯然不是特別友好,所以你可以再封裝一個bean繼承自BaseResponseEntity,然後給code起別名就可以了,那樣專案中的其他的bean只需要繼承你自己封裝的bean。

網路狀態的封裝類BaseObserver

public abstract class BaseObserver<T extends BaseResponseEntity> implements Observer<T> {


    /**
     * dialog 顯示文字
     */
    private String mMsg;
    private CustomProgressDialogUtils progressDialogUtils;
    private Context mContext;
    private boolean mShowLoading = false;

    /**
     * token失效 傳送廣播標識
     */
    public static final String TOKEN_INVALID_TAG = "token_invalid";
    public static final String QUIT_APP = "quit_app";

    private static final String CONNECT_ERROR = "網路連線失敗,請檢查網路";
    private static final String CONNECT_TIMEOUT = "連線超時,請稍後再試";
    private static final String BAD_NETWORK = "伺服器異常";
    private static final String PARSE_ERROR = "解析伺服器響應資料失敗";
    private static final String UNKNOWN_ERROR = "未知錯誤";
    private static final String RESPONSE_RETURN_ERROR = "伺服器返回資料失敗";

    public BaseObserver() {
    }

    /**
     * 如果傳入上下文,那麼表示您將開啟自定義的進度條
     *
     * @param context 上下文
     */
    public BaseObserver(Context context, boolean isShow) {
        this.mContext = context;
        this.mShowLoading = isShow;
    }

    /**
     * 如果傳入上下文,那麼表示您將開啟自定義的進度條
     *
     * @param context 上下文
     */
    public BaseObserver(Context context, boolean isShow, String msg) {
        this.mContext = context;
        this.mShowLoading = isShow;
        this.mMsg = msg;
    }

    @Override
    public void onSubscribe(Disposable d) {
        onRequestStart();
    }


    @Override
    public void onNext(T response) {
        onRequestEnd();
        if (response.success()) {
            try {
                onSuccess(response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } else if (response.getTokenInvalid() == response.code) {
            //token失效捕捉,傳送廣播,在專案中接收該動態廣播然後做退出登入等一些列操作
            Intent intent = new Intent();
            intent.setAction(ApiConfig.getQuitBroadcastReceiverFilter());
            intent.putExtra(TOKEN_INVALID_TAG, QUIT_APP);
            AppContextUtils.getContext().sendBroadcast(intent);

        } else {
            try {
                onFailing(response);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    @Override
    public void onError(Throwable e) {
        onRequestEnd();
        if (e instanceof retrofit2.HttpException) {
            //HTTP錯誤
            onException(ExceptionReason.BAD_NETWORK);
        } else if (e instanceof ConnectException || e instanceof UnknownHostException) {
            //連線錯誤
            onException(ExceptionReason.CONNECT_ERROR);
        } else if (e instanceof InterruptedIOException) {
            //連線超時
            onException(ExceptionReason.CONNECT_TIMEOUT);
        } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
            //解析錯誤
            onException(ExceptionReason.PARSE_ERROR);
        } else {
            //其他錯誤
            onException(ExceptionReason.UNKNOWN_ERROR);
        }
    }

    private void onException(ExceptionReason reason) {
        switch (reason) {
            case CONNECT_ERROR:
                Toast.makeText(AppContextUtils.getContext(), CONNECT_ERROR, Toast.LENGTH_SHORT).show();
                break;

            case CONNECT_TIMEOUT:
                Toast.makeText(AppContextUtils.getContext(), CONNECT_TIMEOUT, Toast.LENGTH_SHORT).show();
                break;

            case BAD_NETWORK:
                Toast.makeText(AppContextUtils.getContext(), BAD_NETWORK, Toast.LENGTH_SHORT).show();
                break;

            case PARSE_ERROR:
                Toast.makeText(AppContextUtils.getContext(), PARSE_ERROR, Toast.LENGTH_SHORT).show();
                break;

            case UNKNOWN_ERROR:
            default:
                Toast.makeText(AppContextUtils.getContext(), UNKNOWN_ERROR, Toast.LENGTH_SHORT).show();
                break;
        }
    }

    @Override
    public void onComplete() {
        onRequestEnd();
    }

    /**
     * 網路請求成功並返回正確值
     *
     * @param response 返回值
     */
    public abstract void onSuccess(T response);

    /**
     * 網路請求成功但是返回值是錯誤的
     *
     * @param response 返回值
     */
    public void onFailing(T response) {
        String message = response.msg;
        if (TextUtils.isEmpty(message)) {
            Toast.makeText(AppContextUtils.getContext(), RESPONSE_RETURN_ERROR, Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(AppContextUtils.getContext(), message, Toast.LENGTH_SHORT).show();
        }
    }


    /**
     * 網路請求失敗原因
     */
    public enum ExceptionReason {
        /**
         * 解析資料失敗
         */
        PARSE_ERROR,
        /**
         * 網路問題
         */
        BAD_NETWORK,
        /**
         * 連線錯誤
         */
        CONNECT_ERROR,
        /**
         * 連線超時
         */
        CONNECT_TIMEOUT,
        /**
         * 未知錯誤
         */
        UNKNOWN_ERROR
    }

    /**
     * 網路請求開始
     */
    private void onRequestStart() {
        if (mShowLoading) {
            showProgressDialog();
        }
    }

    /**
     * 網路請求結束
     */
    private void onRequestEnd() {
        closeProgressDialog();
    }

    /**
     * 開啟Dialog
     */
    private void showProgressDialog() {
        progressDialogUtils = new CustomProgressDialogUtils();
        if (TextUtils.isEmpty(mMsg)) {
            progressDialogUtils.showProgress(mContext);
        } else {
            progressDialogUtils.showProgress(mContext, mMsg);
        }
    }

    /**
     * 關閉Dialog
     */
    private void closeProgressDialog() {
        if (progressDialogUtils != null) {
            progressDialogUtils.dismissProgress();
        }
    }

}

在onNext(T response)對網路請求成功和失敗進行處理,如果網路請求成功並且code返回的是成功的code碼,則直接用抽象方法做了處理,public abstract void onSuccess(T response),如果code返回成功但是是token失效的code碼,則使用了動態廣播將訊息傳送出去,專案中受到該廣播後,可以做退出登入等一系列操作。如果網路請求返回失敗,我只做了Toast操作。網路請求錯誤則是在onErrorr(Throwable e)中做了處理。

@Override
    public void onError(Throwable e) {
        onRequestEnd();
        if (e instanceof retrofit2.HttpException) {
            //HTTP錯誤
            onException(ExceptionReason.BAD_NETWORK);
        } else if (e instanceof ConnectException || e instanceof UnknownHostException) {
            //連線錯誤
            onException(ExceptionReason.CONNECT_ERROR);
        } else if (e instanceof InterruptedIOException) {
            //連線超時
            onException(ExceptionReason.CONNECT_TIMEOUT);
        } else if (e instanceof JsonParseException || e instanceof JSONException || e instanceof ParseException) {
            //解析錯誤
            onException(ExceptionReason.PARSE_ERROR);
        } else {
            //其他錯誤
            onException(ExceptionReason.UNKNOWN_ERROR);
        }
    }

Retrofit封裝RetrofitFactory

public class RetrofitFactory {


    private final Retrofit.Builder retrofit;
    private Retrofit build;

    private RetrofitFactory() {

        // 指定快取路徑,快取大小100Mb
        File cacheFile = new File(AppContextUtils.getContext().getCacheDir(), "HttpCache");
        Cache cache = new Cache(cacheFile, 1024 * 1024 * 100);

        OkHttpClient.Builder httpClientBuilder = new OkHttpClient().newBuilder()
                .readTimeout(ApiConfig.getDefaultTimeout(), TimeUnit.MILLISECONDS)
                .connectTimeout(ApiConfig.getDefaultTimeout(), TimeUnit.MILLISECONDS)
                .addInterceptor(HttpLoggerInterceptor.getLoggerInterceptor())
                .addInterceptor(new HttpHeaderInterceptor())
                .addNetworkInterceptor(new HttpCacheInterceptor())
                .cache(cache);

        if (ApiConfig.getOpenHttps()) {
            httpClientBuilder.sslSocketFactory(1 == ApiConfig.getSslSocketConfigure().getVerifyType() ?
                    SslSocketFactory.getSSLSocketFactory(ApiConfig.getSslSocketConfigure().getCertificateInputStream()) :
                    SslSocketFactory.getSSLSocketFactory(), new UnSafeTrustManager());
            httpClientBuilder.hostnameVerifier(new UnSafeHostnameVerify());
        }

        OkHttpClient httpClient = httpClientBuilder.build();

        Gson gson = new GsonBuilder()
                .setDateFormat("yyyy-MM-dd HH:mm:ss")
                .serializeNulls()
                .registerTypeAdapterFactory(new NullTypeAdapterFactory())
                .create();

        retrofit = new Retrofit.Builder()
                .client(httpClient)
                .addConverterFactory(GsonConverterFactory.create(gson))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create());

        if (!TextUtils.isEmpty(ApiConfig.getBaseUrl())) {
            build = retrofit.baseUrl(ApiConfig.getBaseUrl()).build();
        }

    }

    private static class RetrofitFactoryHolder {
        private static final RetrofitFactory INSTANCE = new RetrofitFactory();
    }

    public static final RetrofitFactory getInstance() {
        return RetrofitFactoryHolder.INSTANCE;
    }


    /**
     * 根據Api介面類生成Api實體
     *
     * @param clazz 傳入的Api介面類
     * @return Api實體類
     */
    public <T> T create(Class<T> clazz) {
        checkNotNull(build, "BaseUrl not init,you should init first!");
        return build.create(clazz);
    }

    /**
     * 根據Api介面類生成Api實體
     *
     * @param baseUrl baseUrl
     * @param clazz   傳入的Api介面類
     * @return Api實體類
     */
    public <T> T create(String baseUrl, Class<T> clazz) {
        return retrofit.baseUrl(baseUrl).build().create(clazz);
    }

    private <T> T checkNotNull(@Nullable T object, String message) {
        if (object == null) {
            throw new NullPointerException(message);
        }
        return object;
    }
}

create()方法之所以有兩個,是因為當時專案中涉及到動態切換BaseUrl需求所以才封裝了一個兩個引數的create方法。

攔截器封裝

快取攔截器

public class HttpCacheInterceptor implements Interceptor {
    @Override
    @EverythingIsNonNull
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        //沒網強制從快取讀取
        if (!NetworkUtils.isConnected(AppContextUtils.getContext())) {
            request = request.newBuilder()
                    .cacheControl(CacheControl.FORCE_CACHE)
                    .build();
            Log.e("-->", "no network");
        }

        Response originalResponse = chain.proceed(request);

        if (NetworkUtils.isConnected(AppContextUtils.getContext())) {
            //有網的時候讀介面上的@Headers裡的配置
            String cacheControl = request.cacheControl().toString();
            return originalResponse.newBuilder()
                    .header("Cache-Control", cacheControl)
                    .removeHeader("Pragma")
                    .build();
        } else {
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, only-if-cached, max-stale=2419200")
                    .removeHeader("Pragma")
                    .build();
        }
    }
}

請求頭攔截器

public class HttpHeaderInterceptor implements Interceptor {

    @Override
    @EverythingIsNonNull
    public Response intercept(Chain chain) throws IOException {

        Request originalRequest = chain.request();

        Map<String, String> heads = ApiConfig.getHeads();

        String token = ApiConfig.getToken();

        Request.Builder authorization = originalRequest.newBuilder()
                .header("Content-type", "application/json")
                .header("Authorization", token)
                .addHeader("Connection", "close")
                .addHeader("Accept-Encoding", "identity");

        //動態新增Header
        if (null != heads) {
            heads.forEach(new BiConsumer<String, String>() {
                @Override
                public void accept(String key, String value) {
                    authorization.addHeader(key, value);
                }
            });
        }

        Request build = authorization.build();

        return chain.proceed(build);
    }
}

log攔截器

public class HttpLoggerInterceptor {

    public static HttpLoggingInterceptor getLoggerInterceptor() {
        //日誌攔截器
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(message -> {
            Log.e("-->", message.toString());
        });

        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        return interceptor;
    }

}

HTTPS認證

public class SslSocketFactory {

    /**
     * HTTPS單向認證
     *
     * @return
     */
    public static SSLSocketFactory getSSLSocketFactory(InputStream... certificates) {
        try {
            CertificateFactory certificateFactory = CertificateFactory.getInstance(ApiConfig.getSslSocketConfigure().getCertificateType());
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            keyStore.load(null);
            int index = 0;
            for (InputStream input : certificates) {
                String iAlias = Integer.toString(index++);
                keyStore.setCertificateEntry(iAlias, certificateFactory.generateCertificate(input));
                try {
                    if (null != input) {
                        input.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            SSLContext sslContext = SSLContext.getInstance(ApiConfig.getSslSocketConfigure().getProtocolType());
            TrustManagerFactory managerF = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
            managerF.init(keyStore);
            sslContext.init(null, managerF.getTrustManagers(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * HTTPS雙向認證
     *
     * @return
     */
    public static SSLSocketFactory getSSLSocketFactory() {

        try {
            KeyStore keyStore = KeyStore.getInstance(ApiConfig.getSslSocketConfigure().getKeystoreType());
            KeyStore trustStore = KeyStore.getInstance(ApiConfig.getSslSocketConfigure().getKeystoreType());
            InputStream keyInput = AppContextUtils.getContext().getAssets().open(ApiConfig.getSslSocketConfigure().getClientPriKey());
            InputStream trustInput = AppContextUtils.getContext().getAssets().open(ApiConfig.getSslSocketConfigure().getTrustPubKey());
            keyStore.load(keyInput, ApiConfig.getSslSocketConfigure().getClientBKSPassword().toCharArray());
            trustStore.load(trustInput, ApiConfig.getSslSocketConfigure().getTruststoreBKSPassword().toCharArray());

            try {
                if (null != keyInput) {
                    keyInput.close();
                }
                if (null != keyInput) {
                    trustInput.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

            SSLContext sslContext = SSLContext.getInstance(ApiConfig.getSslSocketConfigure().getProtocolType());
            TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(ApiConfig.getSslSocketConfigure().getCertificateType());
            KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(ApiConfig.getSslSocketConfigure().getCertificateType());
            trustManagerFactory.init(trustStore);
            keyManagerFactory.init(keyStore, ApiConfig.getSslSocketConfigure().getClientBKSPassword().toCharArray());
            sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom());
            return sslContext.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}
public class UnSafeHostnameVerify implements HostnameVerifier {
    @SuppressLint("BadHostnameVerifier")
    @Override
    public boolean verify(String hostname, SSLSession session) {
        return true;
    }
}
public class UnSafeTrustManager implements X509TrustManager {
    @SuppressLint("TrustAllX509TrustManager")
    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

    }

    @SuppressLint("TrustAllX509TrustManager")
    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}

Gson返回null處理

public class NullTypeAdapterFactory<T> implements TypeAdapterFactory {

    @Override
    @SuppressWarnings("unchecked")
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        Class<T> rawType= (Class<T>) type.getRawType();
        if (rawType!=String.class) {
            return null;
        }
        return (TypeAdapter<T>) new NullAdapter();
    }
}
public class NullAdapter extends TypeAdapter<String> {

    @Override
    public void write(JsonWriter out, String value) throws IOException {
        if (null == value) {
            out.nullValue();
            return;
        }
        out.value(value);
    }

    @Override
    public String read(JsonReader in) throws IOException {
        if (JsonToken.NULL == in.peek()) {
            in.nextNull();
            return "";
        }
        return in.nextString();
    }
}

使用配置類ApiConfig

這個類是對所有初始化引數進行配置的地方,比如返回碼code,BaseURL,失效InvalidateToken,請求頭Heads,是否開啟https認證等的一系配置。採用了建造者模式,就是為了方便開發者鏈式呼叫。

public class ApiConfig implements Serializable {

    private static int mInvalidateToken;
    private static String mBaseUrl;
    private static int mDefaultTimeout = 2000;
    private static int mSucceedCode;
    private static String mQuitBroadcastReceiverFilter;
    private static ArrayMap<String, String> mHeads;
    private static String mToken = "";
    private static boolean mOpenHttps;
    private static SslSocketConfigure mSslSocketConfigure;

    private ApiConfig(Builder builder) {
        mInvalidateToken = builder.invalidateToken;
        mBaseUrl = builder.baseUrl;
        mDefaultTimeout = builder.defaultTimeout;
        mSucceedCode = builder.succeedCode;
        mQuitBroadcastReceiverFilter = builder.broadcastFilter;
        mHeads = builder.heads;
        mOpenHttps = builder.openHttps;
        mSslSocketConfigure = builder.sslSocketConfigure;
    }

    public void init(Context appContext) {
        AppContextUtils.init(appContext);
    }

    public static int getInvalidateToken() {
        return mInvalidateToken;
    }

    public static String getBaseUrl() {
        return mBaseUrl;
    }

    public static int getDefaultTimeout() {
        return mDefaultTimeout;
    }

    public static int getSucceedCode() {
        return mSucceedCode;
    }

    public static String getQuitBroadcastReceiverFilter() {
        return mQuitBroadcastReceiverFilter;
    }

    public static ArrayMap<String, String> getHeads() {
        return mHeads;
    }

    public static void setHeads(ArrayMap<String, String> mHeads) {
        ApiConfig.mHeads = mHeads;
    }


    public static String getToken() {
        return mToken;
    }

    public static void setToken(String mToken) {
        ApiConfig.mToken = mToken;
    }

    public static boolean getOpenHttps() {
        return mOpenHttps;
    }


    public static SslSocketConfigure getSslSocketConfigure() {
        return mSslSocketConfigure;
    }

    public static final class Builder  {

        private int invalidateToken;

        private String baseUrl;

        private int defaultTimeout;

        private int succeedCode;

        private String broadcastFilter;

        private ArrayMap<String, String> heads;

        private boolean openHttps = false;

        private SslSocketConfigure sslSocketConfigure;

        public Builder setHeads(ArrayMap<String, String> heads) {
            this.heads = heads;
            return this;
        }

        public Builder setFilter(@NonNull String filter) {
            this.broadcastFilter = filter;
            return this;
        }


        public Builder setSucceedCode(int succeedCode) {
            this.succeedCode = succeedCode;
            return this;
        }

        public Builder setBaseUrl(String mBaseUrl) {
            this.baseUrl = mBaseUrl;
            return this;
        }

        public Builder setInvalidateToken(int invalidateToken) {
            this.invalidateToken = invalidateToken;
            return this;
        }

        public Builder setDefaultTimeout(int defaultTimeout) {
            this.defaultTimeout = defaultTimeout;
            return this;
        }

        public Builder setOpenHttps(boolean open) {
            this.openHttps = open;
            return this;
        }

        public Builder setSslSocketConfigure(SslSocketConfigure sslSocketConfigure) {
            this.sslSocketConfigure = sslSocketConfigure;
            return this;
        }

        public ApiConfig build() {
            return new ApiConfig(this);
        }
    }
}

檔案請求上傳

採用RxJava進行流的切換操作。

public Single<List<MultipartBody.Part>> uploadMultiPicList(@NonNull List<File> pathList) {

        return Flowable.fromIterable(pathList).concatMap((Function<File, Flowable<MultipartBody.Part>>) f -> {
            Bitmap bitmap = BitmapFactory.decodeFile(f.toString());
            File file = compressBitmapToFile(bitmap, AppContextUtils.getContext());
            Log.e("-->檔案大小:", bytesTrans(file.length()) + ",fileSize=" + file.length() / 1024 + "kb");
            RequestBody requestBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
            MultipartBody.Part imageBodyPart = MultipartBody.Part.createFormData("file", file.getName(), requestBody);
            return Flowable.just(imageBodyPart);
        }).collect((Callable<List<MultipartBody.Part>>) ArrayList::new, List::add)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread());
    }

由於好久都沒有寫文章了,所以可能存在一些問題歡迎大家指正。

程式碼傳送門:https://github.com/Mp5A5/HttpRequest 歡迎大家fork或者star。