RecyclerView not updating after AsyncTask - java

I have a Fragment with a RecyclerView. My fragment calls an AsyncTask, and after the request is completed the task returns to my Fragment which implements a processFinish method. in my processFinish I reference a global RV object and try to updated its' content
#Override
public void processFinish(String output, String id) {
switch (id){
case "exchange": {
Gson gson = new Gson();
JsonReader reader = new JsonReader(new
InputStreamReader( new ByteArrayInputStream(output.getBytes()) ));
Type type = new TypeToken<ExchangeResponse>(){}.getType();
ExchangeResponse data = gson.fromJson(reader, type);
homeRV.refreshDrawableState();
// This add doesn't work...
((DisplayableAdapter)homeRV.getAdapter()).addItem(new ExchangeDisplayable(data));
}
}
}
This is my addItem method.
public void addItem(Displayable d){
this.results.add(d);
this.notifyItemInserted(results.size() - 1);
this.notifyDataSetChanged();
}
If I update the adapter from onViewCreated it works well. But not from processFinish. I stopped the debugger right after adding the item, and it seems like the reference is not lost (like others with a similar issue posted). The size of my dataset is changed, but visually nothing happens.
EDIT - I've added my onViewCreated where homeRV is defined, and my onPostExecute.
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
homeRV = getView().findViewById(R.id.rv_home);
homeRV.setHasFixedSize(true);
homeRV.addItemDecoration(new DividerItemDecoration(homeRV.getContext(), DividerItemDecoration.VERTICAL));
RecyclerView.LayoutManager layoutManager = new LinearLayoutManager(this.getContext());
homeRV.setLayoutManager(layoutManager);
homeRV.setAdapter(new DisplayableAdapter(new ArrayList<Displayable>()));
// This add works...
((DisplayableAdapter)homeRV.getAdapter()).addItem(new HomeDisplayable("Heads up", "this is where Heads Up info will be stored"));
getExchangeRates();
}
In AsyncTask:
#Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
response.processFinish(result, identifier);
}

Remove homeRV.setHasFixedSize(true); and see if it works. It is not updating width/height of recyclerview because of this line so you can't see new items.
More information on how setHasFixedSize works can be found here.

Related

Using multithreading in rxjava2 but it didn't pass to onSuccess or onError

I am new to RxJava in android and trying to call method to query the SQLite. When using the same thread it successfull to pass the query result to the view. But, when i try to using different trhead by using subscribeOn() it doesn't work. How to fix this?
public void fetchNaskah() {
compositeDisposable.add(naskahRepository.getNaskah()
.subscribeOn(Schedulers.io())
.observeOn(mainScheduler)
.subscribeWith(new DisposableSingleObserver<List<NaskahDao>>() {
#Override
public void onSuccess(List<NaskahDao> naskahDaos) {
if (naskahDaos.isEmpty()) {
mainView.setFetchNaskahListError(true);
} else {
// Collections.sort(naskahDaos1);
mainView.updateNaskahList(naskahDaos);
mainView.setFetchNaskahListError(false);
}
}
#Override
public void onError(Throwable e) {
mainView.setFetchNaskahListError(true);
}
}));
}
rxJavaVersion = 2.1.16
rxAndroidVersion = 2.0.2
#Override
protected void onCreate(Bundle savedInstanceState) {
...
rvListNaskah.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
databaseNaskah = new NaskahDbImpl(getApplication());
mainPresenter = new MainPresenter(this, databaseNaskah,
AndroidSchedulers.mainThread());
listNaskah = new ArrayList<>();
mainPresenter.fetchNaskah();
if(!listNaskah.isEmpty()) {
rvNaskahAdapter = new MainNaskahAdapter(this, listNaskah);
rvListNaskah.setAdapter(rvNaskahAdapter);
rvListNaskah.setLayoutManager(mLayoutManager);
}
}
I try to debug it again and found that it called the onSuccess, but it didn't update the RecyclerView
Debugger screen shot
this is the Activity method
#Override
public void updateNaskahList(List<NaskahDao> naskahDaos) {
listNaskah = naskahDaos;
Log.d("naskahDaos", naskahDaos.toString());
rvNaskahAdapter = new MainNaskahAdapter(this, listNaskah);
rvNaskahAdapter.notifyDataSetChanged();
rvListNaskah.setAdapter(rvNaskahAdapter);
}
Solved
This is because of fetchNaskah call getNaskah in a different thread from UI Thread it should be set the recyclearview first.
#Override
protected void onCreate(Bundle savedInstanceState) {
...
rvListNaskah.setHasFixedSize(true);
mLayoutManager = new LinearLayoutManager(this);
databaseNaskah = new NaskahDbImpl(getApplication());
mainPresenter = new MainPresenter(this, databaseNaskah,
AndroidSchedulers.mainThread());
listNaskah = new ArrayList<>();
mainPresenter.fetchNaskah();
// if(!listNaskah.isEmpty()) {
// rvNaskahAdapter = new MainNaskahAdapter(this, listNaskah);
// rvListNaskah.setAdapter(rvNaskahAdapter);
// rvListNaskah.setLayoutManager(mLayoutManager);
// }
rvListNaskah.setLayoutManager(mLayoutManager);
}
The if line i commented above will work if the fetchNaskah call getNaskah in syncronous. So i just commented it and set the layout manager without checking the list if it's empty because the list will come later.
My guess is that compositeDisposable was already disposed, and thus disposes the new observable when it is added.
Then without the subscribeOn it would work because it synchronously finishes the observable, meaning it finishes before CompositeDisposable.add is ever called. This means it is able to finish before it gets disposed.

Assigned a ArrayList in AsyncTask, but the change discard afterwords

I'm writing a simple program to request a JOSN request of a list of earthquakes to display for users. I use Asynctask to put the request in the background thread and use an ArrayList Adaptor to display the relevant information. I declare an empty ArrayList and then extract the JOSN request and put them in a temporary list and then assign the temporary list to the empty ArrayList.
I use a debugger tool to see that in the updateEarthquakeList method. I set the break point in the updateEarthquakeList method. this.earthquak and earthquakes both have 10 elements. Pics are as follow:
But when I set the break point after task.execute(USGS_REQUEST_URL) in the onCreate method, I got this:
As the pics shown after execute the AsyncTask the ArrayList is empty. But inside the AsyncTask The array was actually updated. (To do a little experiment I create an int haha as 0 and change it to 1 in the Asynctask, but it changed back to 0 afterwards)
How is this happen and how do I supposted to make it right?
public class EarthquakeActivity extends AppCompatActivity {
public static final String LOG_TAG = EarthquakeActivity.class.getName();
ArrayList<Earthquake> earthquak = new ArrayList<Earthquake>();
int haha = 0;
private static final String USGS_REQUEST_URL = "http://earthquake.usgs.gov/fdsnws/event/1/query?format=geojson&eventtype=earthquake&orderby=time&minmag=6&limit=10";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.earthquake_activity);
EarthquakeAsyncTask task = new EarthquakeAsyncTask();
task.execute(USGS_REQUEST_URL);
// Create a fake list of earthquake locations.
// Find a reference to the {#link ListView} in the layout
ListView earthquakeListView = (ListView) findViewById(R.id.list);
// Create a new {#link ArrayAdapter} of earthquakes
EarthquakeAdapter adapter = new EarthquakeAdapter(this, earthquak);
// Set the adapter on the {#link ListView}
// so the list can be populated in the user interface
earthquakeListView.setAdapter(adapter);
//OPEN a web page of a specific when textview is clicked.
earthquakeListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Intent intent = new Intent(Intent.ACTION_VIEW, earthquak.get(position).getUrl());
if (intent.resolveActivity(getPackageManager()) != null) {
startActivity(intent);
}
}
});
}
private void updateEarthquakeList(ArrayList<Earthquake> earthquake) {
this.earthquak = earthquake;
haha = 1;
}
private class EarthquakeAsyncTask extends AsyncTask<String, Void, ArrayList<Earthquake>> {
#Override
protected ArrayList<Earthquake> doInBackground(String... urls) {
if (urls.length < 1 || urls[0] == null) {
return null;
}
ArrayList<Earthquake> earthquakes = QueryUtils.fetchEarthquakeData(urls[0]);
return earthquakes;
}
#Override
protected void onPostExecute(ArrayList<Earthquake> earthquakes) {
updateEarthquakeList(earthquakes);
}
}

Spinner in Custom Function for Android with java

I am confused about calling the spinner widget through a custom function. I am create an app in which I use spinner 20-35 times a spinner widget in single layout or activity. So for this i want to avoid the spinner code repetition again and again. i am creating a method for this i add the items to the spinner but i want to pass item value on select to other activity which bind to that class
Here is my code
Spin_tester.class
public class Spin_tester {
public String result;
public Context ctx;
public Spin_tester(Spinner spinner, final ArrayList<String> arraylist, final Context ctx , final String value) {
this.ctx= ctx;
ArrayAdapter<String> adpts =
new ArrayAdapter<String>(ctx, android.R.layout.simple_dropdown_item_1line,arraylist);
spinner.setAdapter(adpts);
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
result = arraylist.get(position);
value = result ; // This is not working
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
}
}
Test_Activity.class
public class Test_Activity extends AppCompatActivity {
ArrayList<String> data_list = new ArrayList<>();
Spinner spins;
String value;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
spins = (Spinner)findViewById(R.id.spinner);
data_list.add("1");
data_list.add("2");
data_list.add("3");
data_list.add("4");
Spin_tester asd = new Spin_tester(spins,data_list,this,value);
TextView txt = (TextView)findViewById(R.id.textView17);
}
}
Please help
Thanks in advance
Java is a pass-by-value language, not pass-by-reference. What this means is that setting value in setOnItemSelectedListener will only change value within that method — the result won't be passed back anywhere.
I see that you've place the result in result. That is where the calling program will find the answer.
Remove all instances of value from Spin_tester and Test_Activity and then have your main activity get the result from asd.result
I'm going to get a little meta at this point: I've only answered the question you actually asked, but this code is wrong on so many levels that you're never going to get it to work. I strongly suggest you work your way through the examples and tutorials in the documentation before you try to proceed any further.

The recyclerview crashes when I update it

Please don't mark it as duplicate, as the solutions provided yesterday did not work.
I have populated a recyclerview, and it is working fine, albeit it loads a bit slowly.
However, say when I add a new name and update the recyclerview it crashes with the NullPointerException.
Below are the classes and code:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.mainOnes);
recyclerView = (RecyclerView) findViewById(R.id.recycleOnes);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setHasFixedSize(true);
adapter = new MusicRecyclerAdapter(list);
recyclerView.setAdapter(adapter);
populateList();
}
public void populateList(){
DisplayUsersList displayUsersList = new DisplayUsersList();
displayUsersList.execute();
}
public class DisplayUsersList extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
...
}
#Override
protected void onPostExecute(String postData) {
try {
list.clear();
JSONArray jsonArray = new JSONArray(postData);
for(int i=0; i < jsonArray.length(); i++){
String forename = jsonArray.getJSONObject(i).getString("forename");
String surname = jsonArray.getJSONObject(i).getString("surname");
UserDetails userDetails = new UserDetails(forename, musicName, surname);
list.add(userDetails);
}
adapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
}
}
public class AddUsers extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
...
}
#Override
protected void onPostExecute(String postData) {
populateList();
}
}
I think it is crashing because of the populateList() method call because I have already initialised everything
My logcat:
java.lang.NullPointerException
at georgezs.userdas.UserDBGUI$DisplayUsersList.onPostExecute(UserDBGUI.java:218)
at android.os.AsyncTask.finish(AsyncTask.java:741)
at android.os.AsyncTask.access$600(AsyncTask.java:197)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:654)
at android.os.Handler.dispatchMessage(Handler.java:100)
Line 218 is adapter.notifyDataSetChanged();
I tried instantiating the recyclerview in onPostExecute but it keeps crashing. My code works in the sense that it actually loads the data and displays it in a recyclerview. It just doesn't redisplay or reload when I call the populateList() method again.
This bug has been bugging me in my sleep literally - so any help and solutions would mean a lot to me
EDIT: I THINK I KNOW EXACTLY WHERE THE ERROR IS - IT IS IN THE POSTEXECUTE METHOD IN ADDUSERS CLASS.
Thanks
Just check if the adapter is not null before calling notifyDataSetChanged():
if (adapter != null) {
adapter.notifyDataSetChanged();
}
This happens because the AsyncTask executes asynchronously and by the time it finishes, the adapter might not be available anymore.
One option you could try is, instead of using
adapter.notifyDataSetChanged();
replace with
recyclerView.getAdapter().notifyDataSetChanged();
This is assuming for whatever reason, you have got your adapter set to null, but not shown in the code clearly above, while your recyclerview is still fully intact.
p/s: I also didn't see you update your latest retrieved data and insert into the adapter. You might want to check that as well.
You're handling variables outside the scope of your main class in a Threaded class which can always be sketchy. Granted AsyncTask is supposed to synchronize operations for you, I'm always cautious
Try passing your list and adapter to your DisplayUsersList class e.g.
DisplayUsersList displayUsersList = new DisplayUsersList(list, adapter);
displayUsersList.execute();
And in your DisplayUsersList make sure the reference of these is explicit e.g.
public class DisplayUsersList extends AsyncTask<String, String, String> {
private final List<UserDetails> list;
private final Adapter adapter;
public DisplayUsersList(List<UserDetails> list, Adapter adapter) {
this.list = list;
this.adapter = adapter;
}
...
Also, try moving the populateList(); to OnStart rather than onCreate
As although the variables are assigned they may still be initialising.
Edit
Could you try moving the call to the top level class e.g.
public void dataSetChangedCallback() {
adapter.notifyDataSetChanged();
}
And in your DisplayUsersList change adapter.notifyDataSetChanged(); to
...
dataSetChangedCallback();
...

Keeping list entries when I move activity

I have a ListView.
I populate this list from 2 editTexts
When I move activity and go back to it the entries are gone again.
I kind of understand why this is but dont know how to correct it.
ListView lv2 = (ListView) findViewById(R.id.listView2);
final SimpleAdapter simpleAdpt = new SimpleAdapter(this, planetsList, android.R.layout.simple_list_item_1, new String[]{"planet"}, new int[]{android.R.id.text1});
planetsList.add(createPlanet("planet", "testme"));
lv2.setAdapter(simpleAdpt);
button21.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
iinitList();
simpleAdpt.notifyDataSetChanged();
editText5.setText("");
editText6.setText("");
}
});
}
private void iinitList() {
String st,str;
Double db;
if (editText5.getText().toString()!= "" && editText6.getText().toString()!="") {
st = editText5.getText().toString();
str = editText6.getText().toString();
db = Double.parseDouble(str);
planetsList.add(createPlanet("planet", ""+st+
": \n" +db+""));
}
}
HashMap<String, String> createPlanet(String key, String name) {
HashMap<String, String> planet = new HashMap<String, String>();
planet.put(key, name);
return planet;
}
As you can see I have added a value to the list manually called test also, when I move activity this stays in the list, I would love if the editText entries were to stay in there also when I move activities.
Activities can be destroyed when you navigate to a new one or rotate. This will clear anything that is only referenced by the activity, like your EditTexts. However, Android provides a nice utility for saving things you want to remain in a method called, which you can override in your activity:
#Override
protected void onSaveInstanceState(Bundle state) {
// Put your values in the state bundle here
}
#Override
protected void onCreate(Bundle savedState) {
// Load your UI elements as usual
if (savedState != null) {
// Load your state from the bundle
}
}
That same bundle will be given back to you in onCreate, where you create your UI to begin with so you can reload the state from it.
This is a really good description of how activities work:
http://developer.android.com/reference/android/app/Activity.html

Categories

Resources