RetrofitカスタムGsonConverter処理要求エラー異常処理
26663 ワード
通常、サービス側から入手するJSONデータフォーマットは、次のようになります.
したがって、通常、対応するjsonを解析するためにエンティティクラスを定義します.
codeフィールドはステータスを表します.たとえば、次の値は異なる意味を表す可能性があります. code=1、成功を表し、1に等しくないのはエラー を表す code=-101、tokenの期限切れを示す code=-102は、携帯電話番号が に登録されていることを示しています.など通常のRetrofit+RxJavaロジックに従って処理すると、書かれたコードは次のようになります.
使用時:
もしすべての要求に対してすべてこのようにするならば、それは死んだ人を書くのではありませんか、もしある日これらの値が変わったら、例えば-102から-105になったら、あなたはすべての場所がすべて変えなければならないのではなくて、考えてみると怖いです!
解決策
addConverterFactory(GsonConverterFactory.create()というコードは、サービス側から返されたjsonデータをGsonでエンティティに解析するためのものです.ここから、GsonConverterを自分で定義し、元の機能を拡張することができます
まず、デフォルトのGsonConverterがどのように書かれているかを分析します.3つのクラスから構成されています. GsonConverterFactory//GsonConverterファクトリクラスGsonConverter を作成する GsonResponseBodyConverter//処理ResponseBody GsonRequestBodyConverter//処理RequestBody 名前から各クラスが何をしているのか分かりやすいので、GsonResponseBodyConverterというクラスが重要に違いありません.このクラスを見てみましょう.
あなたは間違っていません.この数行のコードです.このconvert()方法は拡張する場所です.
元のロジックに処理コードを追加するだけ!=1の場合、code!=1、異常を投げ出し、
まず直接コードを入力:
彼の2つのクラスはデフォルトと同じで、3番目のクラスだけを見ています.
ここでは、HttpStatusとApiExceptionの2つのクラスをカスタマイズします.次の2つのクラスがあります.
わかりやすく、重要なコードをいくつか説明します.
ここにはResponseBodyに関する穴があります.もし誰かがこの異常に遭遇したことがあれば、きっと知っています.
ResponseBodyを1回しか読めないのでresponseを呼び出すとbody().string()2回またはresponse.body().charStream()は2回でこの異常が発生するので、string()を呼び出してからcharStream()を呼び出してもよい.
だから通常のやり方は1回読み取った後に保存して、次回はResponseBodyから読み取らないことです.
使用方法
まずBaseSubscriberを構築します
リクエストインタフェース
簡書より:http://www.jianshu.com/p/5b8b1062866b
{
"code":1,
"message":" ",
"detail":{"aa":"123","bb":"123","cc":"123"}
}
したがって、通常、対応するjsonを解析するためにエンティティクラスを定義します.
public class Response {
@SerializedName("code")
private int code;
@SerializedName("message")
private String message;
@SerializedName("detail")
private DetailBean detail;
// getter setter ...
public static class DetailBean {
@SerializedName("aa")
private String aa;
@SerializedName("bb")
private String bb;
@SerializedName("cc")
private String cc;
// getter setter ...
}
}
codeフィールドはステータスを表します.たとえば、次の値は異なる意味を表す可能性があります.
//ApiService.java
public interface ApiService {
String ENDPOINT = Constants.END_POINT;
@POST("app/api")
Observable<Response1> request1(@Body Request1 request);
@POST("app/api")
Observable<Response2> request2(@Body Request2 request);
/**
* Create a new ApiService
*/
class Factory {
private Factory() { }
public static ApiService createService( ) {
OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
builder.readTimeout(10, TimeUnit.SECONDS);
builder.connectTimeout(9, TimeUnit.SECONDS);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
builder.addInterceptor(interceptor);
}
builder.addInterceptor(new HeaderInterceptor());
OkHttpClient client = builder.build();
Retrofit retrofit =
new Retrofit.Builder().baseUrl(ApiService.ENDPOINT)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit.create(ApiService.class);
}
}
}
使用時:
ApiService mApiService = ApiService.Factory.createService();
mApiService.request1(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
}
@Override
public void onNext(Response1 response) {
int code = response.getCode();
switch (code) {
case 1: //do something
break;
case -101://do something
break;
case -102: //do something
break;
default:
break;
}
}
});
もしすべての要求に対してすべてこのようにするならば、それは死んだ人を書くのではありませんか、もしある日これらの値が変わったら、例えば-102から-105になったら、あなたはすべての場所がすべて変えなければならないのではなくて、考えてみると怖いです!
解決策
Retrofit retrofit =
new Retrofit.Builder().baseUrl(ApiService.ENDPOINT)
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
addConverterFactory(GsonConverterFactory.create()というコードは、サービス側から返されたjsonデータをGsonでエンティティに解析するためのものです.ここから、GsonConverterを自分で定義し、元の機能を拡張することができます
まず、デフォルトのGsonConverterがどのように書かれているかを分析します.3つのクラスから構成されています.
final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter adapter;
GsonResponseBodyConverter(Gson gson, TypeAdapter adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override public T convert(ResponseBody value) throws IOException {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
あなたは間違っていません.この数行のコードです.このconvert()方法は拡張する場所です.
元のロジックに処理コードを追加するだけ!=1の場合、code!=1、異常を投げ出し、
まず直接コードを入力:
//CustomGsonConverterFactory.java
public class CustomGsonConverterFactory extends Converter.Factory {
private final Gson gson;
private CustomGsonConverterFactory(Gson gson) {
if (gson == null) throw new NullPointerException("gson == null");
this.gson = gson;
}
public static CustomGsonConverterFactory create() {
return create(new Gson());
}
public static CustomGsonConverterFactory create(Gson gson) {
return new CustomGsonConverterFactory(gson);
}
@Override
public Converter responseBodyConverter(Type type, Annotation[] annotations,Retrofit retrofit) {
TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonResponseBodyConverter<>(gson, adapter);
}
@Override
public Converter, RequestBody> requestBodyConverter(Type type,Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
return new CustomGsonRequestBodyConverter<>(gson, adapter);
}
}
//CustomGsonRequestBodyConverter.java
final class CustomGsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
private static final MediaType MEDIA_TYPE = MediaType.parse("application/json; charset=UTF-8");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private final Gson gson;
private final TypeAdapter adapter;
CustomGsonRequestBodyConverter(Gson gson, TypeAdapter adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public RequestBody convert(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());
}
}
//CustomGsonResponseBodyConverter.java
final class CustomGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
private final Gson gson;
private final TypeAdapter adapter;
CustomGsonResponseBodyConverter(Gson gson, TypeAdapter adapter) {
this.gson = gson;
this.adapter = adapter;
}
@Override
public T convert(ResponseBody value) throws IOException {
String response = value.string();
HttpStatus httpStatus = gson.fromJson(response, HttpStatus.class);
if (httpStatus.isCodeInvalid()) {
value.close();
throw new ApiException(httpStatus.getCode(), httpStatus.getMessage());
}
MediaType contentType = value.contentType();
Charset charset = contentType != null ? contentType.charset(UTF_8) : UTF_8;
InputStream inputStream = new ByteArrayInputStream(response.getBytes());
Reader reader = new InputStreamReader(inputStream, charset);
JsonReader jsonReader = gson.newJsonReader(reader);
try {
return adapter.read(jsonReader);
} finally {
value.close();
}
}
}
彼の2つのクラスはデフォルトと同じで、3番目のクラスだけを見ています.
ここでは、HttpStatusとApiExceptionの2つのクラスをカスタマイズします.次の2つのクラスがあります.
//HttpStatus.java
public class HttpStatus {
@SerializedName("code")
private int mCode;
@SerializedName("message")
private String mMessage;
public int getCode() {
return mCode;
}
public String getMessage() {
return mMessage;
}
/**
* API
*
* @return true, false
*/
public boolean isCodeInvalid() {
return mCode != Constants.WEB_RESP_CODE_SUCCESS;
}
}
//ApiException.java
public class ApiException extends RuntimeException {
private int mErrorCode;
public ApiException(int errorCode, String errorMessage) {
super(errorMessage);
mErrorCode = errorCode;
}
/**
* token
*
* @return true, false;
*/
public boolean isTokenExpried() {
return mErrorCode == Constants.TOKEN_EXPRIED;
}
}
わかりやすく、重要なコードをいくつか説明します.
String response = value.string(); // responsebody string
// code ==1, HttpStatus , code message
HttpStatus httpStatus = gson.fromJson(response, HttpStatus.class);
if (httpStatus.isCodeInvalid()) {
value.close();
// RuntimeException, Subscriber onError()
throw new ApiException(httpStatus.getCode(), httpStatus.getMessage());
}
ここにはResponseBodyに関する穴があります.もし誰かがこの異常に遭遇したことがあれば、きっと知っています.
java.lang.IllegalStateException: closed
at com.squareup.okhttp.internal.http.HttpConnection$FixedLengthSource.read(HttpConnection.java:455)
at okio.Buffer.writeAll(Buffer.java:594)
at okio.RealBufferedSource.readByteArray(RealBufferedSource.java:87)
at com.squareup.okhttp.ResponseBody.bytes(ResponseBody.java:56)
at com.squareup.okhttp.ResponseBody.string(ResponseBody.java:82)
ResponseBodyを1回しか読めないのでresponseを呼び出すとbody().string()2回またはresponse.body().charStream()は2回でこの異常が発生するので、string()を呼び出してからcharStream()を呼び出してもよい.
だから通常のやり方は1回読み取った後に保存して、次回はResponseBodyから読み取らないことです.
使用方法
まずBaseSubscriberを構築します
//BaseSubscriber.java
public class BaseSubscriber extends Subscriber {
protected Context mContext;
public BaseSubscriber(Context context) {
this.mContext = context;
}
@Override
public void onCompleted() {
}
@Override
public void onError(final Throwable e) {
Log.w("Subscriber onError", e);
if (e instanceof HttpException) {
// We had non-2XX http error
Toast.makeText(mContext, mContext.getString(R.string.server_internal_error), Toast.LENGTH_SHORT).show();
} else if (e instanceof IOException) {
// A network or conversion error happened
Toast.makeText(mContext, mContext.getString(R.string.cannot_connected_server), Toast.LENGTH_SHORT).show();
} else if (e instanceof ApiException) {
ApiException exception = (ApiException) e;
if (exception.isTokenExpried()) {
// token
} else {
Toast.makeText(mContext, e.getMessage(), Toast.LENGTH_SHORT).show();
}
}
}
@Override
public void onNext(T t) {
}
}
リクエストインタフェース
ApiService mApiService = ApiService.Factory.createService();
mApiService.request1(request)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BaseSubscriber() {
@Override
public void onCompleted() {
super.onCompleted();
}
@Override
public void onError(Throwable e) {
super.onError(e); //
}
@Override
public void onNext(Response1 response) {
super.onNext(response);
}
});
簡書より:http://www.jianshu.com/p/5b8b1062866b