I'm having an issue with Android Loaders.
I have an activity populated from internet data, and I have a bookmarks option to store and load them locally.
I'm implementing a recyclerView displaying the items.
When I change the sorting criteria the adapter gets cleared and repopulated with new data, and when I choose to see the bookmarked items a local query is started to the ContentProvider.
Now, I'm having issues with the bookmarked data, since I get multiple copies of the same item in my adapter.
I've done some logging and I noted that the loader is called multiple times when loading locally (adding the same items each time), but I can't see why.
Note that this occurs also when I get back to the activity, but does not occur when I start the app with the bookmarks preference.
If I start from bookmarks, select a bookmark and go back, multiple calls are done, too.
Can anyone help me? Here's the code:
In MainActivity this method is called at the end of onCreate
private void loadPosters() {
Log.d(TAG,"Loading posters");
if (mPagesLoaded < MAX_PAGES) {
Bundle args = new Bundle();
args.putInt("page",mPagesLoaded+1);
getSupportLoaderManager().restartLoader(LOADER_ID,args,this);
}
}
My loader code:
public Loader<ArrayList<Movie>> onCreateLoader(int id, final Bundle args) {
return new AsyncTaskLoader<ArrayList<Movie>>(this) {
ArrayList<Movie> mData;
#Override
protected void onStartLoading() {
Log.d(TAG,"Start Loading");
super.onStartLoading();
if (mData!=null){
deliverResult(mData);
}else{
if (mPagesLoaded == 0) {
mProgressBar.setVisibility(View.VISIBLE);
}
mErrorTextView.setVisibility(View.INVISIBLE);
forceLoad();
}
}
#Override
public ArrayList<Movie> loadInBackground() {
Log.d(TAG,"Load in background");
if (args.size() == 0) {
return null;
}
int page = args.getInt("page");
NetworkUtils networker = new NetworkUtils(getApplicationContext());
String criterion = getSharedPreferences(getString(R.string.movie_preferences), Context.MODE_PRIVATE).getString("sorting", "popular");
if (!(criterion.equals(getString(R.string.pref_bookmarked)))) {
URL request = networker.buildMoviesUrl(page, criterion);
try {
String JSONResponse = networker.getResponseFromHttpUrl(request);
ArrayList<Movie> res = fetchMoviesFromJson(JSONResponse);
mPagesLoaded++;
return res;
} catch (IOException | JSONException e) {
e.printStackTrace();
}
return null;
}
else{
Log.d(TAG,"Local Loading");
Cursor cursor = getContentResolver().query(MovieContract.MovieEntry.CONTENT_URI,null,null,null,null);
if (cursor!=null){
Log.d(TAG,"Cursor is not null");
ArrayList<Movie> res = fetchMoviesFromCursor(cursor);
cursor.close();
return res;
}
return null;
}
}
#Override
public void deliverResult(ArrayList<Movie> data) {
mData = data;
mProgressBar.setVisibility(View.INVISIBLE);
super.deliverResult(data);
}
};
}
My onLoadFinished callback:
#Override
public void onLoadFinished(Loader<ArrayList<Movie>> loader, ArrayList<Movie> movies) {
Log.d(TAG,"Load finished");
mProgressBar.setVisibility(View.INVISIBLE);
if (movies != null) {
mPostersAdapter.addMovies(movies);
Log.d(TAG,mPostersAdapter.getItemCount() + " items loaded");
showPosters();
} else {
showErrorMessage();
}
}
And my SharedPreferences code:
private void initSharedPreferences() {
mSharedPrefs = getApplicationContext().getSharedPreferences("movie_preferences", MODE_PRIVATE);
mOnSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
#Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Log.d(TAG, "Shared preferences for " + key + "changed. Pref: " + sharedPreferences.getString(key, null));
mPagesLoaded = 0;
mPostersAdapter.clear();
loadPosters();
}
};
mSharedPrefs.registerOnSharedPreferenceChangeListener(mOnSharedPreferenceChangeListener);
}
I had to call destoryLoader() on loader Manager, to solve this. Not sure if this is the right way..
Related
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.
I am fetching data from database. My views are updating only first time when I open the activity. Then when I again open the activity, my views are not updated.(Activity is starting again, hence onCreate() is called again & all settings are same). If I getText() after setting the text, I am getting proper values in log but nothing is displayed in view.
Here is my code snippet:
//My Call Back method
#Override
public void onRatingDataLoaded(ReviewJsonModel review) {
int ratingCount = 0, ownRating = 0;
String averageRating = "0";
if (review != null) {
ratingCount = review.review_count;
DecimalFormat format = new DecimalFormat("##.00");
averageRating = format.format(review.rating);
if (review.ownreviews != null) {
try {
ownRating = Integer.parseInt(review.ownreviews.rating);
} catch (NumberFormatException e) {
e.printStackTrace();
}
}
} else {
// do something
}
mTotalRatingCount.setText(String.format(getResources().getString(R.string.review_count), ratingCount));
mAverageRating.setText(averageRating);
// Log.v("LoggingReview", mTotalRatingCount.getText().toString().trim);
myRating.setRating(ownRating);
}
//Here I am setting listner as well as loading data.
public void loadReviewData(RatingDataLoadListener listener, int destinationId) {
if (mDataLoadListener == null)
mDataLoadListener = listener;
new getReviews().execute(destinationId);
}
Next is my asyntask
private class getReviews extends AsyncTask<Integer, Void, ReviewJsonModel> {
#Override
protected ReviewJsonModel doInBackground(Integer... integers) {
Cursor appCursor = mRatingApi.getDestinationReview(integers[0]);
ReviewJsonModel mReviewData = new ReviewJsonModel();
if (appCursor != null && appCursor.getCount() > 0) {
appCursor.moveToFirst();
while (!appCursor.isAfterLast()) {
mReviewData = getDocument(appCursor);
appCursor.moveToNext();
}
appCursor.close();
}
return mReviewData;
}
#Override
protected void onPostExecute(ReviewJsonModel result) {
super.onPostExecute(result);
if (mDataLoadListener != null)
mDataLoadListener.onRatingDataLoaded(result);
}
}
Can't find cause of problem. Any help is appreciated.
Looks like there is callback issue, can you please try below
public void loadReviewData(RatingDataLoadListener listener, int destinationId) {
mDataLoadListener = listener;
new getReviews().execute(destinationId);
}
I am relatively new to RxJava/RxAndroid. I have been using AsyncTask to do my long running tasks before now.
I have converted most of my AsyncTask to RxJava but this one.
The particular problem I am having is calling something like AsyncTask's publishProgress(params); in
the background thread. I need to do this to update the progress of a ProgressBar.
First this is the code in AsyncTask
private static class AddBooksToDatabase extends AsyncTask<String, String, String> {
//dependencies removed
AddBooksToDatabase(AddBooksDbParams params) {
//Removed assignment codes
}
#Override
protected String doInBackground(String... strings) {
//Initializing custom SQLiteOpenHelper and SQLite database
File mFile = new File(mFolderPath);
int booksSize = getFilesInFolder(mFile).size();
String[] sizeList = {String.valueOf(booksSize)};
//The first publishProgress is used to set the max of the progressbar
publishProgress(sizeList);
for (int i = 0; i < booksSize; i++) {
//publishProgress with current item, current file
publishProgress(String.valueOf(i), getFilesInFolder(mFile).get(i).getName());
//Inserting current items in database. Code removed
}
return null;
}
#Override
protected void onPreExecute() {
//Show ProgressBar
}
#Override
protected void onPostExecute(String s) {
//Hide ProgressBar
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
if (values.length == 1) {
//The first call to publishProgress
mProgressBar.setMax(Integer.parseInt(values[0]));
} else {
//Subsequent calls to publish progress
Log.i(TAG, "Current item is " + values[0] + " and current file is " + values[1]);
infoText.setText(values[1]);
mProgressBar.setProgress(Integer.parseInt(values[0]), true);
}
}
#Override
protected void onCancelled() {
cancel(true);
}
}
Code Using RxJava
final Observable<String[]> addBooksObserver = Observable.create(new Observable.OnSubscribe<String[]>() {
#Override
public void call(Subscriber<? super String[]> subscriber) {
subscriber.onNext(setAddSubscription());
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
private String[] setAddSubscription() {
S//Initializing custom SQLiteOpenHelper and SQLite database
File mFile = new File(mFolderPath);
int booksSize = getFilesInFolder(mFile).size();
String[] sizeList = {String.valueOf(booksSize)};
//The first publishProgress is used to set the max of the progressbar
addBooksObserver.doOnNext(addReturnParams(String.valueOf(sizeList.length), null, null));
for (int i = 0; i < booksSize; i++) {
EpubReader reader = new EpubReader();
//publishProgress with current item, current file*
addBooksObserver.doOnNext(addReturnParams(String.valueOf(sizeList.length),
String.valueOf(i), getFilesInFolder(mFile).get(i).getName()));
//Inserting current item in database. Code removed
}
return null;
}
private String[] addReturnParams(String totalItems, String currentItem, String currentFile) {
return new String[]{totalItems, currentItem, currentFile};
}
The problem is that lines addBooksObserver.doOnNext(addReturnParams( are displaying this error
doOnNext (rx.functions.Action1) cannot be applied to (java.lang.String[])
I don't know have any idea how to fix this because I thought that since setAddSubscription() and addReturnParams(String totalItems, String currentItem, String currentFile) are returning String array then this shouldn't be a problem. Please can you help me out?
you just have to pass the values to the onNext method of your subscriber, not the doOnNext method of your observable!
you also have to subscribe to the service. try something like this for your obserable:
Observable.create(new Observable.OnSubscribe<String[]>() {
#Override
public void call(Subscriber<? super String[]> subscriber) {
setAddSubscription(subscriber);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<String[]>() {
#Override
public void onCompleted() {
// handle 'oparation is done'
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String[] values) {
if (values.length == 1) {
//The first call to publishProgress
mProgressBar.setMax(Integer.parseInt(values[0]));
} else {
//Subsequent calls to publish progress
Log.i(TAG, "Current item is " + values[0] + " and current file is " + values[1]);
infoText.setText(values[1]);
mProgressBar.setProgress(Integer.parseInt(values[0]), true);
}
}
});
you also need to modify your private methods a little bit:
private void setAddSubscription(Subscriber<? super String[]> subscriber) {
//Initializing custom SQLiteOpenHelper and SQLite database
File mFile = new File(mFolderPath);
int booksSize = getFilesInFolder(mFile).size();
String[] sizeList = {String.valueOf(booksSize)};
//The first publishProgress is used to set the max of the progressbar
subscriber.onNext(addReturnParams(String.valueOf(sizeList.length), null, null));
for (int i = 0; i < booksSize; i++) {
EpubReader reader = new EpubReader();
//publishProgress with current item, current file*
subscriber.onNext(addReturnParams(String.valueOf(sizeList.length),
String.valueOf(i), getFilesInFolder(mFile).get(i).getName()));
//Inserting current item in database. Code removed
}
}
private String[] addReturnParams(String totalItems, String currentItem, String currentFile) {
return new String[]{totalItems, currentItem, currentFile};
}
You can use Subject to call onNext() manually like this:
Subject<Event> event = Subject.create();
Now call the onNext() for sending event like:
event.onNext("event");
Finally you can return Observable by using this code:
event.toFlowable(BackpressureStrategy.LATEST)
.toObservable();
Your Observer should be like Observable.create(new Observable.OnSubscribe<String>() & in your call method you should loop through the StringArray & pass it to onNext for example:
#Override
public void call(Subscriber<? super String> subscriber) {
for(String val : setAddSubscription()) {
subscriber.onNext(val);
}
subscriber.onCompleted();
}
now onNext shall return you individual items & onCompleted will be called upon the loop is finished.
Edit
myObserver.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
// handle completion.
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String value) {
// do whatever with each value passed to onNext
}
});
Observable.create(emitter -> {
for (int i = 0; i < 10; i++) {
int[] ii = {i, i * 2};
emitter.onNext(ii);
}
emitter.onComplete();
}).observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).subscribe(o -> {
// update progress
int[] i = (int[]) o;
Toast.makeText(SearchActivity.this, "oftad " + i[0] + " - " + i[1], Toast.LENGTH_SHORT).show();
}, t -> {
// on error
Toast.makeText(SearchActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}, () -> {
// progress tamom shod
Toast.makeText(SearchActivity.this, "completed", Toast.LENGTH_SHORT).show();
});
I'm trying to create a custom loader which loads an list of data that works fine but now I want to added endless scrolling in the listview. I thought a logical place would be in the loader since almost all the examples I see on the interwebz have a private field in the custom loader which corresponds with the data to be returned to UI and in the deliverResult there is some code like this
#Override
public void deliverResult(T data) {
T oldData = mData;
mData = data;
if (isStarted()) {
// If the loader is currently started, we can immediately deliver a result
super.deliverResult(mData);
}
}
No I thought that mData still contains the previous list [1,2,3,4,5] cause the loader should cache the data to show it instantaneously on configuration changes. And data is the new list [6,7,8,9,10] for instance. I could just add data to mData, mData.add(data) and we are done. Don't have to repeat the code on multiple places or different adapters. But seemingly this doesn't work, everytime you call restartLoader to load the new data the framework creates a new instance of the Loader. Has anyone else run into this problem before? or should I just do the mData.add(data) in the Adapter or somewhere else in the code.
the full implementation of the custom loader which extends ApiResponseLoader which can also be find below:
public class SearchLoader extends ApiResponseLoader {
private SearchType mSearchType;
private int mOffset;
private String mSearchQuery;
public SearchLoader(Context context, SearchType type, int offset, String query) {
super(context);
mSearchType = type;
mOffset = offset;
mSearchQuery = query;
}
#Override
public ApiResponse loadInBackground() {
try {
Map<String, String> parameters = Utils.parametersMap("q:" + mSearchQuery, "offset:" + String.valueOf(mOffset));
return tryLoadInBackground(parameters);
} catch (Exception e) {
setError(e);
return null;
}
}
public ApiResponse tryLoadInBackground(Map<String, String> parameters) throws Exception {
if (mSearchQuery == null) {
throw new NullPointerException("mSearchQuery should not be null");
}
if (mSearchType == SearchType.A) {
return RestAdapter().searchA(parameters);
} else {
return RestAdapter().searchB(parameters);
}
}
}
public abstract class ApiResponseLoader extends AsyncTaskLoader<ApiResponse> {
private final static String TAG = ApiResponseLoader.class.getSimpleName();
private ApiResponse mApiResponse;
private Exception mError;
public ApiResponseLoader(Context context) {
super(context);
}
public abstract ApiResponse tryLoadInBackground(Map<String, String> parameters) throws Exception;
#Override
protected void onStartLoading() {
if (mApiResponse != null) {
deliverResult(mApiResponse);
}
if (takeContentChanged() || mApiResponse == null) {
forceLoad();
}
}
#Override
protected void onForceLoad() {
super.onForceLoad();
}
#Override
protected void onStopLoading() {
cancelLoad();
}
#Override
public void onCanceled(ApiResponse data) {
// Attempt to cancel the current asynchronous load.
super.onCanceled(data);
}
#Override
protected void onReset() {
// Ensure the loader has been stopped.
onStopLoading();
// At this point we can release the resources associated with 'apps' if needed
if (mApiResponse != null) {
mApiResponse = null;
}
}
#Override
public void deliverResult(ApiResponse data) {
if (isReset()) {
// An async query came in while the loader is stopped. We don't need the result
if (data != null) {
onReleaseResources(data);
}
return;
}
if (mApiResponse != null) {
mApiResponse.mMeta = data.mMeta;
mApiResponse.mSampleList.addAll(data.mSampleList);
} else {
mApiResponse = data;
}
if (isStarted()) {
// If the loader is currently started, we can immediately deliver a result
super.deliverResult(mApiResponse);
}
}
public Exception getError() {
return mError;
}
public void setError(Exception mError) {
this.mError = mError;
}
}
I am having Pull to Refresh https://github.com/chrisbanes/Android-PullToRefresh as given in this link. Everything works fine. But when my list item finishes, the loading icon and pull to refresh label is still visible. So, how to disable the scrolling when end of list reached?
mainListView.setOnRefreshListener(new OnRefreshListener() {
#Override
public void onRefresh(PullToRefreshBase refreshView) {
String total_bk_count = subCategory .getTotal_Book_Count();
count_of_book = Integer.parseInt(total_bk_count);
listCountt = mainbooksAdpater.getCount();
Log.e("StroreActivity","Total book count---====----====---+"+count_of_book);
Log.e("StroreActivity","list Count---====----====---+"+listCountt);
if(listCountt < count_of_book)
{
int bookCount = Common.getBookCountNumber();
Common.setBookCount(bookCount+1);
String refresh_Pull_Url = Common.getUrlForeCategoryBooks(id, Common.NUMBER_OF_BOOKS_PER_REQUEST);
Log.e("Rathis to Check url", Common.getUrlForeCategoryBooks(id, Common.NUMBER_OF_BOOKS_PER_REQUEST));
PulltoRefreshAsync onCatBooksTaskScroll = new PulltoRefreshAsync(Common.getUrlForeCategoryBooks(id, Common.NUMBER_OF_BOOKS_PER_REQUEST));
onCatBooksTaskScroll.execute();
Log.e("StroreActivity","Total Book count::" + book_count_no);
}
else
{
mainListView.setMode(Mode.DISABLED);
Toast.makeText(getApplicationContext(), "end of list", Toast.LENGTH_SHORT).show();
}
}
});
Asynctask Class:
public class PulltoRefreshAsync extends AsyncTask<Object,Object,Object> {
int refreshCount;
String refresh_URL;
public PulltoRefreshAsync(String url) {
refresh_URL = url;
}
/*
* PulltoRefreshAsync(int i) { refreshCount = i; }
*/
#Override
protected void onPreExecute() {
super.onPreExecute();
Log.e("Checking Purpose", refresh_URL);
}
#Override
protected String doInBackground(Object... arg0) {
JsonParserRefresh jp = new JsonParserRefresh();
Log.e("StroreActivity","Array to String::" + refresh_URL);
String jsonString = jp.getJSONFromURL(refresh_URL);
Log.e("StroreActivity","JsonString::" + jsonString);
jsonParseForCategoryBooksGridScroll(jsonString);
return null;
}
#Override
protected void onPostExecute(Object result) {
super.onPostExecute(result);
/*
* if(mProgressDialog.isShowing()) { mProgressDialog.dismiss(); }
*/
final MainBooksAdapter mainbooksAdpater = new MainBooksAdapter(
StoreActivity.this, R.layout.aa, mainBooksList);
final int old_pos = mainListView.getRefreshableView()
.getFirstVisiblePosition() + 1;
mainListView.setAdapter(mainbooksAdpater);
tvvisiblebookCount.setText("" + mainbooksAdpater.getCount());
/*if(listCountt < count_of_book)
{
mainListView.setMode(Mode.DISABLED);*/
mainListView.post(new Runnable() {
#Override
public void run() {
mainListView.onRefreshComplete();
mainListView.getRefreshableView().setSelection(old_pos);
}
});
//}
mainbooksAdpater.notifyDataSetChanged();
}
}
For other people who might have similat issue:
you don't have to implement it this way
mainListView.post(new Runnable() {
#Override
public void run() {
mainListView.onRefreshComplete();
mainListView.getRefreshableView().setSelection(old_pos);
}
});
instead do just like this :
mainListView.onRefreshComplete();
one more thing I noticed, instead of saving the old pos value to get back to it, why not just use notifyDataSetChanged it leaves the position of the list the way it is, just try not to re-instanciate you list, i.e: mainBooksList = ..., instead try this:
mainBooksList.clear();
mainBooksList.addAll(YOUR DATA);
adapter.notifyDataSetChanged();
voila!
hope this helps someone