RetrofitカスタムGsonConverter処理要求エラー異常処理

26663 ワード

通常、サービス側から入手するJSONデータフォーマットは、次のようになります.
  {
    "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フィールドはステータスを表します.たとえば、次の値は異なる意味を表す可能性があります.
  • code=1、成功を表し、1に等しくないのはエラー
  • を表す
  • code=-101、tokenの期限切れを示す
  • code=-102は、携帯電話番号が
  • に登録されていることを示しています.
  • など通常のRetrofit+RxJavaロジックに従って処理すると、書かれたコードは次のようになります.
  • //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つのクラスから構成されています.
  • GsonConverterFactory//GsonConverterファクトリクラスGsonConverter
  • を作成する
  • GsonResponseBodyConverter//処理ResponseBody
  • GsonRequestBodyConverter//処理RequestBody
  • 名前から各クラスが何をしているのか分かりやすいので、GsonResponseBodyConverterというクラスが重要に違いありません.このクラスを見てみましょう.
    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