2015-07-26 10 views
13

Ogni richiesta al server può restituire error_code. Voglio gestire questi errori in un unico luogo quando stavo usando AsyncTask Ho avuto un BaseAsyncTask genereRetrofit e gestione degli errori centralizzata

public abstract class BaseAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> { 

    protected Context context; 
    private ProgressDialog progressDialog; 
    private Result result; 

    protected BaseAsyncTask(Context context, ProgressDialog progressDialog) { 
     this.context = context; 
     this.progressDialog = progressDialog; 
    } 

    @Override 
    protected void onPreExecute() { 
     super.onPreExecute(); 
    } 

    @Override 
    protected void onPostExecute(Result result) { 
     super.onPostExecute(result); 
     HttpResponse<ErrorResponse> response = (HttpResponse<ErrorResponse>) result; 
    if(response.getData().getErrorCode() != -1) { 
       handleErrors(response.getData()); 
     }else 
      onResult(result); 

    } 

    private void handleErrors(ErrorResponse errorResponse) { 
    } 
    public abstract void onResult(Result result); 
    } 

Ma, usando retrofit ogni richiesta ha il suo errore di richiamata movimentazione:

    git.getFeed(user,new Callback<gitmodel>() { 
        @Override 
        public void success(gitmodel gitmodel, Response response) { 

        } 

        @Override 
        public void failure(RetrofitError error) { 

        } 
       }); 
      } 
     }); 

Come posso gestire tutti gli errori in un unico posto?

+0

è possibile creare l'oggetto Richiamata da utilizzare come callback comune. –

risposta

17

Se avete bisogno di ottenere qualche errore 'logica', allora avete bisogno di qualche logica Java, in quanto non è una caratteristica Retrofit in modo sostanzialmente:

  1. Creare una richiamata L'implementazione che implementa il Retrofit richiamata
  2. Crea un oggetto di base che definiscono il metodo 'isError'
  3. Modifica Retrofit RestAdapter al fine di ottenere la vostra richiamata al posto del Retrofit One

MyCallback.java

import android.util.Log; 
import retrofit.Callback; 
import retrofit.client.Response; 

public abstract class MyCallback<T extends MyObject> implements Callback<T> { 

    @Override 
    public final void success(T o, Response response) { 
     if (o.isError()) { 
      // [..do something with error] 
      handleLogicError(o); 
     } 
     else { 
      handleSuccess(o, response); 
     } 
    } 

    abstract void handleSuccess(T o, Response response); 

    void handleLogicError(T o) { 
     Log.v("TAG", "Error because userId is " + o.id); 
    } 
} 

MyObject.java (la classe base per tutti gli oggetti che si ottiene da retrofit)

public class MyObject { 
    public long id; 
    public boolean isError() { 
     return id == 1; 
    } 
} 

MyRealObject.java - una classe che estende l'oggetto di base

public class MyRealObject extends MyObject { 
    public long userId; 
    public String title; 
    public String body; 
} 

RetroInterface.java - l'interfaccia utilizzata per retrofit è necessario avere familiarità con

import retrofit.http.GET; 
import retrofit.http.Path; 

public interface RetroInterface { 

    @GET("/posts/{id}") 
    void sendGet(@Path("id") int id, MyCallback<MyRealObject> callback); 

} 

E infine il pezzo di codice in cui si utilizza tutta la logica

RestAdapter adapter = new RestAdapter.Builder() 
      .setEndpoint("http://jsonplaceholder.typicode.com") 
      .build(); 

    RetroInterface itf = adapter.create(RetroInterface.class); 
    itf.sendGet(2, new MyCallback<MyRealObject>() { 
     @Override 
     void handleSuccess(MyRealObject o, Response response) { 
      Log.v("TAG", "success"); 
     } 

     @Override 
     public void failure(RetrofitError error) { 
      Log.v("TAG", "failure"); 
     } 
    }); 

Se si copia e incolla questo codice, si otterrà un errore quando eseguirai il itf.sendGet(1, new MyCallback..) e un successo per itf.sendGet(2, new MyCallback...)

+0

Sì, devo combinare il mio modello di codice con il suggerimento @Teo Inke per ottenere la soluzione –

7

Non sono sicuro di averlo capito correttamente, ma è possibile creare un callback e passarlo come parametro a tutte le vostre richieste.

Invece di:

  git.getFeed(user,new Callback<gitmodel>() { 
       @Override 
       public void success(gitmodel gitmodel, Response response) { 

       } 

       @Override 
       public void failure(RetrofitError error) { 

       } 
      }); 

prima definire il callback:

Callback<gitmodel> mCallback = new Callback<gitmodel>() { 
    @Override 
    public void success(gitmodel gitmodel, Response response) { 

    } 

    @Override 
    public void failure(RetrofitError error) { 
     // logic to handle error for all requests 
    } 
}; 

Poi:

git.getFeed(user, mCallback); 
+1

Penso che quello che chiede Ma7moud sia, supponiamo che la chiamata all'API sia stata un successo, ma ha riportato un successo: false e un codice_errore: "qualcosa" invece di successo: true e successData: "qualcosa", come gestiamo il ritorno per error_code è la domanda. In questo caso, il retrofit lo considera un successo poiché l'API è stata chiamata con successo. –

6

In Retrofit è possibile specificare ErrorHandler a tutte le richieste.

public class ApiErrorHandler implements ErrorHandler { 

    @Override 
    public Throwable handleError(RetrofitError cause) { 
     //here place your logic for all errors 
     return cause; 
    } 
} 

applicarlo a RestAdapter

RestAdapter.Builder() 
      .setClient(client) 
      .setEndpoint(endpoint) 
      .setErrorHandler(errorHandler) 
      .build(); 

penso che sia quello che hai chiesto.

+0

Server 'error_code' non come '404' sono errori logici come @Jaseem Abbas ha detto quindi non posso inserire un gestore di errori globale per loro –

+0

Quindi dovresti prendere in considerazione la creazione di abstract calback, rendere la tua logica e postbackback – jakubbialkowski

0

Semplicemente retrofit esempio di gestione degli errori personalizzato. È impostato in modo tale che non sia necessario svolgere molto lavoro nel gestore di 'errore' di una chiamata di aggiornamento per visualizzare il messaggio di errore visibile all'utente. Funziona su tutti gli endpoint. C'è un sacco di gestione delle eccezioni come il nostro popolo del server piace tenerci sulla punta dei piedi inviando tutti i tipi di cose a caso ..!

 retrofit-custom-error-handling.java 
    // on error the server sends JSON 
    /* 
    { "error": { "data": { "message":"A thing went wrong" } } } 
    */ 
    // create model classes.. 
    public class ErrorResponse { 
    Error error; 
    public static class Error { 
    Data data; 
    public static class Data { 
    String message; 
    } 
    } 
    } 
    // 
    /** 
    * Converts the complex error structure into a single string you can get with error.getLocalizedMessage() in Retrofit error handlers. 
    * Also deals with there being no network available 
    * 
    * Uses a few string IDs for user-visible error messages 
    */ 
    private static class CustomErrorHandler implements ErrorHandler { 
    private final Context ctx; 

    public CustomErrorHandler(Context ctx) { 
     this.ctx = ctx; 
    } 

    @Override 
    public Throwable handleError(RetrofitError cause) { 
     String errorDescription; 
     if (cause.isNetworkError()) { 
      errorDescription = ctx.getString(R.string.error_network); 
     } else { 
      if (cause.getResponse() == null) { 
       errorDescription = ctx.getString(R.string.error_no_response); 
      } else { 
// Error message handling - return a simple error to Retrofit handlers.. 
       try { 
        ErrorResponse errorResponse = (ErrorResponse) cause.getBodyAs(ErrorResponse.class); 
        errorDescription = errorResponse.error.data.message; 
       } catch (Exception ex) { 
        try { 
         errorDescription = ctx.getString(R.string.error_network_http_error, cause.getResponse().getStatus()); 
        } catch (Exception ex2) { 
         Log.e(TAG, "handleError: " + ex2.getLocalizedMessage()); 
         errorDescription = ctx.getString(R.string.error_unknown); 
        } 
       } 
      } 
     } 
     return new Exception(errorDescription); 
    } 
} 
// When creating the Server... 
retrofit.RestAdapter restAdapter = new retrofit.RestAdapter.Builder() 
     .setEndpoint(apiUrl) 
     .setLogLevel(retrofit.RestAdapter.LogLevel.FULL) 
     .setErrorHandler(new CustomErrorHandler(ctx)) // use error handler.. 
     .build(); 
server=restAdapter.create(Server.class); 
// Now when calling server methods, get simple error out like this: 
     server.postSignIn(login,new Callback<HomePageResponse>(){ 
@Override 
public void success(HomePageResponse homePageResponse,Response response){ 
// Do success things! 
     } 
@Override 
public void failure(RetrofitError error){ 
     error.getLocalizedMessage(); // <-- this is the message to show to user. 
     } 
     }); 
1

In Retrofit2 non è possibile impostare un ErrorHandler con il metodo .setErrorHandler(), ma è possibile creare un intercettore a sborsare tutti i possibili errori centralizzati in un unico luogo della vostra applicazione.

Con questo esempio si dispone di una posizione centralizzata per la gestione degli errori con Retrofit2 e OkHttpClient. Basta riutilizzare l'oggetto Retrofit (retrofit).

È possibile provare questo esempio standalone con un intercettore personalizzato per errori di rete e server. Entrambi saranno gestiti diversamente in Retrofit2, pertanto è necessario controllare il codice di errore restituito dal server tramite il codice di risposta (response.code()) e se la risposta non ha avuto esito positivo (!response.isSuccessful()).

Nel caso in cui l'utente non abbia alcuna connessione alla rete o al server, è necessario rilevare un IOExcezione del metodo Response response = chain.proceed(chain.request()); e gestire l'errore di rete nel blocco catch.

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(); 
    loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); 

    OkHttpClient client = new OkHttpClient.Builder() 
      .addInterceptor(loggingInterceptor) 
      .addInterceptor(new Interceptor() { 
       @Override 
       public Response intercept(Chain chain) throws IOException { 
        try { 
         Response response = chain.proceed(chain.request()); 
         if (!response.isSuccessful()) { 
          Log.e("tag", "Failure central - response code: " + response.code()); 
          Log.e("tag", "central server error handling"); 

          // Central error handling for error responses here: 
          // e.g. 4XX and 5XX errors 
          switch (response.code()) { 
           case 401: 
            // do something when 401 Unauthorized happened 
            // e.g. delete credentials and forward to login screen 
            // ... 

            break; 
           case 403: 
            // do something when 403 Forbidden happened 
            // e.g. delete credentials and forward to login screen 
            // ... 

            break; 
           default: 
            Log.e("tag", "Log error or do something else with error code:" + response.code()); 

            break; 
          } 
         } 

         return response; 
        } catch (IOException e) { 
         // Central error handling for network errors here: 
         // e.g. no connection to internet/to server 

         Log.e("tag", e.getMessage(), e); 
         Log.e("tag", "central network error handling"); 

         throw e; 
        } 
       } 
      }) 
      .build(); 

    Retrofit retrofit = new Retrofit.Builder() 
      .baseUrl("http://10.0.2.2:8000/api/v1/") 
      .client(client) 
      .addConverterFactory(GsonConverterFactory.create()) 
      .build(); 

    UserRepository backendRepository = retrofit.create(UserRepository.class); 
    backendRepository.getUser("userId123").enqueue(new Callback<UserModel>() { 
     @Override 
     public void onResponse(Call<UserModel> call, retrofit2.Response<UserModel> response) { 
      Log.d("tag", "onResponse"); 

      if (!response.isSuccessful()) { 
       Log.e("tag", "onFailure local server error handling code:" + response.code()); 
      } else { 
       // its all fine with the request 


      } 
     } 

     @Override 
     public void onFailure(Call<UserModel> call, Throwable t) { 
      Log.e("tag", "onFailure local network error handling"); 
      Log.e("tag", t.getMessage(), t); 

     } 
    }); 

esempio UserRepository:

public interface UserRepository { 
    @GET("users/{userId}/") 
    Call<UserModel> getUser(@Path("userId") String userId); 

} 

UserModel esempio:

public class UserModel implements Parcelable { 
    @SerializedName("id") 
    @Expose 
    public String id = ""; 

    @SerializedName("email") 
    @Expose 
    public String mail = ""; 

    public UserModel() { 

    } 

    protected UserModel(Parcel in) { 
     id = in.readString(); 
     mail = in.readString(); 
    } 

    public static final Creator<UserModel> CREATOR = new Creator<UserModel>() { 
     @Override 
     public UserModel createFromParcel(Parcel in) { 
      return new UserModel(in); 
     } 

     @Override 
     public UserModel[] newArray(int size) { 
      return new UserModel[size]; 
     } 
    }; 

    @Override 
    public int describeContents() { 
     return 0; 
    } 

    @Override 
    public void writeToParcel(Parcel dest, int flags) { 
     dest.writeString(id); 
     dest.writeString(mail); 
    } 
}