在日常开发中经常会涉及到网络请求,随着业务的复杂多变,对于请求库的功能及职责也要求越来越高,一个不错的请求库能使日常开发事半功倍。使用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 远程响应结果
* @param 转换后的结果
*/
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。
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。
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:验签失败