[Android開源框架]RxHttp使用說明

RxHttp

對RxJava2+Retrofit2+OkHttp3的封裝,優(yōu)雅實現(xiàn)接口請求和文件下載

GitHub主頁

Demo下載

功能簡介

  • 網(wǎng)絡請求(RxRequest)
    • 支持監(jiān)聽請求聲明周期,如開始結束和網(wǎng)絡錯誤
    • 支持多BaseUrl,可針對不同請求重定向
    • 支持針對不同請求設置不同緩存策略,如無網(wǎng)強制獲取緩存,有網(wǎng)緩存有效10秒
    • 支持添加公共請求參數(shù)
    • 支持自定義異常處理和異常提示消息
  • 文件下載(RxDownload)
    • 支持斷點續(xù)傳
    • 支持下載進度回調
    • 支持下載速度回調
    • 支持下載過程狀態(tài)監(jiān)聽
    • 支持在僅保存下載路徑未保存進度時自動恢復斷點續(xù)傳
    • 支持自動獲取真實文件名

發(fā)起請求之RxRequest

使用說明

一、初始化

  1. 新建網(wǎng)絡請求配置類繼承RequestSetting或DefaultRequestSetting,并復寫部分方法。
public class RxHttpRequestSetting extends DefaultRequestSetting {

    @NonNull
    @Override
    public String getBaseUrl() {
        return Config.BASE_URL;
    }

    @Override
    public int getSuccessCode() {
        return 200;
    }
}
  1. 在Application中初始化并傳入配置類實例
RxHttp.init(this);
RxHttp.initRequest(new RxHttpRequestSetting());

二、定義公共請求頭攔截器

public class PublicHeadersInterceptor implements Interceptor {
    private static String TIME = "";
    private static String TOKEN = "";

    public static void updateTime(String time) {
        PublicHeadersInterceptor.TIME = time;
    }

    public static void updateToken(String token) {
        PublicHeadersInterceptor.TOKEN = token;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        request = request.newBuilder()
                .header(Constant.PUBLIC_HEADER_TIME_NAME, TIME)
                .header(Constant.PUBLIC_HEADER_SIGN_NAME, getSign(request))
                .build();
        return chain.proceed(request);
    }

    private String getSign(Request request){
        return MD5Coder.encode(request.url().url().toString() + "?token=" + TOKEN);
    }
}

三、定義公共參數(shù)攔截器

public class PublicParamsInterceptor implements Interceptor {
    private static final String GET = "GET";
    private static final String POST = "POST";

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        String method = request.method();
        if (TextUtils.equals(method, GET)) {
            request = addForGet(request);
        } else if (TextUtils.equals(method, POST)) {
            request = addForPost(request);
        }
        return chain.proceed(request);
    }

    private Request addForGet(Request request) {
        List<Param> params = getPublicParams();
        HttpUrl httpUrl = request.url();
        HttpUrl.Builder httpUrlBuilder = httpUrl.newBuilder();
        for (int i = 0; i < httpUrl.querySize(); i++) {
            String name = httpUrl.queryParameterName(i);
            String value = httpUrl.queryParameterValue(i);
            params.add(new Param(name, value));
            httpUrlBuilder.removeAllQueryParameters(name);
        }
        JsonObjUtils json = JsonObjUtils.create();
        for (Param param : params) {
            json.add(param.getKey(), param.getValue());
        }
        LogUtils.i("PublicParamsInterceptor", "data=" + json.toJson());
        httpUrlBuilder.setQueryParameter(Constant.PUBLIC_PARAM_KEY, json.toJson());
        return request.newBuilder()
                .url(httpUrlBuilder.build())
                .build();
    }

    private Request addForPost(Request request) {
        RequestBody requestBody = request.body();
        if (requestBody == null) {
            return request;
        } else if (requestBody instanceof FormBody) {
            List<Param> params = getPublicParams();
            FormBody formBody = (FormBody) requestBody;
            for (int i = 0; i < formBody.size(); i++) {
                params.add(new Param(formBody.name(i), formBody.value(i)));
            }
            JsonObjUtils json = JsonObjUtils.create();
            for (Param param : params) {
                json.add(param.getKey(), param.getValue());
            }
            LogUtils.i("PublicParamsInterceptor", "data=" + json.toJson());
            FormBody.Builder formBodyBuilder = new FormBody.Builder()
                    .add(Constant.PUBLIC_PARAM_KEY, json.toJson());
            return request.newBuilder()
                    .post(formBodyBuilder.build())
                    .build();
        } else if (requestBody instanceof MultipartBody) {
            return request;
        } else {
            try {
                if (requestBody.contentLength() == 0) {
                    List<Param> params = getPublicParams();
                    JsonObjUtils json = JsonObjUtils.create();
                    for (Param param : params) {
                        json.add(param.getKey(), param.getValue());
                    }
                    LogUtils.i("PublicParamsInterceptor", "data=" + json.toJson());
                    FormBody.Builder formBodyBuilder = new FormBody.Builder()
                            .add(Constant.PUBLIC_PARAM_KEY, json.toJson());
                    return request.newBuilder()
                            .post(formBodyBuilder.build())
                            .build();
                } else {
                    return request;
                }
            } catch (IOException e) {
                return request;
            }
        }
    }

    private List<Param> getPublicParams() {
        List<Param> params = new ArrayList<>();
        params.add(new Param(Constant.PUBLIC_PARAM_SYSTEM_KEY, Constant.PUBLIC_PARAM_SYSTEM_VALUE));
        params.add(new Param(Constant.PUBLIC_PARAM_VERSION_KEY, String.valueOf(AppInfoUtils.getVersionCode())));
        params.add(new Param(Constant.PUBLIC_PARAM_USER_ID_KEY, UserUtils.getInstance().getUserId()));
        params.add(new Param(Constant.PUBLIC_PARAM_USER_DEVICE_KEY, DeviceIdUtils.getId()));
        params.add(new Param(Constant.PUBLIC_PARAM_JPUSH_DEVICE_KEY, JPushHelper.getId()));
        return params;
    }
}

四、定義響應體結構

定義ResponseBean< E >繼承BaseResponse< E >,定義成員變量并實現(xiàn)方法。

public class ResponseBean<E> implements BaseResponse<E> {
    @SerializedName(value = "code"/*, alternate = {"status"}*/)
    private int code;
    @SerializedName(value = "data"/*, alternate = {"result"}*/)
    private E data;
    @SerializedName(value = "msg"/*, alternate = {"message"}*/)
    private String msg;

    @Override
    public int getCode() {
        return code;
    }

    @Override
    public void setCode(int code) {
        this.code = code;
    }

    @Override
    public E getData() {
        return data;
    }

    @Override
    public void setData(E data) {
        this.data = data;
    }

    @Override
    public String getMsg() {
        return msg;
    }

    @Override
    public void setMsg(String msg) {
        this.msg = msg;
    }
}

五、定義接口數(shù)據(jù)結構

public class TimeBean extends BaseBean {
    private String token;
    private String time;

    public String getToken() {
        return token;
    }

    public void setToken(String token) {
        this.token = token;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }
}

六、定義Api接口類

  1. 新建子類繼承自Api
  2. 定義一個內部類Service聲明請求
  3. 定義靜態(tài)無參方法返回Api.api(Service.class)創(chuàng)建Api實例
public class FreeApi extends Api {

    public static Service api() {
        return Api.api(Service.class);
    }

    public interface Config {
        String BASE_URL = Config.HTTP_HOST + Config.HTTP_VERSION;
        long HTTP_TIMEOUT = 5000;
    }

    public interface Code{
        int TIME_OUT = 1000;                     // 請求延遲
        int REQUEST_ERROR = 1001;                // 請求方式錯誤
        int ILLEGAL_PARAMETER = 1002;            // 非法參數(shù)

        int SUCCESS = 2000;                      // 獲取信息成功
        int SUCCESS_OLD = 104;                   // 獲取信息成功 老版本的
        int SUCCESS_NO_DATA = 2001;              // 暫無相關數(shù)據(jù)

        int FAILED = 3000;                       // 獲取信息失敗
        int PHONE_EXIST = 3001;                  // 該手機號已注冊過
        int PASSWARD_ERROR = 3002;               // 密碼錯誤
        int PHONE_ILLEGAL = 3003;                // 手機號不合法
        int PHONE_NOT_BIND = 3004;               // 請綁定手機號
        int PHONE_NOT_REGIST = 3005;             // 該手機號未注冊
        
        int ACCOUNT_NOT_EXIST = 4001;            // 賬號不存在
        int ACCOUNT_EXCEPTION = 4002;            // 賬號異常,請重新登錄
        int ACCOUNT_FROZEN = 4003;               // 該賬戶已被凍結,請聯(lián)系管理員
        int ACCOUNT_DELETED = 4004;              // 該賬戶已被管理員刪除

        int ERROR = 5000;                        // 訪問異常
        int ERROR_NET = 5001;                    // 網(wǎng)絡異常
    }

    public interface Service {
        @GET("public/time")
        Observable<ResponseBean<TimeBean>> getTime();
    }
}

七、定義請求回調

public interface RequestBackListener<T> {
    void onStart();
    void onSuccess(int code, T data);
    void onFailed(int code, String msg);
    void onNoNet();
    void onError(Throwable e);
    void onFinish();
}

八、封裝BaseRequest基類

封裝簽名的獲取和響應回調的處理邏輯。

在調用正式接口之前,我們可能需要先調用一個獲取時間戳的接口,將時間戳接口返回的時間戳和簽名字段添加到正式接口的公共參數(shù)或請求頭中,才能發(fā)起正式請求。所以在該基類中進行封裝,抽取公共代碼。

如果時間戳接口返回了版本更新字段,需要版本號等判斷請求的執(zhí)行流程,如強制更新且不需要繼續(xù)執(zhí)行正式接口,可在此處自行處理。

public class BaseRequest {
    protected static <T> Disposable requestWithSign(@NonNull RequestCallback<T> observable, @NonNull RequestBackListener<T> callback) {
        return request(ProjectApi.api().getTime()
                .flatMap(new Function<ResponseBean<TimeBean>, ObservableSource<ResponseBean<T>>>() {
                    @Override
                    public Observable<ResponseBean<T>> apply(ResponseBean<TimeBean> bean) {
                        PublicHeadersInterceptor.updateTime(bean.getData().getTime());
                        PublicHeadersInterceptor.updateToken(bean.getData().getToken());
                        return observable.request().subscribeOn(Schedulers.io());
                    }
                }), callback);
    }

    protected static <T> Disposable request(@NonNull Observable<ResponseBean<T>> observable, @NonNull RequestBackListener<T> callback) {
        return RxRequest.create(observable)
                .listener(new RxRequest.RequestListener() {
                    @Override
                    public void onStart() {
                        callback.onStart();
                    }

                    @Override
                    public void onError(ExceptionHandle handle) {
                        handle.getException().printStackTrace();
                        if (handle.getCode() == ExceptionHandle.Code.NET) {
                            ToastMaker.showShort(R.string.http_no_net);
                            callback.onNoNet();
                            callback.onFailed(ProjectApi.Code.ERROR_NET, ResUtils.getString(R.string.http_no_net));
                        } else {
                            callback.onError(handle.getException());
                            callback.onFailed(ProjectApi.Code.ERROR, ResUtils.getString(R.string.http_error));
                        }
                    }

                    @Override
                    public void onFinish() {
                        callback.onFinish();
                    }
                })
                .request(new RxRequest.ResultCallback<T>() {
                    @Override
                    public void onSuccess(int code, T data) {
                        callback.onSuccess(code, data);
                    }

                    @Override
                    public void onFailed(int code, String msg) {
                        if (code == ProjectApi.Code.ACCOUNT_NOT_EXIST ||
                                code == ProjectApi.Code.ACCOUNT_EXCEPTION ||
                                code == ProjectApi.Code.ACCOUNT_FROZEN ||
                                code == ProjectApi.Code.ACCOUNT_DELETED) {
                            ForceOfflineReceiver.send(code, msg);
                        }
                        callback.onFailed(code, msg);
                    }
                });
    }

    protected interface RequestCallback<T> {
        Observable<ResponseBean<T>> request();
    }
}

九、新建請求類

public class PublicRequest extends BaseRequest {
    /**
     * 獲取系統(tǒng)時間
     */
    public static Disposable getTime(final RequestBackListener<TimeBean> listener) {
        return request(ProjectApi.api().getTime(), listener);
    }

    /**
     * 獲取反饋類型
     */
    public static Disposable other(final RequestBackListener<OtherBean> listener) {
        return requestWithSign(new RequestCallback<OtherAean>() {
            @Override
            public Observable<ResponseBean<OtherBean>> request() {
                return ProjectApi.api().otherApi();
            }
        }, listener);
    }
}

十、發(fā)起請求

你可以在Activity或者Fragment中發(fā)起請求,也可以在你的Presenter層中發(fā)起請求,只需要注意請求生命周期的管理。

使用時分為3步處理:

  1. 在onCreate方法中(如果是Presenter中使用應該在其綁定到視圖時)調用RxLife.create()方法,該方法會返回一個RxLife實例mRxLife。
  2. 在onDestroy方法中(如果是Presenter中使用應該在其從視圖解除綁定時)調用mRxLife.destroy()方法,該方法會自動中斷所有未完成的請求,防止內存泄漏。
  3. 發(fā)起一個請求,并調用mRxLife.add(Disposable)添加至管理隊列。

下面將以MVP模式進行舉例說明。

  1. 在P層基類中添加RxLife的創(chuàng)建和銷毀,并提供addToRxLife方法。
public abstract class MvpPresenter<V extends MvpView> {
    protected Context context;
    private V baseView;
    private RxLife rxLife;

    void onCreate(V baseView) {
        this.baseView = baseView;
        context = baseView.getContext();
        rxLife = RxLife.create();
    }

    void onDestroy() {
        baseView = null;
        context = null;
        rxLife.destroy();
        rxLife = null;
    }

    public RxLife getRxLife() {
        return rxLife;
    }

    public void addToRxLife(Disposable disposable) {
        if (rxLife != null) {
            rxLife.add(disposable);
        }
    }

    public V getBaseView() {
        return baseView;
    }

    public boolean isAttachView() {
        return baseView != null;
    }

    public Context getContext() {
        return context;
    }
}
  1. 新建P層,發(fā)起請求。
public class OtherPresenter extends MvpPresenter<FeedbackView> {
    public void other() {
        addToRxLife(PublicRequest.other(new RequestBackListener<OtherBean>() {
            @Override
            public void onStart() {
                showLoading();
            }

            @Override
            public void onSuccess(int code, OtherBean data) {
                if (isAttachView()) {
                    getBaseView().otherSuccess(code, data);
                }
            }

            @Override
            public void onFailed(int code, String msg) {
                if (isAttachView()) {
                    getBaseView().otherFail(code, msg);
                }
            }

            @Override
            public void onNoNet() {
            }

            @Override
            public void onError(Throwable e) {
            }

            @Override
            public void onFinish() {
                dismissLoading();
            }
        }));
    }
}
  1. 新建V層接口,并在Activity或Fragment實現(xiàn)接口回調,進行數(shù)據(jù)展示。
public interface OtherView extends MvpView {
    void otherSuccess(int code, OtherBean data);
    void otherFail(int code, String msg);
}

API

JsonObjUtils

創(chuàng)建JSONObject對象并生成Json字符串。

RequestBodyUtils

創(chuàng)建RequestBody,針對POST請求。

如圖片上傳接口

/**
 * 鍵        值
 * img      File
 * content  String
 */
@Multipart
@POST("public/img")
Observable<ResponseBean<UploadImgBean>> uploadImg(@PartMap Map<String, RequestBody> img);

發(fā)起請求如下

public static Disposable uploadImg(String content, File imgFile, final RequestBackListener<UploadImgBean> listener) {
    return requestWithSign(new RequestCallback<UploadImgBean>() {
        @Override
        public Observable<ResponseBean<UploadImgBean>> request() {
            Map<String, RequestBody> map = RequestBodyUtils.builder()
                .add("content", content)
                .add("img", imgFile)
                .build();
            return ProjectApi.api().uploadImg(map);
        }
    }, listener);
}

HttpsCompat

主要提供7個靜態(tài)方法,用于實現(xiàn)證書忽略和開啟Android4.4及以下對TLS1.2的支持。

/**
 * 忽略證書的驗證,這樣請求就和HTTP一樣,失去了安全保障,不建議使用
 */
public static void ignoreSSLForOkHttp(OkHttpClient.Builder builder)
    
/**
 * 開啟HttpsURLConnection對TLS1.2的支持
 */
public static void enableTls12ForOkHttp(OkHttpClient.Builder builder)
    
/**
 * 忽略證書的驗證,這樣請求就和HTTP一樣,失去了安全保障,不建議使用
 * 應在使用HttpsURLConnection之前調用,建議在application中
 */
public static void ignoreSSLForHttpsURLConnection()
    
/**
 * 開啟HttpsURLConnection對TLS1.2的支持
 * 應在使用HttpsURLConnection之前調用,建議在application中
 */
public static void enableTls12ForHttpsURLConnection()
    
/**
 * 獲取開啟TLS1.2的SSLSocketFactory
 * 建議在android4.4及以下版本調用
 */
public static SSLSocketFactory getEnableTls12SSLSocketFactory()
    
/**
 * 獲取忽略證書的HostnameVerifier
 * 與{@link #getIgnoreSSLSocketFactory()}同時配置使用
 */
public static HostnameVerifier getIgnoreHostnameVerifier()
    
/**
 * 獲取忽略證書的SSLSocketFactory
 * 與{@link #getIgnoreHostnameVerifier()}同時配置使用
 */
public static SSLSocketFactory getIgnoreSSLSocketFactory()

常見問題

在Android9.0及以上系統(tǒng)HTTP請求無響應

官方資料在框架安全性變更提及,如果應用以 Android 9 或更高版本為目標平臺則默認情況下啟用網(wǎng)絡傳輸層安全協(xié)議 (TLS),即 isCleartextTrafficPermitted() 函數(shù)返回 false。 如果您的應用需要為特定域名啟用明文,您必須在應用的網(wǎng)絡安全性配置中針對這些域名將 cleartextTrafficPermitted 顯式設置為 true。

因此解決辦法有2種:

第一種,啟用HTTP,允許明文傳輸(不建議采用)

  1. 在資源文件夾res/xml下面創(chuàng)建network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
    <base-config cleartextTrafficPermitted="true">
        <trust-anchors>
            <certificates src="system" />
        </trust-anchors>
    </base-config>
</network-security-config>
  1. 在清單文件AndroidManifest.xml的application標簽里面設置networkSecurityConfig屬性引用。
<?xml version="1.0" encoding="utf-8"?>
<manifest ... >
    <application
        android:networkSecurityConfig="@xml/network_security_config">
    </application>
</manifest>

第二種,所有接口采用HTTPS協(xié)議(建議采用)

此方法需確保后臺正確配置,如配置后仍有無法訪問,且提示證書異常,請檢查后臺配置。

HTTPS請求訪問時提示證書異常

該情況一般為后臺未正確配置證書。請檢查后臺配置。

在測試時,我們可以暫時選擇忽略證書,這樣請求就和HTTP一樣,但會失去安全保障,不允許在正式發(fā)布時使用。

可直接使用HttpsCompat工具類。

實現(xiàn)代碼如下:

public static void ignoreSSLForOkHttp(OkHttpClient.Builder builder) {
    builder.hostnameVerifier(getIgnoreHostnameVerifier())
            .sslSocketFactory(getIgnoreSSLSocketFactory());
}

public static void ignoreSSLForHttpsURLConnection() {
    HttpsURLConnection.setDefaultHostnameVerifier(getIgnoreHostnameVerifier());
    HttpsURLConnection.setDefaultSSLSocketFactory(getIgnoreSSLSocketFactory());
}

/**
 * 獲取忽略證書的HostnameVerifier
 * 與{@link #getIgnoreSSLSocketFactory()}同時配置使用
 */
private static HostnameVerifier getIgnoreHostnameVerifier() {
    return new HostnameVerifier() {
        @Override
        public boolean verify(String s, SSLSession sslSession) {
            return true;
        }
    };
}

/**
 * 獲取忽略證書的SSLSocketFactory
 * 與{@link #getIgnoreHostnameVerifier()}同時配置使用
 */
private static SSLSocketFactory getIgnoreSSLSocketFactory() {
    try {
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, getTrustManager(), new SecureRandom());
        return sslContext.getSocketFactory();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private static TrustManager[] getTrustManager() {
    return new TrustManager[]{
        new X509TrustManager() {
            @Override
            public void checkClientTrusted(X509Certificate[] chain, String authType) {
            }

            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) {
            }

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

HTTPS請求在Android4.4及以下無法訪問

服務器已正確配置SSL證書,且已打開TLS1.1和TLS1.2,但是在Android4.4及以下無法訪問網(wǎng)絡。是因為在Android4.4及以下版本默認不支持TLS1.2,需要開啟對TLS1.2的支持。代碼如下:

public static void enableTls12ForHttpsURLConnection() {
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        SSLSocketFactory ssl = getEnableTls12SSLSocketFactory();
        if (ssl != null) {
            HttpsURLConnection.setDefaultSSLSocketFactory(ssl);
        }
    }
}

public static void enableTls12ForOkHttp(OkHttpClient.Builder builder) {
    if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
        SSLSocketFactory ssl = HttpsCompat.getEnableTls12SSLSocketFactory();
        if (ssl != null) {
            builder.sslSocketFactory(ssl);
        }
    }
}

public static SSLSocketFactory getEnableTls12SSLSocketFactory() {
    try {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        sslContext.init(null, null, null);
        return new Tls12SocketFactory(sslContext.getSocketFactory());
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

private static class Tls12SocketFactory extends SSLSocketFactory {
    private static final String[] TLS_SUPPORT_VERSION = {"TLSv1.1", "TLSv1.2"};

    private final SSLSocketFactory delegate;

    private Tls12SocketFactory(SSLSocketFactory base) {
        this.delegate = base;
    }

    @Override
    public String[] getDefaultCipherSuites() {
        return delegate.getDefaultCipherSuites();
    }

    @Override
    public String[] getSupportedCipherSuites() {
        return delegate.getSupportedCipherSuites();
    }

    @Override
    public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
        return patch(delegate.createSocket(s, host, port, autoClose));
    }

    @Override
    public Socket createSocket(String host, int port) throws IOException, UnknownHostException {
        return patch(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException, UnknownHostException {
        return patch(delegate.createSocket(host, port, localHost, localPort));
    }

    @Override
    public Socket createSocket(InetAddress host, int port) throws IOException {
        return patch(delegate.createSocket(host, port));
    }

    @Override
    public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
        return patch(delegate.createSocket(address, port, localAddress, localPort));
    }

    private Socket patch(Socket s) {
        if (s instanceof SSLSocket) {
            ((SSLSocket) s).setEnabledProtocols(TLS_SUPPORT_VERSION);
        }
        return s;
    }
}

Glide在Android4.4及以下圖片加載失敗

原因同上,需要自定義Glide的AppGlideModule,傳入支持TLS1.2的OkHttpClient。

@GlideModule
public class CustomAppGlideModule extends AppGlideModule {

    @Override
    public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
        registry.replace(GlideUrl.class, InputStream.class, new OkHttpUrlLoader.Factory(getOkHttpClient()));
    }

    @Override
    public boolean isManifestParsingEnabled() {
        return false;
    }

    private static OkHttpClient getOkHttpClient() {
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        HttpsCompat.enableTls12ForOkHttp(builder);
        return builder.build();
    }
}

文件下載之RxDownload

使用方法

初始化

初始化操作可在Application中也可在應用啟動頁中進行

RxHttp.init(this);
// 可選,未配置設置將自動采用DefaultDownloadSetting
RxHttp.initDownload(new DefaultDownloadSetting() {
            @Override
            public long getTimeout() {
                return 60000;
            }
        }); 

調用

RxDownload mRxDownload = RxDownload.create(et_url.getText().toString())
        .setDownloadListener(new RxDownload.DownloadListener() {
            @Override
            public void onStarting(DownloadInfo info) {
                tv_start.setText("正在開始...");
            }

            @Override
            public void onDownloading(DownloadInfo info) {
                tv_start.setText("正在下載");
            }

            @Override
            public void onError(DownloadInfo info, Throwable e) {
                tv_start.setText("下載失敗");
            }

            @Override
            public void onStopped(DownloadInfo info) {
                tv_start.setText("已停止");
            }

            @Override
            public void onCanceled(DownloadInfo info) {
                tv_start.setText("已取消");
                pb_1.setProgress(0);
            }

            @Override
            public void onCompletion(DownloadInfo info) {
                tv_start.setText("下載成功");
            }
        })
        .setProgressListener(new RxDownload.ProgressListener() {
            @Override
            public void onProgress(float progress, long downloadLength, long contentLength) {
                pb_1.setProgress((int) (progress * 10000));
            }
        })
        .setSpeedListener(new RxDownload.SpeedListener() {
            @Override
            public void onSpeedChange(float bytePerSecond, String speedFormat) {
                tv_start.setText("正在下載(" + speedFormat + ")");
            }
        });

tv_start.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mRxDownload.start();
    }
});

tv_stop.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mRxDownload.stop();
    }
});

tv_cancel.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        mRxDownload.cancel();
    }
});

常用類說明

RxHttp

用于初始化和設置

DownloadSetting/DefaultDownloadSetting

RxDownload的設置

  • String getBaseUrl()

    指定默認BaseUrl,傳入一個合法的就可以了

  • long getTimeout()

    指定超時時間,建議長一點,如60秒

  • long getConnectTimeout()

    設置0則取getTimeout(),單位毫秒

  • long getReadTimeout()

    設置0則取getTimeout(),單位毫秒

  • long getWriteTimeout()

    設置0則取getTimeout(),單位毫秒

  • String getSaveDirPath()

    指定默認的下載文件夾路徑

  • DownloadInfo.Mode getDefaultDownloadMode()

    獲取保存路徑的文件已存在但未保存下載進度時的默認模式

DownloadInfo

用于保存下載信息,如需斷點續(xù)傳,需要自己保存以下幾個必傳項

  • String url

    下載文件的鏈接(必傳項)

  • String saveDirPath

    自定義下載文件的保存目錄(斷點續(xù)傳時必傳項)

  • String saveFileName

    自定義下載文件的保存文件名,需帶后綴名(斷點續(xù)傳時必傳項)

  • long downloadLength

    已下載文件的長度(斷點續(xù)傳時必傳項)

  • long contentLength

    下載文件的總長度

  • State state

    當前下載狀態(tài)

    • STARTING

      正在開始

    • DOWNLOADING

      正在下載

    • STOPPED

      未開始/已停止

    • ERROR

      下載出錯

    • COMPLETION

      下載完成

  • Mode mode

    獲取保存路徑的文件已存在但未保存下載進度時的模式

    • APPEND

      追加

    • REPLACE

      替換

    • RENAME

      重命名

  • create(String)

    創(chuàng)建一個下載對象,參數(shù)為url

  • create(String, String, String)

    創(chuàng)建一個下載對象,參數(shù)為url/保存目錄/文件名

  • create(String, String, String, long, long)

    創(chuàng)建一個下載對象,參數(shù)為url/保存目錄/文件名/已下載長度/總長度

RxDownload

  • create(DownloadInfo)

    用于新建一個下載任務

  • setDownloadListener(DownloadListener)

    設置下載狀態(tài)監(jiān)聽

    • onStarting()

      正在開始,正在連接服務器

    • onDownloading()

      正在下載

    • onStopped()

      已停止,不會刪除已下載部分,支持斷點續(xù)傳

    • onCanceled()

      已取消,會刪除已下載的部分文件,再次開始會重新下載

    • onCompletion(DownloadInfo)

      下載完成

    • onError(Throwable)

      下載出錯

  • setProgressListener(ProgressListener)

    • onProgress(float)

      下載進度回調(0~1)

  • setSpeedListener(SpeedListener)

    • onSpeedChange(float, String)

      下載速度回調,兩個值分別為每秒下載比特數(shù)和格式化后速度(如:1.2KB/s,3.24MB/s)

  • start()

    開始下載/繼續(xù)下載

  • stop()

    停止下載,不會刪除已下載部分,支持斷點續(xù)傳

  • cancel()

    取消下載,會刪除已下載的部分文件,再次開始會重新下載

UnitFormatUtils

單位格式化工具

  • calculateSpeed(long, float)

    計算速度

  • formatSpeedPerSecond(float)

    格式化速度(如:1.12MB/s,628KB/s)

  • formatSpeed(float,TimeUnit)

    格式化速度(如:1.12MB/s,628KB/s)

  • formatBytesLength(float)

    格式化比特值(如:12.1KB,,187.24MB,154GB)

  • formatTimeUnit(TimeUnit)

    格式化時間單位(如:秒為s,毫秒為ms)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容