Android NetworkBoundResource с использованием ошибки API шаблона репозитория - PullRequest
0 голосов
/ 12 марта 2020

Привет, я только начал работать с mvvm с шаблоном репозитория. Я застрял с ошибкой API. Я использую обобщенный класс c API Response согласно официальному репозиторию Google в java

/**
 * Generic class for handling responses from Retrofit
 *
 * @param <T>
 */
public class ApiResponse<T> {

    public ApiResponse<T> create(Throwable error) {
        return new ApiErrorResponse<>(error.getMessage().equals("") ? error.getMessage() : "Unknown error\nCheck network connection");
    }

    public ApiResponse<T> create(Response<T> response) {

        if (response.isSuccessful()) {
            T body = response.body();

            if (body instanceof GithubApiResponse) {
                if (AppUtils.isValid((GithubApiResponse) body)) {
                    String errorMsg = "Empty Response.";
                    return new ApiErrorResponse<>(errorMsg);
                }
            }

            if (body == null || response.code() == 204) { // 204 is empty response
                return new ApiEmptyResponse<>();
            } else {
                return new ApiSuccessResponse<>(body);
            }
        } else {
            String errorMsg = "";
            try {
                errorMsg = response.errorBody().string();
            } catch (IOException e) {
                e.printStackTrace();
                errorMsg = response.message();
            }
            return new ApiErrorResponse<>(errorMsg);
        }
    }

    /**
     * Generic success response from api
     *
     * @param <T>
     */
    public class ApiSuccessResponse<T> extends ApiResponse<T> {

        private T body;

        ApiSuccessResponse(T body) {
            this.body = body;
        }

        public T getBody() {
            return body;
        }

    }

    /**
     * Generic Error response from API
     *
     * @param <T>
     */
    public class ApiErrorResponse<T> extends ApiResponse<T> {

        private String errorMessage;

        ApiErrorResponse(String errorMessage) {
            this.errorMessage = errorMessage;
        }

        public String getErrorMessage() {
            return errorMessage;
        }

    }


    /**
     * separate class for HTTP 204 resposes so that we can make ApiSuccessResponse's body non-null.
     */
    public class ApiEmptyResponse<T> extends ApiResponse<T> {
    }

}

и Мой networkBoundResource как этот

public abstract class NetworkBoundResource<ResultType, RequestType> {

    private AppExecutors appExecutors;
    private MediatorLiveData<Resource<ResultType>> results = new MediatorLiveData<>();

    public NetworkBoundResource(AppExecutors appExecutors) {
        this.appExecutors = appExecutors;
        init();
    }


    private void init() {

        // update LiveData for loading status
        results.setValue((Resource<ResultType>) Resource.loading(null));

        // observe LiveData source from local db
        final LiveData<ResultType> dbSource = loadFromDb();

        results.addSource(dbSource, new Observer<ResultType>() {
            @Override
            public void onChanged(@Nullable ResultType ResultType) {

                results.removeSource(dbSource);

                if (shouldFetch(ResultType)) {
                    // get data from the network
                    fetchFromNetwork(dbSource);
                } else {
                    results.addSource(dbSource, new Observer<ResultType>() {
                        @Override
                        public void onChanged(@Nullable ResultType ResultType) {
                            setValue(Resource.success(ResultType));
                        }
                    });
                }
            }
        });
    }

    /**
     * 1) observe local db
     * 2) if <condition/> query the network
     * 3) stop observing the local db
     * 4) insert new data into local db
     * 5) begin observing local db again to see the refreshed data from network
     *
     * @param dbSource
     */
    private void fetchFromNetwork(final LiveData<ResultType> dbSource) {

        Timber.d("fetchFromNetwork: called.");

        // update LiveData for loading status
        results.addSource(dbSource, new Observer<ResultType>() {
            @Override
            public void onChanged(@Nullable ResultType ResultType) {
                setValue(Resource.loading(ResultType));
            }
        });

        final LiveData<ApiResponse<RequestType>> apiResponse = createCall();

        results.addSource(apiResponse, new Observer<ApiResponse<RequestType>>() {
            @Override
            public void onChanged(@Nullable final ApiResponse<RequestType> requestObjectApiResponse) {
                results.removeSource(dbSource);
                results.removeSource(apiResponse);

                /*
                    3 cases:
                       1) ApiSuccessResponse
                       2) ApiErrorResponse
                       3) ApiEmptyResponse
                 */

                if (requestObjectApiResponse instanceof ApiResponse.ApiSuccessResponse) {
                    Timber.d("onChanged: ApiSuccessResponse.");

                    appExecutors.diskIO().execute(new Runnable() {
                        @Override
                        public void run() {

                            // save the response to the local db
                            saveCallResult((RequestType) processResponse((ApiResponse.ApiSuccessResponse) requestObjectApiResponse));

                            appExecutors.mainThread().execute(new Runnable() {
                                @Override
                                public void run() {
                                    results.addSource(loadFromDb(), new Observer<ResultType>() {
                                        @Override
                                        public void onChanged(@Nullable ResultType ResultType) {
                                            setValue(Resource.success(ResultType));
                                        }
                                    });
                                }
                            });
                        }
                    });
                } else if (requestObjectApiResponse instanceof ApiResponse.ApiEmptyResponse) {
                    Timber.d("onChanged: ApiEmptyResponse");
                    appExecutors.mainThread().execute(new Runnable() {
                        @Override
                        public void run() {
                            results.addSource(loadFromDb(), new Observer<ResultType>() {
                                @Override
                                public void onChanged(@Nullable ResultType ResultType) {
                                    setValue(Resource.success(ResultType));
                                }
                            });
                        }
                    });
                } else if (requestObjectApiResponse instanceof ApiResponse.ApiErrorResponse) {
                    Timber.d("onChanged: ApiErrorResponse.");
                    results.addSource(dbSource, new Observer<ResultType>() {
                        @Override
                        public void onChanged(@Nullable ResultType ResultType) {

                            setValue(
                                    Resource.error(
                                            ((ApiResponse.ApiErrorResponse) requestObjectApiResponse).getErrorMessage(),
                                            ResultType
                                    )
                            );
                        }
                    });
                }
            }
        });
    }

    private ResultType processResponse(ApiResponse.ApiSuccessResponse response) {
        return (ResultType) response.getBody();
    }

    private void setValue(Resource<ResultType> newValue) {
        if (results.getValue() != newValue) {
            results.setValue(newValue);
        }
    }

    // Called to save the result of the API response into the database.
    @WorkerThread
    protected abstract void saveCallResult(@NonNull RequestType item);

    // Called with the data in the database to decide whether to fetch
    // potentially updated data from the network.
    @MainThread
    protected abstract boolean shouldFetch(@Nullable ResultType data);

    // Called to get the cached data from the database.
    @NonNull
    @MainThread
    protected abstract LiveData<ResultType> loadFromDb();

    // Called to create the API call.
    @NonNull
    @MainThread
    protected abstract LiveData<ApiResponse<RequestType>> createCall();

    // Returns a LiveData object that represents the resource that's implemented
    // in the base class.
    public final LiveData<Resource<ResultType>> getAsLiveData() {
        return results;
    }

}

мой репозиторий выглядит так

@Singleton
public class GithubRepository {

    private GithubDao githubDao;
    private GithubTrendingApiService githubApiService;

    public GithubRepository(GithubDao githubDao, GithubTrendingApiService githubApiService) {
        this.githubDao = githubDao;
        this.githubApiService = githubApiService;
    }

    public LiveData<Resource<List<GithubEntity>>> getRepositories() {
        return new NetworkBoundResource<List<GithubEntity>, GithubApiResponse>(AppExecutors.getInstance()) {

            @Override
            protected void saveCallResult(@NonNull GithubApiResponse item) {
                List<GithubEntity> repositories = item.getItems();

                githubDao.insertRepositories(repositories);
            }

            @Override
            protected boolean shouldFetch(@Nullable List<GithubEntity> data) {
//                Timber.d("shouldFetch: repo: " + data.toString());
//                int currentTime = (int) (System.currentTimeMillis() / 1000);
//                Timber.d("shouldFetch: current time: " + currentTime);
//                int lastRefresh = data.getTimestamp();
//                Timber.d("shouldFetch: last refresh: " + lastRefresh);
//                Timber.d("shouldFetch: it's been " + ((currentTime - lastRefresh) / 60 / 60 / 24) +
//                        " days since this recipe was refreshed. 30 days must elapse before refreshing. ");
//                if ((currentTime - data.getTimestamp()) >= Constants.RECIPE_REFRESH_TIME) {
//                    Timber.d("shouldFetch: SHOULD REFRESH RECIPE?! " + true);
//                    return true;
//                }
//                Timber.d("shouldFetch: SHOULD REFRESH RECIPE?! " + false);
                return true;
            }


            @NonNull
            @Override
            protected LiveData<List<GithubEntity>> loadFromDb() {
                return githubDao.getTrendingRepository();
            }

            @NonNull
            @Override
            protected LiveData<ApiResponse<GithubApiResponse>> createCall() {
                return githubApiService.fetchTrendingRepositories();
            }

        }.getAsLiveData();
    }
}

Может кто-нибудь сказать мне, что я делаю неправильно, потому что Я получаю ошибку API? почему-то я не могу получить правильные данные, поэтому база данных также не заполняет

мой GithubAPi класс ответа это

public class GithubApiResponse {

    public GithubApiResponse() {
        this.items = new ArrayList<>();
    }

    public GithubApiResponse(List<GithubEntity> items) {
        this.items = items;
    }

    private List<GithubEntity> items;


    public List<GithubEntity> getItems() {
        return items;
    }

    public void setItems(List<GithubEntity> items) {
        this.items = items;
    }
}

и Entity класс это

@Entity
public class GithubEntity implements Parcelable {

    public GithubEntity(@NonNull Long id, String author, String name, String avatar,
                        String url, String description, Integer stars, Integer forks, Integer currentPeriodStars, String language, String languageColor) {
        this.id = id;
        this.author = author;
        this.name = name;
        this.avatar = avatar;
        this.url = url;
        this.description = description;
        this.stars = stars;
        this.forks = forks;
        this.currentPeriodStars = currentPeriodStars;
        this.language = language;
        this.languageColor = languageColor;
    }

    @NonNull
    @PrimaryKey
    private Long id;

    @SerializedName("author")
    @Expose
    private String author;
    @SerializedName("name")
    @Expose
    private String name;
    @SerializedName("avatar")
    @Expose
    private String avatar;
    @SerializedName("url")
    @Expose
    private String url;
    @SerializedName("description")
    @Expose
    private String description;
    @SerializedName("stars")
    @Expose
    private Integer stars;
    @SerializedName("forks")
    @Expose
    private Integer forks;
    @SerializedName("currentPeriodStars")
    @Expose
    private Integer currentPeriodStars;

    @SerializedName("language")
    @Expose
    private String language;
    @SerializedName("languageColor")
    @Expose
    private String languageColor;

    @NonNull
    public Long getId() {
        return id;
    }

    public void setId(@NonNull Long id) {
        this.id = id;
    }

    protected GithubEntity(Parcel in) {
        author = in.readString();
        name = in.readString();
        avatar = in.readString();
        url = in.readString();
        description = in.readString();
        if (in.readByte() == 0) {
            stars = null;
        } else {
            stars = in.readInt();
        }
        if (in.readByte() == 0) {
            forks = null;
        } else {
            forks = in.readInt();
        }
        if (in.readByte() == 0) {
            currentPeriodStars = null;
        } else {
            currentPeriodStars = in.readInt();
        }
        language = in.readString();
        languageColor = in.readString();
    }

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

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

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAvatar() {
        return avatar;
    }

    public void setAvatar(String avatar) {
        this.avatar = avatar;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getStars() {
        return stars;
    }

    public void setStars(Integer stars) {
        this.stars = stars;
    }

    public Integer getForks() {
        return forks;
    }

    public void setForks(Integer forks) {
        this.forks = forks;
    }

    public Integer getCurrentPeriodStars() {
        return currentPeriodStars;
    }

    public void setCurrentPeriodStars(Integer currentPeriodStars) {
        this.currentPeriodStars = currentPeriodStars;
    }


    public String getLanguage() {
        return language;
    }

    public void setLanguage(String language) {
        this.language = language;
    }

    public String getLanguageColor() {
        return languageColor;
    }

    public void setLanguageColor(String languageColor) {
        this.languageColor = languageColor;
    }


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

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(author);
        dest.writeString(name);
        dest.writeString(avatar);
        dest.writeString(url);
        dest.writeString(description);
        if (stars == null) {
            dest.writeByte((byte) 0);
        } else {
            dest.writeByte((byte) 1);
            dest.writeInt(stars);
        }
        if (forks == null) {
            dest.writeByte((byte) 0);
        } else {
            dest.writeByte((byte) 1);
            dest.writeInt(forks);
        }
        if (currentPeriodStars == null) {
            dest.writeByte((byte) 0);
        } else {
            dest.writeByte((byte) 1);
            dest.writeInt(currentPeriodStars);
        }
        dest.writeString(language);
        dest.writeString(languageColor);
    }
}

Может кто-нибудь направить меня, чтобы я мог решить эту проблему? застрял здесь на 2 дня

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...