From 42a4b4df940cfc79a56a8deec7322bc9e213243d Mon Sep 17 00:00:00 2001 From: Kosh Date: Sat, 18 Mar 2017 16:06:56 +0800 Subject: [PATCH] this commit improves error messages and close #97 --- app/proguard-rules.pro | 21 ++++- app/src/main/java/com/fastaccess/App.java | 2 +- .../provider/rest/RestProvider.java | 36 +++----- .../rest/handler/RetrofitException.java | 88 +++++++++++++++++++ .../RxErrorHandlingCallAdapterFactory.java | 67 ++++++++++++++ .../com/fastaccess/ui/base/BaseActivity.java | 3 +- .../ui/base/mvp/presenter/BasePresenter.java | 24 ++++- .../layout/title_header_layout.xml | 1 - 8 files changed, 209 insertions(+), 33 deletions(-) create mode 100644 app/src/main/java/com/fastaccess/provider/rest/handler/RetrofitException.java create mode 100644 app/src/main/java/com/fastaccess/provider/rest/handler/RxErrorHandlingCallAdapterFactory.java diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 1a38046b..39212d32 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -79,4 +79,23 @@ -keepclassmembers class com.prettifier.pretty.callback.MarkDownInterceptorInterface { public *; } -#-keep class com.siimkinks.sqlitemagic.** {*;} \ No newline at end of file +-dontwarn java.lang.FunctionalInterface +-dontwarn java.util.** +-dontwarn java.time.** +-dontwarn javax.annotation.** +-dontwarn javax.cache.** +-dontwarn javax.naming.** +-dontwarn javax.transaction.** +-dontwarn java.sql.** +-dontwarn android.support.** +-dontwarn io.requery.cache.** +-dontwarn io.requery.rx.** +-dontwarn io.requery.reactivex.** +-dontwarn io.requery.reactor.** +-dontwarn io.requery.query.** +-dontwarn io.requery.android.sqlcipher.** +-dontwarn io.requery.android.sqlitex.** +-keepclassmembers enum io.requery.** { + public static **[] values(); + public static ** valueOf(java.lang.String); +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/App.java b/app/src/main/java/com/fastaccess/App.java index c87f8e15..46df77e8 100644 --- a/app/src/main/java/com/fastaccess/App.java +++ b/app/src/main/java/com/fastaccess/App.java @@ -45,7 +45,7 @@ public class App extends MultiDexApplication { public SingleEntityStore getDataStore() { if (dataStore == null) { EntityModel model = Models.DEFAULT; - DatabaseSource source = new DatabaseSource(this, model, "FastHub-DB", 2); + DatabaseSource source = new DatabaseSource(this, model, "FastHub-DB", 1); Configuration configuration = new ConfigurationBuilder(source, model) .useDefaultLogging() .build(); diff --git a/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java b/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java index 2f8d7703..da402001 100644 --- a/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java +++ b/app/src/main/java/com/fastaccess/provider/rest/RestProvider.java @@ -6,33 +6,33 @@ import android.net.Uri; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.fastaccess.App; import com.fastaccess.BuildConfig; import com.fastaccess.R; -import com.fastaccess.data.service.LoginRestService; import com.fastaccess.data.dao.GitHubErrorResponse; import com.fastaccess.data.service.GistService; import com.fastaccess.data.service.IssueService; +import com.fastaccess.data.service.LoginRestService; import com.fastaccess.data.service.NotificationService; import com.fastaccess.data.service.PullRequestService; import com.fastaccess.data.service.RepoService; import com.fastaccess.data.service.SearchService; import com.fastaccess.data.service.UserRestService; import com.fastaccess.helper.InputHelper; -import com.fastaccess.helper.Logger; import com.fastaccess.helper.PrefGetter; import com.fastaccess.provider.rest.converters.GithubResponseConverter; +import com.fastaccess.provider.rest.handler.RetrofitException; +import com.fastaccess.provider.rest.handler.RxErrorHandlingCallAdapterFactory; import com.fastaccess.provider.rest.interceptors.PaginationInterceptor; import com.google.gson.FieldNamingPolicy; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import java.io.IOException; import java.lang.reflect.Modifier; import okhttp3.Cache; import okhttp3.OkHttpClient; import okhttp3.Request; -import okhttp3.ResponseBody; import okhttp3.logging.HttpLoggingInterceptor; import retrofit2.Retrofit; import retrofit2.adapter.rxjava.HttpException; @@ -55,14 +55,6 @@ public class RestProvider { .setPrettyPrinting() .create(); - private static Cache provideCache() { - if (cache == null) { - int cacheSize = 20 * 1024 * 1024; //20MB - cache = new Cache(App.getInstance().getCacheDir(), cacheSize); - } - return cache; - } - private static OkHttpClient provideOkHttpClient(boolean forLogin) { OkHttpClient.Builder client = new OkHttpClient.Builder(); if (BuildConfig.DEBUG) { @@ -87,7 +79,6 @@ public class RestProvider { Request request = requestBuilder.build(); return chain.proceed(request); }); -// client.cache(provideCache());//disable cache, since we are going offline. return client.build(); } @@ -96,18 +87,18 @@ public class RestProvider { .baseUrl(BuildConfig.REST_URL) .client(provideOkHttpClient(false)) .addConverterFactory(new GithubResponseConverter(gson)) - .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) + .addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create()) .build(); } - public static long downloadFile(@NonNull Context context, @NonNull String url) { + public static void downloadFile(@NonNull Context context, @NonNull String url) { DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE); DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url)); request.setDescription(url); request.setTitle(context.getString(R.string.downloading_file)); request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_MOBILE | DownloadManager.Request.NETWORK_WIFI); request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED); - return downloadManager.enqueue(request); + downloadManager.enqueue(request); } public static int getErrorCode(Throwable throwable) { @@ -157,14 +148,11 @@ public class RestProvider { } @Nullable public static GitHubErrorResponse getErrorResponse(@NonNull Throwable throwable) { - if (throwable instanceof HttpException) { - ResponseBody body = ((HttpException) throwable).response().errorBody(); - if (body != null) { - try { - Logger.e(body.string()); - return new Gson().fromJson(body.toString(), GitHubErrorResponse.class); - } catch (Exception ignored) {} - } + RetrofitException error = (RetrofitException) throwable; + try { + return error.getErrorBodyAs(GitHubErrorResponse.class); + } catch (IOException e) { + e.printStackTrace(); } return null; } diff --git a/app/src/main/java/com/fastaccess/provider/rest/handler/RetrofitException.java b/app/src/main/java/com/fastaccess/provider/rest/handler/RetrofitException.java new file mode 100644 index 00000000..e71e1cbc --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/rest/handler/RetrofitException.java @@ -0,0 +1,88 @@ +package com.fastaccess.provider.rest.handler; + +import java.io.IOException; +import java.lang.annotation.Annotation; + +import okhttp3.ResponseBody; +import retrofit2.Converter; +import retrofit2.Response; +import retrofit2.Retrofit; + +/** + * copied from http://bytes.babbel.com/en/articles/2016-03-16-retrofit2-rxjava-error-handling.html + */ +public class RetrofitException extends RuntimeException { + public static RetrofitException httpError(String url, Response response, Retrofit retrofit) { + String message = response.code() + " " + response.message(); + return new RetrofitException(message, url, response, Kind.HTTP, null, retrofit); + } + + public static RetrofitException networkError(IOException exception) { + return new RetrofitException(exception.getMessage(), null, null, Kind.NETWORK, exception, null); + } + + public static RetrofitException unexpectedError(Throwable exception) { + return new RetrofitException(exception.getMessage(), null, null, Kind.UNEXPECTED, exception, null); + } + + /** Identifies the event kind which triggered a {@link RetrofitException}. */ + public enum Kind { + /** An {@link IOException} occurred while communicating to the server. */ + NETWORK, + /** A non-200 HTTP status code was received from the server. */ + HTTP, + /** + * An internal error occurred while attempting to execute a request. It is best practice to + * re-throw this exception so your application crashes. + */ + UNEXPECTED + } + + private final String url; + private final Response response; + private final Kind kind; + private final Retrofit retrofit; + + public RetrofitException(String message, String url, Response response, Kind kind, Throwable exception, Retrofit retrofit) { + super(message, exception); + this.url = url; + this.response = response; + this.kind = kind; + this.retrofit = retrofit; + } + + /** The request URL which produced the error. */ + public String getUrl() { + return url; + } + + /** Response object containing status code, headers, body, etc. */ + public Response getResponse() { + return response; + } + + /** The event kind which triggered this error. */ + public Kind getKind() { + return kind; + } + + /** The Retrofit this request was executed on */ + public Retrofit getRetrofit() { + return retrofit; + } + + /** + * HTTP response body converted to specified {@code type}. {@code null} if there is no + * response. + * + * @throws IOException + * if unable to convert the body to the specified {@code type}. + */ + public T getErrorBodyAs(Class type) throws IOException { + if (response == null || response.errorBody() == null) { + return null; + } + Converter converter = retrofit.responseBodyConverter(type, new Annotation[0]); + return converter.convert(response.errorBody()); + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/provider/rest/handler/RxErrorHandlingCallAdapterFactory.java b/app/src/main/java/com/fastaccess/provider/rest/handler/RxErrorHandlingCallAdapterFactory.java new file mode 100644 index 00000000..d85b8469 --- /dev/null +++ b/app/src/main/java/com/fastaccess/provider/rest/handler/RxErrorHandlingCallAdapterFactory.java @@ -0,0 +1,67 @@ +package com.fastaccess.provider.rest.handler; + +import java.io.IOException; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; + +import retrofit2.Call; +import retrofit2.CallAdapter; +import retrofit2.Response; +import retrofit2.Retrofit; +import retrofit2.adapter.rxjava.HttpException; +import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory; +import rx.Observable; +import rx.functions.Func1; + +/** + * copied from http://bytes.babbel.com/en/articles/2016-03-16-retrofit2-rxjava-error-handling.html + */ +public class RxErrorHandlingCallAdapterFactory extends CallAdapter.Factory { + private final RxJavaCallAdapterFactory original; + + private RxErrorHandlingCallAdapterFactory() { + original = RxJavaCallAdapterFactory.create(); + } + + @Override public CallAdapter get(Type returnType, Annotation[] annotations, Retrofit retrofit) { + return new RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit)); + } + + public static CallAdapter.Factory create() { + return new RxErrorHandlingCallAdapterFactory(); + } + + private static class RxCallAdapterWrapper implements CallAdapter> { + private final Retrofit retrofit; + private final CallAdapter wrapped; + + public RxCallAdapterWrapper(Retrofit retrofit, CallAdapter wrapped) { + this.retrofit = retrofit; + this.wrapped = wrapped; + } + + @Override public Type responseType() { + return wrapped.responseType(); + } + + @SuppressWarnings("unchecked") @Override public Observable adapt(Call call) { + return ((Observable) wrapped.adapt(call)).onErrorResumeNext(new Func1() { + @Override public Observable call(Throwable throwable) { + return Observable.error(asRetrofitException(throwable)); + } + }); + } + + private RetrofitException asRetrofitException(Throwable throwable) { + if (throwable instanceof HttpException) { + HttpException httpException = (HttpException) throwable; + Response response = httpException.response(); + return RetrofitException.httpError(response.raw().request().url().toString(), response, retrofit); + } + if (throwable instanceof IOException) { + return RetrofitException.networkError((IOException) throwable); + } + return RetrofitException.unexpectedError(throwable); + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java index 9d36d175..31c317e8 100644 --- a/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java +++ b/app/src/main/java/com/fastaccess/ui/base/BaseActivity.java @@ -147,10 +147,9 @@ public abstract class BaseActivity extends TiPresenter impl return; } GitHubErrorResponse errorResponse = RestProvider.getErrorResponse(throwable); - Logger.e(errorResponse); if (errorResponse != null && errorResponse.getMessage() != null) { sendToView(v -> v.showErrorMessage(errorResponse.getMessage())); } else { - sendToView(v -> v.showErrorMessage(throwable.getMessage())); + sendToView(v -> v.showMessage(R.string.error, getPrettifiedErrorMessage(throwable))); } } @@ -64,6 +63,23 @@ public class BasePresenter extends TiPresenter impl } @StringRes private int getPrettifiedErrorMessage(@Nullable Throwable throwable) { - return 0; + int resId = R.string.network_error; + RetrofitException exception = (RetrofitException) throwable; + if (exception != null) { + if (exception.getKind() != null) { + switch (exception.getKind()) { + case NETWORK: + resId = R.string.network_error; + break; + case HTTP: + resId = R.string.request_error; + break; + case UNEXPECTED: + resId = R.string.unexpected_error; + break; + } + } + } + return resId; } } diff --git a/app/src/main/res/layouts/main_layouts/layout/title_header_layout.xml b/app/src/main/res/layouts/main_layouts/layout/title_header_layout.xml index 5a10d687..4a3e2d63 100644 --- a/app/src/main/res/layouts/main_layouts/layout/title_header_layout.xml +++ b/app/src/main/res/layouts/main_layouts/layout/title_header_layout.xml @@ -89,7 +89,6 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" - android:layout_marginStart="@dimen/spacing_normal" android:visibility="gone" tools:text="Java" tools:visibility="visible"/>