How to fix load duplicate data in Android - android

I want load data from server and show into RecyclerView, for connection library i use Retrofit. and in my application i check internet connection! if connected load data else show another layout.
My codes:
boolean isConnected = ConnectivityReceiver.isConnected();
...
private void loadDataCheckNet() {
loadData(isConnected);
}
private void loadData(boolean isConnect) {
if (isConnect) {
Retrofit_ApiInterface apiInterface = Retrofit_ApiClient.getClient().create(Retrofit_ApiInterface.class);
Call<R_SearchModelResponse> call = apiInterface.getSearchResponse(searchText);
call.enqueue(new Callback<R_SearchModelResponse>() {
#Override
public void onResponse(Call<R_SearchModelResponse> call, Response<R_SearchModelResponse> response) {
if (response != null) {
models.addAll(response.body().getPosts());
mAdapter.notifyDataSetChanged();
if (mAdapter.getItemCount() > 0) {
search_recycler.setAdapter(mAdapter);
searchLoadLayout.setVisibility(View.GONE);
} else {
empty_layout.setVisibility(View.VISIBLE);
searchLoadLayout.setVisibility(View.GONE);
}
} else {
searchLoadLayout.setVisibility(View.VISIBLE);
TastyToast.makeText(context, "Error", TastyToast.LENGTH_LONG, TastyToast.ERROR);
}
mAdapter.notifyDataSetChanged();
searchCheckNet.setVisibility(View.GONE);
}
#Override
public void onFailure(Call<R_SearchModelResponse> call, Throwable t) {
Log.e("CatResponseError", "Error : " + t);
}
});
} else {
searchCheckNet.setVisibility(View.VISIBLE);
if (mAdapter != null) {
mAdapter.clear();
search_recycler.setAdapter(mAdapter);
}
searchCheckNetButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mAdapter.notifyDataSetChanged();
loadDataCheckNet();
}
});
}
}
When running application and isConnected to internet application is ok, load data very good! but when disconnect internet and connect again, load data from server and again duplicate is!
For example : if you have 3 post, post#1,post#2,post#3 when connected to internet load again this three posts! post#1,post#2,post#3,post#1,post#2,post#3 .
How can i fix this problem? Thanks all <3

You just add this models.clear(); into your public void onResponse method.
#Override
public void onResponse(Call<R_SearchModelResponse> call, Response<R_SearchModelResponse> response) {
if (response != null) {
models.clear();
models.addAll(response.body().getPosts());
mAdapter.notifyDataSetChanged();
if (mAdapter.getItemCount() > 0) {
search_recycler.setAdapter(mAdapter);
searchLoadLayout.setVisibility(View.GONE);
} else {
empty_layout.setVisibility(View.VISIBLE);
searchLoadLayout.setVisibility(View.GONE);
}

You're just adding items to your models container, which I assume is some sort of list, you yes, you will have duplicates. You should clear your list before adding new items to it.

Of course, the data is duplicated because you added new data into the list without clearing old data.
One simple fix is, add this line:
models.clear();
before this line:
models.addAll(response.body().getPosts());
By the way, you should set the RecycleView adapter right after you initialized the adapter in onCreate(), there is no point in setting the same adapter to the RecycleView over and over again every time the data is Updated.

UPDATE
Define a Method
public void clearData() {
//for remving list data
int size = this.models.size();
if (size > 0) {
for (int i = 0; i < size; i++) {
this.models.remove(i);
}
this.notifyItemRangeRemoved(0, size);
}
}
call the method before like this
clearData();
models.addAll(response.body().getPosts());

Related

Cannot modify managed objects outside of a write transaction

I must push a object just with one note and to push his media files, but it do not want to do this. I do not know why is difference , but if i do this without changing notes its work fine , but I need to send just changed notes , please help. What is my problem? I need to understand why this happen
private void pushMediaFiles(Task task,Note newNote, PushTaskListener listener)
{
PushTaskModel pushTaskModel=new PushTaskModel(Realm.getDefaultInstance().copyFromRealm(task));
pushTaskModel.notes.clear();
Note oldNote = new Note();
oldNote.update(newNote);
pushTaskModel.notes.add(oldNote);
service.pushData(pushTaskModel)
.map(taskResponse ->
{
ArrayList<MediaFile> filesToSave = new ArrayList<>();
for (Note note : task.getNotes())
{
for (MediaFile mediaFile : note.getMediaFiles())
{
if (mediaFile.hasChanges() && mediaFile.getFileAbsolutePath() != null)
{
filesToSave.add(Realm.getDefaultInstance().copyFromRealm(mediaFile));
}
}
}
return filesToSave;
})
.subscribe(taskMediaFiles ->
{
Log.d("PUSH_TASK_EVENT","3 GOOD");
if (taskMediaFiles.size() == 0)
{
listener.onSuccess();
return;
}
for (MediaFile mediaFile: taskMediaFiles)
{
saveMediaFile(mediaFile, new OnResponseListener()
{
#Override
public void onStart()
{
}
#Override
public void onComplete(boolean successfully)
{
listener.onSuccess();
}
#Override
public void onError(Throwable throwable)
{
listener.onError(throwable);
}
});
}
});
}

Why is my ContentResolver batch operation only putting in 7 out of 21 entries?

I have three fragments in a FragmentPagerAdapter, and each of them would fetch a list of frames/data from a server using Volley. This data would later be used to update the Fragment's RecyclerView Adapter as a Cursor.
VolleyRestClientUtils.get(getString(R.string.PATH_SHOP), LOG_TAG, params, true, false, new JsonHttpResponseHandler() {
public void onSuccess(JSONObject response) {
Log.d(LOG_TAG, "request Response : " + response.toString());
try {
String status = response.getString("status");
if (RestClientUtils.STATUS_OK.equals(status)) {
final JSONArray frames = response.getJSONArray("items");
Log.d(LOG_TAG, "request Response : " + frames.length());
if (frames != null && frames.length() > 0) {
new AsyncTask<Void, Void, Boolean>() {
#Override
protected Boolean doInBackground(Void... voids) {
List<ContentValues> listShopFrame = ShopFrame.fromJSONArray(frames, sort);
if (listShopFrame.size() > 0 && isActivityActive()) {
ContentResolver cr = getActivity().getContentResolver();
if (!isRequestMore) {
cr.delete(ShopFrame.CONTENT_URI, ShopFrame.COLUMN_CATEGORY + "=?",
new String[]{sort});
paramSkip = frames.length();
} else {
paramSkip += frames.length();
}
ArrayList<ContentProviderOperation> operations = new ArrayList<>();
String log = listShopFrame.size()+" ";
for (int i = 0; i < listShopFrame.size(); i++) {
operations.add(ContentProviderOperation
.newInsert(ShopFrame.CONTENT_URI)
.withValues(listShopFrame.get(i))
.build());
log += listShopFrame.get(i).toString()+"\n";
}
Log.i("loader_callback_"+sort, log);
//cr.applyBatch(ShopFrame.CONTENT_AUTHORITY, operations);
ContentValues[] opsAsArray = new ContentValues[listShopFrame.size()];
listShopFrame.toArray(opsAsArray);
cr.bulkInsert(ShopFrame.CONTENT_URI, opsAsArray);
//return true;
}
return true;
}
#Override
protected void onPostExecute(Boolean result) {
dataRefreshed = true;
Log.i("loader_callback_"+sort, "response post execute");
if (result) {
loadSucceed();
PicMixApp.getInstance().setRefreshed(ShopFrameFragment.this.getClass().getName());
} else {
loadFailed(null);
}
}
}.execute();
} else {
//TODO
//Handle error
loadFailed(getString(R.string.err_load_s, getString(R.string.frame)));
}
} else if (VolleyRestClientUtils.STATUS_RESOURCE_NOT_FOUND.equals(status)) {
hasMore = false;
loadSucceed();
} else {
loadFailed(getString(R.string.err_load_s, getString(R.string.frame)));
}
} catch (Exception e) {
Log.e(LOG_TAG, "Exception:" + e.getMessage());
loadFailed(getString(R.string.err_load_s, getString(R.string.frame)));
}
}
#Override
public void onJSONError(String responseString) {
loadFailed(getString(R.string.err_load_s, getString(R.string.frame)));
}
#Override
public void onFailure(String errMessage, int statusCode, Map<String, String> headers, byte[] responseBytes) {
loadFailed(getString(R.string.err_load_s, getString(R.string.frame)));
}
});
Whereas loadSucceed() has this following code:
if (this.recyclerView != null) {
final RecyclerView.Adapter adapter = recyclerView.getAdapter();
if (adapter != null) {
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
#Override
public void onChanged() {
super.onChanged();
Log.i(DefaultRecyclerFragment.this.getClass().getName(), "onChanged");
adapter.unregisterAdapterDataObserver(this);
isLoading = false;
}
public void onItemRangeRemoved(int positionStart, int itemCount) {
Log.i(DefaultRecyclerFragment.this.getClass().getName(), "onItemRangeRemoved:" + positionStart + ", itemcount:" + itemCount);
adapter.unregisterAdapterDataObserver(this);
isLoading = false;
}
});
if (adapter instanceof CursorRecyclerAdapter && loadMoreView != null) {
((CursorRecyclerAdapter) adapter).removeFooter(loadMoreView);
}
}
}
I've put the code to initialize the loader in the onResume() method of each fragment:
int id = 100+Integer.valueOf(sort);
Loader l = getLoaderManager().getLoader(id);
Log.i("loader_callback_"+sort, "success loading volley "+l);
if(l == null) {
getLoaderManager().restartLoader(id, null, this);
}
My problem is that there seems to be some sort of race condition happening, that the currently viewed fragment's adapter seem to be updated twice, and sometimes thrice. The initial cursor fetched by the Fragment's Loader has 10 rows, sure, but after the update, most of the time it only has 7 of the 21 rows expected to be put in.
I thought all the ContentResolvers' operations are synchronous (can only be done one after another, not simultaneously). What's going on here?
EDIT: Should I just put the loader init code in the loadSuccess() callback?
EDIT2: I should note that these Fragments extend android.support.v4.app.Fragment, and I'm using the version 27.1.1 of the Support Library.

Retrofit 2 error response handling

I have a method that downloads api data. But I get response with empty data, so I need to check if data is correct.
Call the method:
try {
fetchChartData();
} catch (EmptyResponseException e) {
view.showError();
}
And fetch data:
public void fetchChartData() throws EmptyResponseException {
Call<ChartModel> dailyChartCall = ... //some call
dailyChartCall.enqueue(new Callback<ChartModel>() {
#Override
public void onResponse(Call<ChartModel> call, Response<ChartModel> response) {
//EMPTY
if(response.body().getData().size() == 0){
<<-- THROW HERE
} else ...
How should I implement exception and pass it to the top?
Simple 'throw new EmptyResponseException' is not working as onResponse is anonymous class.
I assume that you are designing your app using MVP structure.
So instead of catching exception, you can do
public void fetchChartData(){
Call<ChartModel> dailyChartCall = ... //some call
dailyChartCall.enqueue(new Callback<ChartModel>() {
#Override
public void onResponse(Call<ChartModel> call, Response<ChartModel> response) {
//EMPTY
if(response.body().getData().size() == 0){
***presenter.showError();***
} else ...
{
>>>> do your logic here <<<<
presenter.showChartData();
}
Which in presenter showError and showChartData you can call corresponding view function.

eventbus inside recursive function

i am posting event using EventBus inside a recursive function that fetches pagination data from the webservice.
public void getCallsData(final UserRequest userRequest){
serviceCall.enqueue(new Callback<UserResponseInfo>() {
#Override
public void onResponse(Call<UserResponseInfo> call, Response<UserResponseInfo> response) {
if(response.isSuccessful()) {
UserResponseInfo userResponseInfo = response.body();
if (userResponseInfo != null) {
try {
Log.e(TAG, "getCallsData response " + LoganSquare.serialize(userResponseInfo));
} catch (IOException e) {
e.printStackTrace();
}
int currentPage = userRequest.getUserRequestInfo().get(0).getPage();
int totalPages = userResponseInfo.getTotalPages();
if(currentPage < totalPages){
userRequest.getUserRequestInfo().get(0).setPage(++currentPage);
Log.e(TAG, "getCallsData fetching next page "+currentPage);
userResponseInfo.setCurrentPage(currentPage);
userResponseInfo.setRequestType(GET_CALL_REQUEST);
EventBus.getDefault().postSticky(userResponseInfo);
getCallsData(userRequest);
}
} else {
}
}else{
}
}
#Override
public void onFailure(Call<UserResponseInfo> call, Throwable t) {
}
});
}
The issue is EventBus.getDefault().postSticky(userResponseInfo); when getCall executes in recursive fashion its not posting the event properly as in only first event is getting called and it misses the last one.

Retrofit Periodic call with Pagination

I am currently using the Retrofit2.0 to poll the server .I am getting the result in x second but the problem is page number is not updating in the API.Lets come to the code for better clarification
private void startPolling() throws Exception {
Log.e("APP CONSTANT","" + current_page);
MrSaferWebService service = ServiceFactory.createRetrofitService(MrSaferWebService.class, AppConstants.BASE_URL);
final Observable<ReportResponse> reportResponseObservable = service.getListOfInciden("get_report", current_page, 5, incident_lat, incident_long);
Observable.interval(0,20,TimeUnit.SECONDS)
.flatMap(new Func1<Long, Observable<ReportResponse>> () {
#Override
public Observable<ReportResponse> call(Long aLong) {
return reportResponseObservable;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<ReportResponse>() {
#Override
public void call(ReportResponse response) {
Log.i("HEARTBEAT_INTERVAL", "Response from HEARTBEAT");
ActivityUtils.showProgress(false, mRootView, mProgressView, mContext);
if (response.getStatus() == 1) {
current_page = current_page + 1;
if (!response.getReportList().isEmpty()) {
addItems(response.getReportList());
}
else{
//do nothing
}
} else {
Log.e("MY ERROR", "" + "SOME ERROR OCCURED");
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
ActivityUtils.showProgress(true, mRootView, mProgressView, mContext);
// TODO: 22/03/16 ADD ERROR HANDLING
}
});
}
As you can see i have incremented the current_page by 1 every time on
SuccessFull Response but when i check the Log the current_page value are increased only once and after that log are not there and hence there value is also not increasing..So it taking the the same page number every time and giving me the Duplicate response.
Please help me to find what i am missing.
After spending more than a day i just changed Action with Subscriber and everything seems to be working .I don't know what happen internally but it works . I am still trying to figure it out what the difference between Action and Subscriber.
Below are my updated code which did the tricks.
private void startPolling() throws Exception {
final MrSaferWebService service = ServiceFactory.createRetrofitService(MrSaferWebService.class, AppConstants.BASE_URL);
Observable
.interval(0,20,TimeUnit.SECONDS)
.flatMap(new Func1<Long, Observable<ReportResponse>>() {
#Override
public Observable<ReportResponse> call(Long aLong) {
Log.e("PAGE", "" + current_page);
return service.getListOfInciden("get_report", current_page, 5, incident_lat, incident_long);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<ReportResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
if (mProgressView !=null && mRootView !=null) {
ActivityUtils.showProgress(false, mRootView, mProgressView, mContext);
}
}
#Override
public void onNext(ReportResponse response) {
if (mProgressView !=null && mRootView !=null) {
ActivityUtils.showProgress(false, mRootView, mProgressView, mContext);
}
if (response.getStatus() == 1) {
if (!response.getReportList().isEmpty()){
current_page = current_page + 1;
addItems(response.getReportList());
}
else{
//do nothing
}
} else {
Log.e("MY ERROR", "" + "SOME ERROR OCCURED");
}
}
});
}

Categories

Resources