This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 6 years ago.
So I have an AsyncTask class to handle populating my RecyclerView. It works well but when it needs to be refreshed then it crashes with a NullPointerException. I kind of know why but then I can't do it because I am creating a new object and adding it to my list.
This is my code:
#Override
protected void onPostExecute(String s) {
try {
userslist.clear();
JSONArray jsonArray = new JSONArray(s);
for(int i=0; i < jsonArray.length(); i++){
...
UsersData usersData = new UsersData(var1, var2);
userslist.add(UsersData);
}
cAdapter.notifyDataSetChanged(); // Culprit line, despite the list being deleted and added again in the try block
} catch (JSONException e) {
e.printStackTrace();
}
}
I suspect is it something to do with the userslist.
This is my error logs:
java.lang.NullPointerException
at lukazs.newapp.UserInfo$GetUserList.onPostExecute(UserInfo.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)
EDIT: This is the method call where I populate the recyclerview:
public void populateRecyclerList(){
GetUserList getUserList = new GetUserList();
getUserList.execute();
}
#Override
protected void onCreate(Bundle savedInstanceState) {
...
setContentView(R.layout.usersdetails);
populateRecyclerList();
}
This is where cAdapter is initialised, in onCreate method:
RecyclerView.Adapter cAdapter;
ArrayList<UserDetailsProvider> userslist = new ArrayList<UserDetailsProvider>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recyclerView = (RecyclerView) findViewById(R.id.recycleMusic);
cAdapter = new UserDefinedAdapter(userslist);
recyclerView.setAdapter(cAdapter);
populateRecyclerList();
}
EDIT: I have done something now, but the populateRecyclerList() is crashing in another AsyncTask class on onPostExecute. Basically, I want to repopulate the recyclerView after a user has been added. This is my code for the onPostExecute() method, where I am calling the populateRecyclerList() method:
#Override
protected void onPostExecute(String s) {
populateRecyclerList();
}
Maybe you are not meant to call this method here? But then how would I update the RecyclerView?
Your cAdapter is not initialized when you call cAdapter.notifyDataSetChanged().
EDIT:
Call your populateRecyclerList() after initilize your fields
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
recyclerView = (RecyclerView) findViewById(R.id.recycleMusic);
cAdapter = new UserDefinedAdapter(userslist);
recyclerView.setAdapter(cAdapter);
populateRecyclerList();
}
Hope it helps!
My bad, onPostExecute DOES run on the UI thread... Thanks you guys for correcting...
http://developer.android.com/reference/android/os/AsyncTask.html
"onPostExecute(Result), invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter."
Related
I've read so many posts here in Stackoverflow about this problem... I implemented all the solution that I've seen, but none of them worked.
What is happening: the notifyDataSetChanged doesn't work when opening the app. If I change orientation or change the sort (popular to top rated, or vise-versa), it works. I debbuged the code and the data arrives correctly in the Adapter, but the notify doesn't work, so the interface doesn't got updated.
Can anyone help me, please?
My onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Resources rs = getResources();
int numColumns = rs.getInteger(R.integer.list_columns);
mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view_popular_movies);
mErrorMessageDisplay = (TextView)findViewById(R.id.tv_error_message_display);
mLoadingIndicator = (ProgressBar) findViewById(R.id.pb_loading_indicator);
//Resource based on https://discussions.udacity.com/t/is-there-a-way-to-fit-columns-in-gridlayoutmanager/221936/4
GridLayoutManager layoutManager = new GridLayoutManager(this, numColumns);
mRecyclerView.setLayoutManager(layoutManager);
mRecyclerView.setHasFixedSize(true);
mPopularMoviesAdapter = new PopularMoviesAdapter(this, this);
mRecyclerView.setAdapter(mPopularMoviesAdapter);
loadMoviesData(NetworkUtils.POPULAR_SORT);
}
The loadMoviesData method simply calls the execute() from AsyncTask.
My onPostExecute code:
#Override
protected void onPostExecute(String result) {
Log.v(TAG, "onPostExecute");
mLoadingIndicator.setVisibility(View.INVISIBLE);
if(result != null){
showMoviesDataView();
Gson gson = new Gson();
Movies data = gson.fromJson(result, Movies.class);
mPopularMoviesAdapter.setMovieList(data);
} else {
showErrorMessage();
}
}
My setMovieList method:
public void setMovieList(Movies movieList) {
this.movieList = movieList;
this.notifyDataSetChanged();
}
My full code: https://github.com/guuilp/PopularMovies
I had a look at your code and your notifyDataSetChanged() is working, the problem is that your RecyclerView has no height.
Change it to android:layout_height="match_parent".
How can I add elements to an Array or ArrayList from the AsyncTask class(Inner) , from where I am receiving data from server .
I tried adding elements from doInBackgroud() and printing them in onPostExecute() ,it works fine then .
But the Array and List are both empty when I am trying to access them outside the AsyncTask class.
Code :-
public String[][] mov_details ;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.vpmovies);
mov_details=new String[NUM_OF_MOVIES][9];
new MyHttpMovies().execute();
Toast.makeText(this,mov_details[0][2],Toast.LENGTH_LONG).show(); //prints blank
doInBackgroud() and onPostExecute() -
for(int i=0; i < jsonArray.length(); i++){
JSONObject jsonObject = jsonArray.getJSONObject(i);
//parsing some json
mov_details[i][0] = (jsonObject.optString("name").toString());
mov_details[i][1] = (jsonObject.optString("lang").toString());
mov_details[i][2] = (jsonObject.optString("genre").toString());
.
.
.
.
return mov_details;
}
protected void onPostExecute(String[][] strings) {
super.onPostExecute(strings);
Toast.makeText(VPmovies.this,strings[0][1],Toast.LENGTH_LONG).show();
// prints successfully
}
public class MainActivity extends AppCompatActivity {
ArrayList<String> list;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public class fetchData extends AsyncTask<Void,Void,Void> {
#Override
protected Void onPreExecute() {
super.onPreExecute();
list = new ArrayList<String>();
}
#Override
protected Void onPostExecute(Void... params) {
...
list.add("bla bla");
...
return null;
}
}
}
Use publishProgress("data") inside your doInBackgroung and pass data from AsyncTask to your Activity/Fragment. Keep in mind doInBackgroung runs in separate thread you can not directly update your main GUI from doInBackgroung, if you will call some methods directly from doInBackgroung than your calling method should run in main thread.
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
// send data to your required location its recommended to use interfaces for communication
}
Solved by simply using the get() method on the AsyncTask instance that would return the output/result of the AsyncTask backgroud thread.
mov_details = new MyHttpMovies().execute().get(); //returns 2d array
and instead of using mov_details variable inside doInBackground() , I used some other 2d array variable local to the just AsyncTask.
I am currently trying to update a RecyclerView once an AsyncTask has completed its operation.
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initializing RecycleView
mRecyclerView = (RecyclerView)findViewById(R.id.list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
mAdapter = new AdvertAdapter(this.results, R.layout.card_advert, this);
mRecyclerView.setAdapter(mAdapter);
Here is a code snippet from my AsyncTask:
#Override
protected void onPostExecute(String result){
// your stuff
listener.onTaskCompleted(data);
}
In the listener Activity class, I am updating the data set and attempting to notify my custom adapter that the data set has been changed:
#Override
public void onTaskCompleted(ArrayList<Data>results) {
this.results = results;
this.mAdapter.notifyDataSetChanged();
System.out.println("Done.");
}
It seems that notifyDataSetChanged() is not actually doing anything, as the list remains empty even after the final line. From the debugger, I can verify that results does contain the correct results, and the onTaskCompleted is being run on the main thread. How can I update my RecyclerView with the new results?
I think you should add the ArrayList<Data>results to your adapter data.
Something like this:
#Override
public void onTaskCompleted(ArrayList<Data>results) {
//this.results = results;
this.mAdapter.clear();
this.mAdapter.add(results);
//or this.mAdapter.results = results;
this.mAdapter.notifyDataSetChanged();
System.out.println("Done.");
}
I've checked other answers regarding the asynchronous updates of ListFragments and have figured out that the most common problem for the notifyDataSetChanged() method not working is, that developers tend to overwrite the initial adapter source, causing the adapter to lose reference and hence not updating the view. I have a ListFragment that I want to update asynchronously. What I've decided to do, is parse the list data in an AsyncTask and finally update the fragment in onPostExecute(). I want the UI to be as fluid as possible, so I want to avoid doing heavy processing on the main thread. I call the AsyncTask after the views are created to avoid null pointers.
public class CategoryFragment extends ListFragment {
ArrayList<Category> categoriesArray = new ArrayList<Category>();
CategoryAdapter adapter;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_category, container, false);
adapter = new CategoryAdapter(getActivity(), R.layout.category_list_item, categoriesArray);
setListAdapter(adapter);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
new UpdateUITask().execute(categories);
}
...
// The async task to update the UI.
class UpdateUITask extends AsyncTask<String, String, ArrayList<Category>>{
#Override
protected ArrayList<Category> doInBackground(String... input) {
// Do some data processing, to fill the categoriesArray.
// categoriesArray.add(...); -- loop
return categoriesArray;
}
#Override
protected void onPostExecute(ArrayList<Category> result) {
super.onPostExecute(result);
adapter.notifyDataSetChanged();
}
}
}
The refresh method fires, but produces no result. What else am I missing here? Should I pick a completely different approach?
Its looking like your instance of categoriesArray is getting lost. adapter.notifyDataSetChanged(); doesn't work only in case when your refrence of the listArray which you just passed to the adapter has been lost or changed. So, I would reccomend you to please make sure about this.
Also if you are going to populate your custom array then, use onProgressUpdate() method of the AsyncTask. It will reduce the loading time too.
You can do this like this:
class UpdateUITask extends AsyncTask<String, Category, ArrayList<Category>>
{
#Override
protected ArrayList<Category> doInBackground(String... input)
{
// Do some data processing, to fill the categoriesArray.
// and get the category objects one by one and call
//publishprogress till data is there
publishProgress(Category);
// and finallly just return somthing to get in onpostexecute
}
#Override
protected void onProgressUpdate(Category... values)
{
// TODO Auto-generated method stub
super.onProgressUpdate(values);
categoriesArray.add(...);
adapter.notifyDataSetChanged();
}
#Override
protected void onPostExecute(ArrayList<Category> result)
{
super.onPostExecute(result);
adapter.notifyDataSetChanged();
}
}
use below code for your AsyncTask, but please make sure again you are changing in the original categoryList.
private class UpdateUITask extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
// Do some data processing, to fill the categoriesArray.
return null;
}
#Override
protected void onPostExecute(Void result) {
adapter.notifyDataSetChanged();
}
}
and just run this task with passing any argument.
new UpdateUITask().execute();
I have a main activity with three fragments. In the first fragment is a listview. I populate it in the fragment's onCreateView method like this:
private ArrayList<MobileNETDistinctChatInfo> m_parts = new ArrayList<MobileNETDistinctChatInfo>();
public MobileNETDistinctChatInfoAdapter m_adapter;
public ListView list;
public String logged_user;
onCreateView(){
LinearLayout view = (LinearLayout) inflater.inflate(R.layout.tab1, container, false);
list = (ListView)view.findViewById(R.id.chats_list)
m_parts = db.MESSAGES_getAllDistinctChatInfo(logged_user);
// adapter extends the ArrayAdapter
m_adapter = new MobileNETDistinctChatInfoAdapter(getActivity(), R.layout.chatlist_list_item, m_parts);
list.setAdapter(m_adapter);
return view;
}
I would like to refresh the listview in the onResume() method in fragment1, but I can't get it to work. I tried these two methods (If I use the first method, nothing happens. If I use the second, the app crashes, returning a NullPointerException):
# 1
public void onResume() {
super.onResume();
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
m_adapter.notifyDataSetChanged();
}
}
}
# 2
public void onResume() {
super.onResume();
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
m_parts.clear();
m_parts = db.MESSAGES_getAllDistinctChatInfo(logged_user);
ListView list = (ListView) getActivity().findViewById(R.id.chats_list);
MobileNETDistinctChatInfoAdapter caa = (MobileNETDistinctChatInfoAdapter) list.getAdapter();
caa.clear();
for(MobileNETDistinctChatInfo el : m_parts){
caa.add(el);
}
list.setAdapter(caa);
}
}
}
I've printed the size of m_parts and m_adapter in OnResume() and it seems the adapter isn't being refreshed, but m_parts is. Does anybody know why, or how I can solve this?
You are running a m_adapter.notifyDataSetChanged() in a separate thread which leads the execution of refreshing the list before even the list is modified or updated actually.
if you debeg your code then you can see that it is working fine because the thread got enough time to get executed.