How to give RecyclerView Adapter new data from AsyncTaskLoader - android

My AsyncTaskLoader is loading data from a remote server. When new data arrives, naturally a call is made to onLoadFinished. At this point I don't know how to give the new data to the RecyclerView.Adapter. Calling notifyDataSetChanged does not give it the new data: it simply tells it there is new data. So any advice on how I might do this? Right now the best I can think of is to create my own setData method in my implementation of RecyclerView.Adapter as
public void setData(List<MyObject> data){
if(null != data && !data.isEmpty()){
synchronized(mItems){
mItems.clear();
mItems.addAll(data);
}
notifyDataSetChanged();
}
}
Is my idea the best there is? Or is there a more sound way of doing this?

Expose a public method in your adapter to update data.
For example, you could put it like this
public void updateItems(ArrayList<MyObject> myObjects) {
this.data = myObjects;
notifyDataSetChanged();
}

You have two options,
1. Re- instantiate your adapter with your new data and reset the adapter.
2. The way you do it.
I can not think of any other methods.

Ok, I had the same issue and here is the solution what I did.
1st I passed list object from onLoadFinished and in the RecyclerViewAdapter I have created method name setCardInfoList() and there I passed this object to global List object which I define in adapter class.
In onLoadFinished method..
#Override
public void onLoadFinished(android.content.Loader<List<Earthquake>> loader, List<Earthquake> earthquakes) {
if (earthquakes != null && !earthquakes.isEmpty()) {
adapter.setCardInfoList(earthquakes);
adapter.notifyDataSetChanged();
}else {
emptyView.setText("No Earthquake Found...");
}
}
Inside Adapter class
public void setCardInfoList(List<Earthquake> earthquakes){
this.earthquakeList = earthquakes;
}

Related

ArrayList Length gets 0 in Singleton

I am using a singleton for fetching data from a web service and storing the resulting data object in an ArrayList. It looks like this:
public class DataHelper {
private static DataHelper instance = null;
private List<CustomClass> data = null;
protected DataHelper() {
data = new ArrayList<>();
}
public synchronized static DataHelper getInstance() {
if(instance == null) {
instance = new DataHelper();
}
return instance;
}
public void fetchData(){
BackendlessDataQuery query = new BackendlessDataQuery();
QueryOptions options = new QueryOptions();
options.setSortBy(Arrays.asList("street"));
query.setQueryOptions(options);
CustomClass.findAsync(query, new AsyncCallback<BackendlessCollection<CustomClass>>() {
#Override
public void handleResponse(BackendlessCollection<CustomClass> response) {
int size = response.getCurrentPage().size();
if (size > 0) {
addData(response.getData());
response.nextPage(this);
} else {
EventBus.getDefault().post(new FetchedDataEvent(data));
}
}
#Override
public void handleFault(BackendlessFault fault) {
EventBus.getDefault().post(new BackendlessFaultEvent(fault));
}
});
}
public List<CustomClass> getData(){
return this.data;
}
public void setData(List<CustomClass> data){
this.data = data;
}
public void addData(List<Poster> data){
this.data.addAll(data);
}
public List<CustomClass> getData(FilterEnum filter){
if(filter == FilterEnum.NOFILTER){
return getData();
}else{
// Filtering and returning filtered data
}
return getData();
}
}
The data is fetched correctly and the list actually contains data after it. Also, only one instance is created, as intended. However, whenever I call getData later, the length of this.data is 0. Because of this I also tried it with a subclass of Application holding the DataHelper object, resulting in the same problem.
Is there a good way of debugging this? Is there something like global watches in Android Studio?
Is there something wrong with my approach? Is there a better approach? I am mainly an iOS developer, so Android is pretty new to me. I am showing the data from the ArrayList in different views, thus I want to have it present in an the ArrayList as long as the application runs.
Thanks!
EDIT: Example use in a list view fragment (only relevant parts):
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
filter = FilterEnum.NOFILTER;
data = DataHelper.getInstance().getData(filter);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
customClassListAdapter = new customClassListAdapter(getActivity(), data);}
EDIT2: Added code where I fetch the data from Backendless, changed reference of DataHelper to reference of data in first EDIT
EDIT3: I usa a local EventBus for notifying the list view about the new data. This looks like this and works (initially the data gets populated, but after e.g. applying a filter, the ArrayList I get with getData is empty):
#Subscribe
public void onMessageEvent(FetchedDataEvent event) {
customClassListAdapter.notifyDataSetChanged();
}
Try instead of keeping reference to your DataHelper instance, keeping reference to your list of retrieved items. F.e. when you first fetch the list (and it's ok as you say), assign it to a class member. Or itarate through it and create your own array list of objects for future use.
Okay I finally found the problem. It was not about the object or memory management at all. Since I give the reference on getData to my ArrayAdapter, whenever I call clear (which I do when changing the filter) on the ArrayAdapter, it empties the reference. I basically had to create a copy of the result for the ArrayAdapter:
data = new ArrayList<>(DataHelper.getInstance().getData(filter));
I was not aware of the fact that this is a reference at all. So with this the data always stays in the helper entirely. I only did this because this:
customClassListAdapter.notifyDataSetChanged();
does hot help here, it does not call getData with the new filter again.
Thanks everyone for your contributions, you definitely helped me to debug this.
It is likely that getData does get called before the data is filled.
A simple way to debug this is to add (import android.util.Log) Log.i("MyApp.MyClass.MyMethod", "I am here now"); entries to strategic places in fetchData, addData and getData and then, from the logs displayed by adb logcat ensure the data is filled before getData gets called.

Android ParseQueryAdapter notifyDataSetChanged does not work

Using latest Parse library v1.5.1
Thanks to the update now I can do:
ParseQueryAdapter<ParseObject> mAdapter = new ParseQueryAdapter<ParseObject>(MainActivity.this, new ParseQueryAdapter.QueryFactory<ParseObject>() {
#Override
public ParseQuery<ParseObject> create() {
ParseQuery<ParseObject> query = new ParseQuery<ParseObject>(ParseObject.class);
query.fromLocalDatastore();
return query;
}
});
mListView.setAdapter(mAdapter);
Now I have some pinned objects and they appear correctly, but when I unpin them like so:
//Some ParseObject in the above adapter
object.unpinInBackground(new DeleteCallback() {
#Override
public void done(ParseException e) {
if(e == null) {
//I beleive this would be the correct approach.
mAdapter.notifyDataSetChanged();
}
}
});
Naturally I want that item to disappear from the corresponding ListView, but it doesn't. But say I go back to a different activity and revisit this activity, the ListView is displayed properly without the recently unpinned object.
Is this a bug? If not what am I doing wrong?
I have the same problem) I solve it with invoke method ParseQueryAdapter.loadObjects().
You can try mAdapter.remove(object) before calling notifyDataSetChanged();
unpinInBackground removes the object from the database. Probably the adapter has a local copy of the object.
Looks like there is no remove method in ParseQueryAdapter.
Here is an response from official source:
Since a ParseQueryAdapter is designed to always show the results of a
ParseQuery, you would need to use an API request to reload the query.
https://www.parse.com/questions/delete-a-object-using-parsequeryadapter

How do I refresh a ListFragment from a Asynctask?

I am trying to refresh a ListFragment after Asynctask is finished. In my Asynctask doInBackground method I connect to a database and add elements to an ArrayList. In the onPostExecute method I want to access my ListFragment by something like this to call it's refreshData(method):
//REFRESH ARTISTFRAGMENT
#Override
public void onPostExecute(ArrayList<String> result) {
ArtistFragment artistFrag = new ArtistFragment();
artistFrag = (ArtistFragment) artistFrag.getSherlockActivity().getSupportFragmentManager().findFragmentById(R.id.frame_container);
if (artistFrag != null) {
artistFrag.refreshData(result);
}
}
But getSupportFragmentManager results in a NullPointerException!
The refreshData method in my Fragment looks like this:
public void refreshData(ArrayList<String> data) {
artists = new ArrayList<String>(data);
this.artistAdpater.notifyDataSetChanged();
}
I have found very similar approaches to do the exact thing I want but I can't find a solution to my problem. Basically it's done over here https://stackoverflow.com/a/16388650 - but it doesn't work for me like that.
Has anybody a solution for this or a workaround?
I am assuming that artists is the array list that feeds your listview.
artists = new ArrayList<String>(data);
What you are doing creates a new arraylist object reference to artists. But the list adapter has the older refrence. Thats why the list is not getting update.
Instead of passing the reference of the new arraylist, you can add it to the old one like so:
newArtistsArraylist = new ArrayList<String>(data);
artists .add(newArtistsArraylist );
Here is a nice solution to your problem.

AsyncTaskLoader doesn't call onLoadFinished unless I return a new object in loadInBackground

I have a AsyncTaskLoader<List<String>> with a member variable List<String> mAddresses. In loadInBackground, if I create a new object, fill it, and return it; onLoadFinished is called as expected, so I can update the adapter which in turn updates the view.
public List<String> loadInBackground()
{
// ListView updates properly only if I do this (strange?)
mAddresses = new ArrayList<String>();
// ... fill mAddresses ...
return mAddresses;
}
However, if I try to use the same variable even when it's value changes onLoadFinished does get called:
public List<String> loadInBackground()
{
// ListView does not update with this!
if(mAddresses == null)
{
mAddresses = new ArrayList<String>();
}
// ... fill mAddresses ...
return mAddresses;
}
Can anyone comment on this? Am I supposed to create a new object to return each time like this? Is there any flag I can set that says "the object has changed, so even though it is the same object, make sure all of the updates are performed?
If the data is already loaded (as it seems in your 'mAddresses' variable which is in the Loader) you need to call forceLoad() to get it to load again:
http://developer.android.com/reference/android/content/Loader.html#forceLoad%28%29
Edit:
...after some investigation...
To answer your original question, unhelpfully: 'because it doesn't'!
From the source for LoaderManager:
// Notify of the new data so the app can switch out the old data before
// we try to destroy it.
if (mData != data || !mHaveData) {
mData = data;
mHaveData = true;
if (mStarted) {
callOnLoadFinished(loader, data);
}
}
http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/4.3_r2.1/android/app/LoaderManager.java#LoaderManagerImpl.LoaderInfo.onLoadComplete%28android.content.Loader%2Cjava.lang.Object%29
RE your problem, I'm assuming that you're continually adding to the List in loadInBackground() rather than clearing and starting again.
The cleanest way is to build a new ArrayList each time by passing in the existing List as an argument to the ArrayList() constructor, it will copy the contents.
Or you could override deliverResult(), it's called from the main thread so modifying mAdapter is OK.
Or forget the Loader and just use a much simpler AsyncTask... although that depends on where your data actually comes from.
try to override onStartLoading and call forceLoad()
#Override
protected void onStartLoading() {
super.onStartLoading();
forceLoad();
}
#Override
protected void onStopLoading() {
super.onStopLoading();
cancelLoad();
}

update listview dynamically with adapter

This tutorial uses a SimpleAdapter which works fine, but I need to update the arrays in the adapter when new data is entered.
Could you please guide me on how to update a ListView using something other than a SimpleAdapter?
Use a ArrayAdapter backed by an ArrayList. To change the data, just update the data in the list and call adapter.notifyDataSetChanged().
If you create your own adapter, there is one notable abstract function:
public void registerDataSetObserver(DataSetObserver observer) {
...
}
You can use the given observers to notify the system to update:
private ArrayList<DataSetObserver> observers = new ArrayList<DataSetObserver>();
public void registerDataSetObserver(DataSetObserver observer) {
observers.add(observer);
}
public void notifyDataSetChanged(){
for (DataSetObserver observer: observers) {
observer.onChanged();
}
}
Though aren't you glad there are things like the SimpleAdapter and ArrayAdapter and you don't have to do all that?
SimpleListAdapter's are primarily used for static data! If you want to handle dynamic data, you're better off working with an ArrayAdapter, ListAdapter or with a CursorAdapter if your data is coming in from the database.
Here's a useful tutorial in understanding binding data in a ListAdapter
As referenced in this SO question
Most people recommend using notifyDataSetChanged(), but I found this link pretty useful. In fact using clear and add you can accomplish the same goal using less memory footprint, and more responsibe app.
For example:
notesListAdapter.clear();
notes = new ArrayList<Note>();
notesListAdapter.add(todayNote);
if (birthdayNote != null) notesListAdapter.add(birthdayNote);
/* no need to refresh, let the adaptor do its job */
I created a method just for that. I use it any time I need to manually update a ListView. Hopefully this gives you an idea of how to implement your own
public static void UpdateListView(List<SomeObject> SomeObjects, ListView ListVw)
{
if(ListVw != null)
{
final YourAdapter adapter = (YourAdapter) ListVw.getAdapter();
//You'll have to create this method in your adapter class. It's a simple setter.
adapter.SetList(SomeObjects);
adapter.notifyDataSetChanged();
}
}
I'm using an adapter that inherites from BaseAdapter. Should work for any other type of adapter.

Categories

Resources