web-dev-qa-db-ja.com

一元化されたエラー処理レトロフィット2?

レトロフィット2の前は、エラーを一元的に処理する方法がありました-

_new retrofit.RestAdapter.Builder()
        .setEndpoint(apiUrl)
        .setLogLevel(retrofit.RestAdapter.LogLevel.FULL)
        .setErrorHandler(new CustomErrorHandler(ctx))
_

ただし、Retrofit 2では、RestAdapterの名前がRetrofitに変更され、setErrorHandler()はなくなりました。 Retrofit.Builder()を使用して一元化されたエラー処理を行う方法はありますか?

13
Ashwin

カスタムサービスの成功またはエラーのコールバックでカスタムCallAdapterを作成する必要があります。ジェイク・ウォートンはすでにそれを作りました。 githubのretrofit/samplesで見つけることができます

PS:2.2.0より前のバージョンには適用されません

/*
 * Copyright (C) 2015 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.Apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.example.retrofit;

import Java.io.IOException;
import Java.lang.annotation.Annotation;
import Java.lang.reflect.ParameterizedType;
import Java.lang.reflect.Type;
import Java.util.concurrent.Executor;
import retrofit2.Call;
import retrofit2.CallAdapter;
import retrofit2.Callback;
import retrofit2.converter.gson.GsonConverterFactory;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.http.GET;

/**
 * A sample showing a custom {@link CallAdapter} which adapts the built-in {@link Call} to a custom
 * version whose callback has more granular methods.
 */
public final class ErrorHandlingAdapter {
  /** A callback which offers granular callbacks for various conditions. */
  interface MyCallback<T> {
    /** Called for [200, 300) responses. */
    void success(Response<T> response);
    /** Called for 401 responses. */
    void unauthenticated(Response<?> response);
    /** Called for [400, 500) responses, except 401. */
    void clientError(Response<?> response);
    /** Called for [500, 600) response. */
    void serverError(Response<?> response);
    /** Called for network errors while making the call. */
    void networkError(IOException e);
    /** Called for unexpected errors while making the call. */
    void unexpectedError(Throwable t);
  }

  interface MyCall<T> {
    void cancel();
    void enqueue(MyCallback<T> callback);
    MyCall<T> clone();

    // Left as an exercise for the reader...
    // TODO MyResponse<T> execute() throws MyHttpException;
  }

  public static class ErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
    @Override public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations,
        Retrofit retrofit) {
      if (getRawType(returnType) != MyCall.class) {
        return null;
      }
      if (!(returnType instanceof ParameterizedType)) {
        throw new IllegalStateException(
            "MyCall must have generic type (e.g., MyCall<ResponseBody>)");
      }
      Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
      Executor callbackExecutor = retrofit.callbackExecutor();
      return new ErrorHandlingCallAdapter<>(responseType, callbackExecutor);
    }

    private static final class ErrorHandlingCallAdapter<R> implements CallAdapter<R, MyCall<R>> {
      private final Type responseType;
      private final Executor callbackExecutor;

      ErrorHandlingCallAdapter(Type responseType, Executor callbackExecutor) {
        this.responseType = responseType;
        this.callbackExecutor = callbackExecutor;
      }

      @Override public Type responseType() {
        return responseType;
      }

      @Override public MyCall<R> adapt(Call<R> call) {
        return new MyCallAdapter<>(call, callbackExecutor);
      }
    }
  }

  /** Adapts a {@link Call} to {@link MyCall}. */
  static class MyCallAdapter<T> implements MyCall<T> {
    private final Call<T> call;
    private final Executor callbackExecutor;

    MyCallAdapter(Call<T> call, Executor callbackExecutor) {
      this.call = call;
      this.callbackExecutor = callbackExecutor;
    }

    @Override public void cancel() {
      call.cancel();
    }

    @Override public void enqueue(final MyCallback<T> callback) {
      call.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, Response<T> response) {
          // TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed
          // on that executor by submitting a Runnable. This is left as an exercise for the reader.

          int code = response.code();
          if (code >= 200 && code < 300) {
            callback.success(response);
          } else if (code == 401) {
            callback.unauthenticated(response);
          } else if (code >= 400 && code < 500) {
            callback.clientError(response);
          } else if (code >= 500 && code < 600) {
            callback.serverError(response);
          } else {
            callback.unexpectedError(new RuntimeException("Unexpected response " + response));
          }
        }

        @Override public void onFailure(Call<T> call, Throwable t) {
          // TODO if 'callbackExecutor' is not null, the 'callback' methods should be executed
          // on that executor by submitting a Runnable. This is left as an exercise for the reader.

          if (t instanceof IOException) {
            callback.networkError((IOException) t);
          } else {
            callback.unexpectedError(t);
          }
        }
      });
    }

    @Override public MyCall<T> clone() {
      return new MyCallAdapter<>(call.clone(), callbackExecutor);
    }
  }
}
10
Mustapha Hadid

Retrofit 2.0はErrorHandlerを移動し、2つのメソッドを含む新しいCallbackを使用しました。

/** Successful HTTP response. */
public void onResponse(Response<T> response, Retrofit retrofit)````

/** Invoked when a network or unexpected exception occurred during the HTTP request. */
public void onFailure(Throwable t)

Retrofit2.xは、httpコードが2xxまたは3xxではない場合でも、onResponseですべてのHTTP応答を受信します。ここで、onResponseメソッドで応答ステータスコードを確認し、応答成功応答(通常2xxまたは3xx)および正しい論理処理を行います。

Retrofit2.xをアップグレードしました。集中型エラー処理に関する私の解決策は、Retrofit.Callbackを拡張する抽象クラスを2つのメソッドonSuccessとonFailedで作成します。ビジネスロジックが失敗したときに常に同じプロセスを実行するため、onFailedは抽象的ではありません。リクエストが成功したときの別のこと。あなたはサンプルコードを参照することができます ここ

次に、httpリクエストを送信する必要がある場合は、onSuccessメソッドを実装する必要があります。また、場合によってはonFailedメソッドをオーバーライドすることもできます。プロジェクトで述べたように、ほとんどの場合、同じ方法で失敗を処理します。例 here を参照できます。これは、retrofit2を使用して投稿リクエストを送信しました。

これがあなたに役立つことを願っています!

3
Weibo

401に固有のケースを一元的に処理する場合、および新しい認証トークンでリクエストを再試行する場合については、これを参照してください スタックオーバーフローの回答

0
KnowIT

私はアミールが提案するのと同じような解決策を使用しましたが、これがさらに簡単になるかどうか疑問に思っています。私は以下を試しました:

public void onResponse(Response<T> response) {
        if (response.isSuccess()) {
            T resp = response.body();
            handleSuccessResponse(resp);

        } else {
            Response<StatusResponse> statusResponse = response.error(response.code(), response.errorBody());
            handleHttpErrorResponse(statusResponse);
        }
    }

この方法では、Retrofitインスタンスを渡す必要はありません。ただし、エラー本文がStatusResponseに正常に解析されないため、何か不足しています。これが実際に何を意味するのかわかりません:

CallbackのonResponseコールバックにRetrofitインスタンスを提供する2.0.0-beta2での変更が元に戻されました。エラー本文の逆シリアル化を可能にするために、Retrofitオブジェクトの提供に関連するEdgeケースが多すぎます。この使用例に対応するには、Retrofitレスポンスを手動で渡すか、カスタムCallAdapter.Factoryを実装して自動で行います。

2.0.0-beta

0
pmellaaho