设计一个响应式网络请求

前言

在日常开发中经常会涉及到网络请求,随着业务的复杂多变,对于请求库的功能及职责也要求越来越高,一个不错的请求库能使日常开发事半功倍。使用Retrofit,OkHttp,RxJava,Autodispose封装一个带有生命周期的请求库。

流程图

在这里插入图片描述

添加依赖

dependencies {
    // Retrofit
    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'
    implementation 'com.squareup.retrofit2:converter-scalars:2.9.0'
    // Gson
    implementation 'com.google.code.gson:gson:2.8.9'
    // RxJava
    implementation 'io.reactivex.rxjava3:rxjava:3.1.2'
    implementation 'io.reactivex.rxjava3:rxandroid:3.0.0'
    // AutoDispose
    implementation 'com.uber.autodispose2:autodispose-android:2.1.1'
    implementation 'com.uber.autodispose2:autodispose-androidx-lifecycle:2.1.1'
}

请求结果包装

一个完整的请求结果包含请求状态,远程结果,异常等。

/**
 * 请求结果包装类,含请求状态,数据,异常
 */
public class RequestBuilder<ResultType> {

    private final ResultType result;
    private final RequestStatus status;
    private final Throwable throwable;

    @NonNull
    public static <ResultType> RequestBuilder<ResultType> loading() {
        return new RequestBuilder<>(RequestStatus.LOADING, null);
    }

    @NonNull
    public static <ResultType> RequestBuilder<ResultType> success(ResultType result) {
        return new RequestBuilder<>(RequestStatus.SUCCEED, result);
    }

    @NonNull
    public static <ResultType> RequestBuilder<ResultType> failed(Throwable throwable) {
        return new RequestBuilder<>(RequestStatus.FAILED, throwable);
    }

    @NonNull
    public static <ResultType> RequestBuilder<ResultType> completed() {
        return new RequestBuilder<>(RequestStatus.COMPLETED);
    }

    public RequestBuilder(@NonNull RequestStatus status) {
        this(status, null, null);
    }

    public RequestBuilder(@NonNull RequestStatus status, ResultType result) {
        this(status, result, null);
    }

    public RequestBuilder(@NonNull RequestStatus status, @Nullable Throwable throwable) {
        this(status, null, throwable);
    }

    public RequestBuilder(@NonNull RequestStatus status, @Nullable ResultType data, @Nullable Throwable throwable) {
        this.result = data;
        this.status = status;
        this.throwable = throwable;
    }

    public ResultType getResult() {
        return result;
    }

    public RequestStatus getStatus() {
        return status;
    }

    public ResponseException getThrowable() {
        return (ResponseException) throwable;
    }
}

请求状态

public enum RequestStatus {

    LOADING,

    SUCCEED,

    FAILED,

    COMPLETED
}

请求结果被观察者

在RequestObservable中进行请求,数据解析与转换,状态分发,订阅观察者。

/**
 * 观察请求结果
 * @param <ResponseType> 远程响应结果
 * @param <ResultType>   转换后的结果
 */
public abstract class RequestObservable<ResponseType, ResultType> extends Observable<RequestBuilder<ResultType>> {

    private Observer<? super RequestBuilder<ResultType>> observer;
    private final AndroidLifecycleScopeProvider lifecycleScopeProvider;

    public RequestObservable(@NonNull Lifecycle lifecycle) {
        this(lifecycle, Lifecycle.Event.ON_DESTROY);
    }

    public RequestObservable(@NonNull LifecycleOwner lifecycleOwner) {
        this(lifecycleOwner, Lifecycle.Event.ON_DESTROY);
    }

    public RequestObservable(Lifecycle lifecycle, Lifecycle.Event event) {
        this(AndroidLifecycleScopeProvider.from(lifecycle, event));
    }

    public RequestObservable(LifecycleOwner lifecycleOwner, Lifecycle.Event event) {
        this(AndroidLifecycleScopeProvider.from(lifecycleOwner, event));
    }

    public RequestObservable(AndroidLifecycleScopeProvider lifecycleScopeProvider) {
        this.lifecycleScopeProvider = lifecycleScopeProvider;
    }

    @Override
    protected void subscribeActual(@NonNull Observer<? super RequestBuilder<ResultType>> observer) {
        this.observer = observer;
        this.observer.onNext(RequestBuilder.loading());
        dispatcherStrategy();
    }

    /**
     * 请求动作分发
     * 在io线程中处理请求与转换,在主线程中进行结果回调。
     */
    private void dispatcherStrategy() {
        request().distinct()
                .subscribeOn(Schedulers.io())
                .observeOn(Schedulers.io())
                .map(this::transform) // 在io线程中对数据进行解析转换
                .observeOn(AndroidSchedulers.mainThread()) // 切换到UI线程,通过回调更新UI层业务
                .to(AutoDispose.autoDisposable(lifecycleScopeProvider))
                .subscribe(this::emitValue, this::emitError, this::emitCompleted);
    }

    /**
     * 远程数据源
     */
    @WorkerThread
    protected abstract Observable<ResponseType> request();

    /**
     * 结果转换 ResponseType ==> ResultType
     * 将远程原始数据转换为结果数据,如远程返回ResponseType结果,通过该函数将ResponseType结果转换为结果ResultType
     */
    @WorkerThread
    protected abstract ResultType transform(ResponseType result) throws ResponseException;

    /**
     * 发送一个 {@link RequestStatus#SUCCEED} 状态,回调请求结果。
     */
    @MainThread
    private void emitValue(ResultType value) {
        if (this.observer != null) {
            this.observer.onNext(RequestBuilder.success(value));
        }
    }

    /**
     * 发送一个 {@link RequestStatus#FAILED} 状态,回调异常结果。
     */
    @MainThread
    private void emitError(Throwable throwable) {
        if (this.observer != null) {
            this.observer.onNext(RequestBuilder.failed(throwable));
        }
    }

    /**
     * 发送一个 {@link RequestStatus#COMPLETED} 状态,请求成功或失败时都会走这个回调。
     */
    @MainThread
    private void emitCompleted() {
        if (this.observer != null) {
            this.observer.onNext(RequestBuilder.completed());
        }
    }

    /**
     * 订阅请求结果
     */
    public void execute(RequestSubscriber<ResultType> request) {
        this.subscribe(new RequestObserver<>(request));
    }
}

请求结果观察者

在RequestObserver中对请求过程事件分发处理。

public class RequestObserver<ResultType> implements Observer<RequestBuilder<ResultType>> {

    private final RequestSubscriber<ResultType> subscriber;

    public RequestObserver(@NonNull RequestSubscriber<ResultType> subscriber) {
        this.subscriber = subscriber;
    }

    @Override
    public void onSubscribe(@NonNull Disposable disposable) {

    }

    @Override
    public void onNext(@NonNull RequestBuilder<ResultType> result) {
        try {
            switch (result.getStatus()) {
                case LOADING:
                    this.subscriber.onLoading();
                    break;
                case SUCCEED:
                    this.subscriber.onSuccess(result.getResult());
                    break;
                case FAILED:
                    this.subscriber.onFailed(result.getThrowable());
                    break;
                case COMPLETED:
                    this.subscriber.onCompleted();
                    break;
            }
        } catch (Throwable throwable) {
            onError(throwable);
        }
    }

    @Override
    public void onError(@NonNull Throwable e) {
        this.subscriber.onFailed(NetworkException.UNKNOWN_NETWORK_EXCEPTION);
    }

    @Override
    public void onComplete() {

    }
}

回调结果

定义一个结果回调接口,将请求状态及请求结果回调给UI层,进行对应的业务处理。

public interface RequestSubscriber<ResultType> {

    /**
     * 开始请求
     */
    default void onLoading() { }

    /**
     * 请求成功
     */
    default void onSuccess(ResultType result) { }

    /**
     * 请求异常
     */
    default void onFailed(ResponseException throwable) { }

    /**
     * 网络加载完成,不管成功失败都会进入
     */
    default void onCompleted() { }
}

解析工厂

在日常开发中,由于后台语言特性的问题,返回的结果多变,直接使用Retrofit提供的解析工厂时遇到类型不匹配的结果就会出错,就需要单独对每个API返回的数据进行处理,如下:

示例1{
    "code": 200,
    "msg": "请求成功"
    "data": {
        "token":"592faa1b-0e1f-4b84-9ab4-786efcb774bb"
    }
}

示例2,data返回null{
    "code": 1,
    "msg": "请求失败"
    "data": null
}

示例3,约定好的data值为对象,结果返回[]{
    "code": 1,
    "msg": "请求失败"
    "data": [],
}

定义解析工厂,直接返回字符串,同时也向外提供了对应的callback接口,可针对一些公共返回值做处理。

public class JsonConverterFactory<T> extends Converter.Factory {

    private final Gson gson;
    private final OnCallback<T> callback;

    public JsonConverterFactory(@NonNull Gson gson) {
        this(gson, null);
    }

    public JsonConverterFactory(OnCallback<T> callback) {
        this(JSONHelper.jsonConverter(), callback);
    }

    public JsonConverterFactory(@NonNull Gson gson, @Nullable OnCallback<T> callback) {
        this.gson = gson;
        this.callback = callback;
    }

    @Override
    public Converter<?, RequestBody> requestBodyConverter(@NonNull Type type, @NonNull Annotation[] parameterAnnotations, @NonNull Annotation[] methodAnnotations, @NonNull Retrofit retrofit) {
        return new JsonRequestBodyConverter<>(gson, gson.getAdapter(TypeToken.get(type)));
    }

    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(@NonNull Type type, @NonNull Annotation[] annotations, @NonNull Retrofit retrofit) {
        return value -> {
            if (callback != null) {
                return callback.converter(value.string());
            } else {
                return value;
            }
        };
    }

    public interface OnCallback<T> {
        T converter(String result) throws ResponseException;
    }
}

转换响应体

public class JsonRequestBodyConverter<T> implements Converter<T, RequestBody> {

    private static final Charset UTF_8 = StandardCharsets.UTF_8;
    private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");

    private final Gson gson;
    private final TypeAdapter<T> adapter;

    public JsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
        this.gson = gson;
        this.adapter = adapter;
    }

    @Override
    public RequestBody convert(@NonNull T value) throws IOException {
        Buffer buffer = new Buffer();
        Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
        JsonWriter jsonWriter = gson.newJsonWriter(writer);
        adapter.write(jsonWriter, value);
        jsonWriter.close();
        return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
    }
}

请求拦截

对于大多数API,都需要传递一些公共参数,如果每次调用一个API都配置一些参数,那样就显得不太友好,代码也相对冗余,针对公共参数建议使用拦截器,在拦截器中添加请求头信息,如:User-Agent, Token, version,sign等。

public class RequestInterceptor implements Interceptor {

    private final OnCallback callback;

    public RequestInterceptor(OnCallback callback) {
        this.callback = callback;
    }

    @NonNull
    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        return chain.proceed(callback.onCallback(chain.request()).build());
    }

    public interface OnCallback {
        /**
         * 向外部传递一个 {@link Request}
         * @return 返回一个 {@link okhttp3.Request.Builder}
         */
        Request.Builder onCallback(Request request);
    }
}

对于一些服务器异常,网络异常,解析异常(API返回非JSON格式结果)等,统一在拦截其中处理。

public class RequestErrorInterceptor implements Interceptor {

    @NonNull
    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        try {
            if (NetworkHelper.isConnected(ApplicationProvider.appContext)) {
                return chain.proceed(chain.request());
            } else {
                throw NetworkException.NETWORK_CONNECTED_EXCEPTION;
            }
        } catch (Exception e) {
            throw NetworkException.handleException(e);
        }
    }
}

异常处理

定义请求响应异常包装类,方便传入code与msg。

public class ResponseException extends IOException {

    private final int code;
    private final String msg;

    public ResponseException(int code, @Nullable String msg) {
        super(msg);
        this.code = code;
        this.msg = msg;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    @NonNull
    public static ResponseException builder(int code, @Nullable String msg) {
        return new ResponseException(code, msg);
    }
}

处理常见异常

/**
 * 定义常见的请求异常并根据响应结果处理对应异常。
 */
public class NetworkException {

    // 无有效网络链接
    public static ResponseException NETWORK_CONNECTED_EXCEPTION;

    // 请求超时
    public static ResponseException REQUEST_TIMEOUT_EXCEPTION;

    // 404,500
    public static ResponseException HTTP_REQUEST_EXCEPTION;

    // 数据解析错误
    public static ResponseException DATA_PARSE_EXCEPTION;

    // 未知网络错误定义为网络异常
    public static ResponseException UNKNOWN_NETWORK_EXCEPTION;

    static {
        NETWORK_CONNECTED_EXCEPTION = new ResponseException(-101, "网络链接异常");
        REQUEST_TIMEOUT_EXCEPTION = new ResponseException(-102, "请求超时");
        HTTP_REQUEST_EXCEPTION = new ResponseException(-103, "网络异常");
        DATA_PARSE_EXCEPTION = new ResponseException(-104, "解析异常");
        UNKNOWN_NETWORK_EXCEPTION = new ResponseException(-105, "网络异常");
    }

    public static ResponseException handleException(Exception throwable) {
        if (throwable instanceof HttpException) {
            HttpException exception = (HttpException) throwable;
            return ResponseException.builder(exception.code(), exception.message());
        } else if (throwable instanceof JsonParseException
                || throwable instanceof JSONException
                || throwable instanceof ParseException
                || throwable instanceof MalformedJsonException) {
            return DATA_PARSE_EXCEPTION;
        } else if (throwable instanceof InterruptedIOException) {
            return REQUEST_TIMEOUT_EXCEPTION;
        } else {
            return UNKNOWN_NETWORK_EXCEPTION;
        }
    }
}

定义请求结果数据模型

根据API返回结果json, 定义数据模型。

public class ResultBody<T> {

    @SerializedName("code")
    private final int code;
    @SerializedName("msg")
    private final String msg;
    @SerializedName("time")
    private final String time;
    @SerializedName("data")
    private final String data;

    /**
     * 扩展参数,返回 {@link #data} 字段对应的具体结果对象。
     * 使用 {@link Expose} 注解,标记解析时忽略该字段。
     */
    @Expose(serialize = false, deserialize = false)
    private T result;

    @NonNull
    public static ResultBody<String> builder(int code, String msg, String time, @Nullable String data) {
        return new ResultBody<>(code, msg, time, data);
    }

    public ResultBody(int code, String msg) {
        this(code, msg, "", "");
    }

    public ResultBody(int code, String msg, String time, String data) {
        this.code = code;
        this.msg = msg;
        this.time = time;
        this.data = data;
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }

    public String getTime() {
        return time;
    }

    public String getData() {
        return data;
    }

    public T getResult() {
        return result;
    }

    public ResultBody<T> setResult(T result) {
        this.result = result;
        return this;
    }

    @NonNull
    @Override
    public String toString() {
        return JSONHelper.toJson(this);
    }
}

客户端请求类

在这里为每次请求添加请求头,对请求参数包装再处理,拦截异常状态,具体设置请参考对应逻辑。

public class ClientRequest {

    private static final long REQUEST_TIMEOUT = 30L;
    private static final String REQUEST_GET = "GET";
    private static final String REQUEST_POST = "POST";
    private static final String REQUEST_PARAMS_SIGN = "sign";
    private static final String REQUEST_PARAMS_TOKEN = "token";
    private static final String REQUEST_PARAMS_APP_KEY = "appkey";
    private static final String REQUEST_PARAMS_TIMESTAMP = "timestamp";

    private volatile static ClientRequest client = null;

    private ClientRequest() {
    }

    public static ClientRequest getClient() {
        if (client == null) {
            synchronized (ClientRequest.class) {
                if (client == null) {
                    client = new ClientRequest();
                }
            }
        }
        return client;
    }

    /**
     * 创建Api接口,本地存在Token时,默认携带。
     */
    public <T> T create(Class<T> service) {
        return create(true, service);
    }

    /**
     * 创建Api接口
     * @param isNeedToken 是否需要携带Token,本地存在Token时,默认携带。
     */
    public <T> T create(boolean isNeedToken, Class<T> service) {
        return retrofitCreator(isNeedToken).create(service);
    }

    @NonNull
    private Retrofit retrofitCreator(boolean isNeedToken) {
        return new Retrofit.Builder()
                .baseUrl(Constants.BASE_URI)
                .client(httpClientCreator(isNeedToken))
                .addConverterFactory(ScalarsConverterFactory.create())
                .addConverterFactory(new JsonConverterFactory<>(this::resultConverter))
                .addCallAdapterFactory(RxJava3CallAdapterFactory.createWithScheduler(Schedulers.io()));
                .build();
    }

    @NonNull
    private OkHttpClient httpClientCreator(boolean isNeedToken) {
        HttpLoggingInterceptor.Level loggingLevel = HttpLoggingInterceptor.Level.BODY;
        return new OkHttpClient().newBuilder()
                .readTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
                .writeTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
                .connectTimeout(REQUEST_TIMEOUT, TimeUnit.SECONDS)
                .addInterceptor(new RequestErrorInterceptor())                        // 请求错误拦截
                .addInterceptor(new HttpLoggingInterceptor().setLevel(loggingLevel)); // 打印请求日志
                .addInterceptor(new RequestInterceptor(request -> getRequestBuilder(request, isNeedToken)))
                .build();
    }

    /**
     * 结果转换
     * 针对API返回的结果做解析并处理异常结果。
     * 因PHP语言特性原因,API在返回Json时偶尔会出现返回对应key的value为null,[],{}值。
     * 这里手动对API返回结果进行解析,对code非 {@link ResultCode#SUCCESS} 以外的状态统一处理。
     */
    @NonNull
    private ResultBody<String> resultConverter(String result) throws ResponseException {
        try {
            JSONObject jsonObject = new JSONObject(result);
            int code = jsonObject.optInt("code");
            String msg = jsonObject.optString("msg");
            String time = jsonObject.optString("time");
            if (code == ResultCode.SUCCESS) { // code值对应的状态非SUCCESS状态,则抛出异常,走失败回调。
                String data = jsonObject.optString("data");
                return ResultBody.builder(code, msg, time, data);
            } else {
                if (code == ResultCode.TOKEN_EXCEED) { // Token过期,清除本地存储的Token
                    AccountHelper.getInstance().clear();
                }
                throw ResponseException.builder(code, msg);
            }
        } catch (JSONException e) {
            throw NetworkException.DATA_PARSE_EXCEPTION;
        }
    }

    /**
     * 设置API请求参数
     * 说明:先将 API
     *
     * @see com.module.platform.data.api.service
     * 目录下Service中相关API的参数遍历出来与公共参数进行加密,
     * 在设置到 {@link Request} 中。
     */
    @NonNull
    private Request.Builder getRequestBuilder(@NonNull Request request, boolean isNeedToken) {
        Request.Builder builder = request.newBuilder();
        if (request.method().equals(REQUEST_POST)) {       // Post
            builder.post(getRequestBody(request.body(), isNeedToken));
        } else if (request.method().equals(REQUEST_GET)) { // Get
            builder.url(getHttpUrl(request.url().newBuilder().build(), isNeedToken));
        }
        return builder;
    }

    /**
     * Post请求
     *
     * @return {@link RequestBody},对原请求体中的参数做修改,添加公共参数与私有参数。
     */
    @NonNull
    private RequestBody getRequestBody(@Nullable RequestBody requestBody, boolean isNeedToken) {
        FormBody.Builder builder = new FormBody.Builder(StandardCharsets.UTF_8);
        if (requestBody == null) return builder.build(); // 请求参数为空时直接返回。
        Map<String, String> params = new HashMap<>();    // 收集参数生成sign值
        // 设置API私有参数
        if (requestBody instanceof FormBody) {
            FormBody formBody = (FormBody) requestBody;
            for (int i = 0; i < formBody.size(); i++) {
                String key = formBody.name(i);
                String value = formBody.value(i);
                builder.add(key, value);
                params.put(key, value);
            }
        }
        // 设置API公共参数
        params.put(REQUEST_PARAMS_APP_KEY, Constants.APP_KEY);
        builder.add(REQUEST_PARAMS_APP_KEY, Constants.APP_KEY);
        String timestamp = String.valueOf(System.currentTimeMillis());
        params.put(REQUEST_PARAMS_TIMESTAMP, timestamp);
        builder.add(REQUEST_PARAMS_TIMESTAMP, timestamp);              // 时间戳
        // 设置Token
        String token = AccountHelper.getInstance().getToken();
        if (isNeedToken && !token.isEmpty()) {
            params.put(REQUEST_PARAMS_TOKEN, token);
            builder.add(REQUEST_PARAMS_TOKEN, token);                  // token
        }
        builder.add(REQUEST_PARAMS_SIGN, getSign(params));             // Sign
        return builder.build();
    }

    /**
     * Get请求
     *
     * @return {@link HttpUrl},对原请求体中的参数做修改,添加公共参数与私有参数。
     */
    @NonNull
    private HttpUrl getHttpUrl(@Nullable HttpUrl url, boolean isNeedToken) {
        HttpUrl.Builder builder = new HttpUrl.Builder();
        if (url == null) return builder.build(); // 请求参数为空时直接返回。
        builder.host(url.host());     // 设置Host
        builder.port(url.port());     // 设置API端口号
        builder.scheme(url.scheme()); // 设置scheme(Http/Https)
        // 设置Api接口名
        for (String pathSegment : url.pathSegments()) {
            builder.addPathSegment(pathSegment);
        }
        // 收集参数生成sign值
        Map<String, String> params = new HashMap<>();
        // 设置API私有参数
        for (int i = 0; i < url.querySize(); i++) {
            String key = url.queryParameterName(i);
            String value = url.queryParameterValue(i);
            builder.addQueryParameter(key, value);
            params.put(key, value);
        }
        // 设置API公共参数
        params.put(REQUEST_PARAMS_APP_KEY, Constants.APP_KEY);
        builder.addQueryParameter(REQUEST_PARAMS_APP_KEY, Constants.APP_KEY); // AppKey
        String timestamp = String.valueOf(System.currentTimeMillis());
        params.put(REQUEST_PARAMS_TIMESTAMP, timestamp);
        builder.addQueryParameter(REQUEST_PARAMS_TIMESTAMP, timestamp);       // 时间戳
        String token = AccountHelper.getInstance().getToken();
        if (isNeedToken && !token.isEmpty()) {
            params.put(REQUEST_PARAMS_TOKEN, token);
            builder.addQueryParameter(REQUEST_PARAMS_TOKEN, token);           // token
        }
        builder.addQueryParameter(REQUEST_PARAMS_SIGN, getSign(params));      // Sign
        return builder.build();
    }

    /**
     * 根据API请求参数返回sign串
     */
    @NonNull
    private String getSign(@NonNull Map<String, String> params) {
        // 根据参数名称的ASCII码表的顺序排序
        List<Map.Entry<String, String>> mappingList = new ArrayList<>(params.entrySet());
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            Collections.sort(mappingList, Map.Entry.comparingByKey());
        } else {
            Collections.sort(mappingList, (value1, value2) -> value2.getKey().compareTo(value1.getKey()));
        }
        StringBuilder builder = new StringBuilder();
        builder.append(Constants.APP_SECRET);
        for (Map.Entry<String, String> param : mappingList) {
            builder.append(param.getValue());
        }
        builder.append(Constants.APP_SECRET);
        // 生成最终的签名字符串
        return toMD5(builder.toString());
    }

    /**
     * MD5
     */
    @NonNull
    private String toMD5(@NonNull String value) {
        byte[] hash;
        try {
            hash = MessageDigest.getInstance("MD5").digest(value.getBytes(StandardCharsets.UTF_8));
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            return "";
        }
        StringBuilder hex = new StringBuilder(hash.length * 2);
        for (byte b : hash) {
            if ((b & 0xFF) < 0x10) hex.append("0");
            hex.append(Integer.toHexString(b & 0xFF));
        }
        return hex.toString();
    }
}

使用

单个API请求

创建登录API。

public interface AuthService {

    /**
     * 登录
     */
    @FormUrlEncoded
    @POST("user/login")
    Observable<ResultBody<Account>> login(@Field("username") String phone, @Field("password") String password);
}

在数据仓库中创建API,处理数据解析与转换。

public class AuthRepository extends BaseRepository {

    public AuthRepository(@NonNull LifecycleOwner lifecycleOwner) {
        super(lifecycleOwner);
    }

    /**
     * 登录
     */
    public RequestObservable<ResultBody<Account>, Account> login(String phone, String password) {
        return new RequestObservable<ResultBody<Account>, Account>(getLifecycleOwner()) {

            @Override
            public Observable<ResultBody<Account>> request() { // 登录API接口
                return ClientRequest.getClient().create(false, AuthService.class).login(phone, password);
            }

            @Override
            protected Account transform(ResultBody<Account> result) throws ResponseException { // 对API返回结果进行转换
                if (JSONHelper.isJsonObject(result.getData())) {
                    String json = JSONHelper.getValue(result.getData(), "userInfo");
                    Account account = JSONHelper.jsonConverter().fromJson(json, Account.class);
                    AccountHelper.getInstance().save(account); // 存储用户信息
                    return account;
                } else {
                    throw NetworkException.DATA_PARSE_EXCEPTION;
                }
            }
        };
    }
}

在ViewModel中调用登录API。

public class RequestViewModel extends BaseViewModel {

    private final AuthRepository repository;

    public RequestViewModel(@NonNull LifecycleOwner lifecycleOwner) {
        super(lifecycleOwner);
        this.repository = new AuthRepository(lifecycleOwner);
    }

    public void login(String phone, String password) {
        repository.login(phone, password).execute(new RequestSubscriber<Account>() {
            @Override
            public void onLoading() {
                Log.e("请求状态", "loading...");
            }

            @Override
            public void onSuccess(Account result) {
                Log.e("请求状态", "success");
                Log.e("结果", "token:" + result.getToken());
            }

            @Override
            public void onFailed(ResponseException throwable) {
                Log.e("请求状态", "failed");
                Log.e("失败", "code:" + throwable.getCode() + "\tmsg:" + throwable.getMsg());
            }

            @Override
            public void onCompleted() {
                Log.e("请求状态", "completed");
            }
        });
    }
}
多个API异步请求同步返回结果

创建账户中心API。

public interface AccountService {

    /**
     * 获取个人信息
     */
    @GET("API接口")
    Observable<ResultBody<Account>> getAccountInfo();

    /**
     * 获取  我的页面  卡券 钱包  礼包数量
     */
    @GET("API接口")
    Observable<ResultBody<AccountAmount>> getAccountAmount();

    /**
     * 检测是否有未读消息
     */
    @GET("API接口")
    Observable<ResultBody<Integer>> checkHasUnreadMsg();
}

在数据仓库中创建API,处理数据解析与转换。

public class AccountRepository extends BaseRepository {

    public AccountRepository(@NonNull LifecycleOwner lifecycleOwner) {
        super(lifecycleOwner);
    }

    public RequestObservable<AccountBody, AccountBody> getAccount() {
        return new RequestObservable<AccountBody, AccountBody>(getLifecycleOwner()) {

            @Override
            protected Observable<AccountBody> request() {
                Observable<ResultBody<Account>> account = ClientRequest.getClient().create(AccountService.class).getAccountInfo();
                Observable<ResultBody<AccountAmount>> accountAmount = ClientRequest.getClient().create(AccountService.class).getAccountAmount();
                return Observable.zip(account, accountAmount, AccountBody::new);
            }

            @Override
            protected AccountBody transform(AccountBody result) {
                ResultBody<Account> account = analysisAccount(result.getAccount());
                ResultBody<AccountAmount> accountAmount = analysisAccountAmount(result.getAccountAmount());
                return new AccountBody(account, accountAmount);
            }
        };
    }

    /**
     * 解析账户数据
     */
    @NonNull
    public ResultBody<Account> analysisAccount(@NonNull ResultBody<Account> resultBody) {
        if (JSONHelper.isJsonObject(resultBody.getData())) {
            String json = JSONHelper.getValue(resultBody.getData(), "userInfo");
            ResultBody<Account> result = new ResultBody<>(resultBody.getCode(), resultBody.getMsg(), resultBody.getTime(), resultBody.getData());
            result.setResult(JSONHelper.jsonConverter().fromJson(json, Account.class));
            return result;
        } else {
            return resultBody;
        }
    }

    /**
     * 解析账户中心卡券,钱包,礼包数据
     */
    public ResultBody<AccountAmount> analysisAccountAmount(@NonNull ResultBody<AccountAmount> resultBody) {
        if (JSONHelper.isJsonObject(resultBody.getData())) {
            ResultBody<AccountAmount> result = new ResultBody<>(resultBody.getCode(), resultBody.getMsg(), resultBody.getTime(), resultBody.getData());
            result.setResult(JSONHelper.jsonConverter().fromJson(resultBody.getData(), AccountAmount.class));
            return result;
        } else {
            return resultBody;
        }
    }
}

在ViewModel中调用账户中心API。

public class RequestViewModel extends BaseViewModel {

    private final AccountRepository repository;

    public RequestViewModel(@NonNull LifecycleOwner lifecycleOwner) {
        super(lifecycleOwner);
        this.repository = new AccountRepository(lifecycleOwner);
    }

    public void getAccount() {
        repository.getAccount().execute(new RequestSubscriber<AccountBody>() {

            @Override
            public void onLoading() {
                Log.e("请求状态", "loading...");
            }

            @Override
            public void onSuccess(AccountBody result) {
                Log.e("请求状态", "success");
                Log.e("结果", result.toString());
            }

            @Override
            public void onFailed(ResponseException throwable) {
                Log.e("请求状态", "completed");
                Log.e("失败", "code:" + throwable.getCode() + "\tmsg:" + throwable.getMsg());
            }

            @Override
            public void onCompleted() {
                Log.e("请求状态", "completed");
            }
        });
    }
}

对于同步与合并请求,请参考RxJava中的函数 .switchMap

请求日志

请求成功log

E/开始状态: loading...
D/OkHttp: --> POST https://API地址
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 37
D/OkHttp: username=17691129200&password=M000000
D/OkHttp: --> END POST (37-byte body)
D/OkHttp: <-- 200 OK https://API地址 (744ms)
D/OkHttp: Server: nginx
D/OkHttp: Date: Tue, 09 Nov 2021 05:55:40 GMT
D/OkHttp: Content-Type: application/json; charset=utf-8
D/OkHttp: Transfer-Encoding: chunked
D/OkHttp: Connection: keep-alive
D/OkHttp: X-Powered-By: PHP/7.2.34
D/OkHttp: Set-Cookie: ""
D/OkHttp: Expires: Thu, 19 Nov 1981 08:52:00 GMT
D/OkHttp: Cache-Control: no-store, no-cache, must-revalidate
D/OkHttp: Pragma: no-cache
D/OkHttp: X-Frame-Options: SAMEORIGIN
D/OkHttp: X-XSS-Protection: 1; mode=block
D/OkHttp: X-Content-Type-Options: nosniff
D/OkHttp: {"code":200,"msg":"登陆成功","time":1636437339,"data":{"userInfo":{"id":473435,"username":"36WD2qYpET","email":"","mobile":"17691129200","score":0,"token":"7be17f28-8f31-411e-8e43-dc3e8ee7b76f","nickname":"语蓉如南","idcard":"","registertime":1631771027,"realname":"","portrait":"","promote_id":0,"expires_in":2592000,"has_password":0,"is_app_card":1}}}
D/OkHttp: <-- END HTTP (361-byte body)
E/开始状态: success
E/结果: token:7be17f28-8f31-411e-8e43-dc3e8ee7b76f
E/开始状态: completed

请求失败log

E/开始状态: loading...
D/OkHttp: --> POST https://API地址
D/OkHttp: Content-Type: application/x-www-form-urlencoded
D/OkHttp: Content-Length: 37
D/OkHttp: username=17691129200&password=M000000
D/OkHttp: --> END POST (37-byte body)
D/OkHttp: <-- 200 OK https://API地址 (797ms)
D/OkHttp: Server: nginx
D/OkHttp: Date: Tue, 09 Nov 2021 05:59:17 GMT
D/OkHttp: Content-Type: application/json; charset=utf-8
D/OkHttp: Transfer-Encoding: chunked
D/OkHttp: Connection: keep-alive
D/OkHttp: X-Powered-By: PHP/7.2.34
D/OkHttp: X-Frame-Options: SAMEORIGIN
D/OkHttp: X-XSS-Protection: 1; mode=block
D/OkHttp: X-Content-Type-Options: nosniff
D/OkHttp: {"code":0,"msg":"验签失败","time":1636437556,"data":null}
D/OkHttp: <-- END HTTP (61-byte body)
E/开始状态: failed
E/失败: code:0	msg:验签失败
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

蒙同學

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值