I create the content of a ListView in the background and when each item is added I update the ListView adapter.Usually it works ok but some times I get this error. And strangely enough it happens 10 times more often in my Galaxy s4 mini than in my HTC Sensation. I don't understand why this happens since I clearly notify the adapter through the UI thread. Any thoughts ?
java.lang.IllegalStateException: The content of the adapter has changed but ListView
did not receive a notification. Make sure the content of your adapter is not modified
from a background thread, but only from the UI thread. Make sure your adapter calls
notifyDataSetChanged() when its content changes. [in ListView(2131100779, class
util.TouchInterceptor) with Adapter(class com.bill.deuterh.ListActivity$ListAdapter)]
AsyncTask:
private class AsyncCaller extends AsyncTask<Void,Void,Void>{
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
ListActivity.this.runOnUiThread(new Runnable(){
#Override
public void run() {
findViewById(R.id.progress_bar).setVisibility(View.GONE);
}
});
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
for (int i=0;i<table.length;i++){
if(isCancelled()){break;}
myList.add(createObject(table[i])));
ListActivity.this.runOnUiThread(new Runnable(){
#Override
public void run() {
ListAdapter.notifyDataSetChanged();
}
});
}
return null;
}
}
(If it matters, I cast the Listview as TouchInterceptor which is a modified google class for supporting the rearrangement of the listview items with drag and drop.)
Try this: If everything else has correct set up. Then this code will Show every update from your doInBackground immediatly in the listview = good user experience!
private class AsyncCaller extends AsyncTask<Void,Void,Void>{
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
findViewById(R.id.progress_bar).setVisibility(View.GONE);
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
for (int i=0;i<table.length;i++){
if(isCancelled()){break;}
myList.add(createObject(table[i])));
publishProgress();
}
return null;
}
protected void onProgressUpdate(Void.. values) {
ListAdapter.notifyDataSetChanged();
}
}
OnPreExecute, onPostExecute and onProgressUpdate are already running on the UIThread. Maybe you don't execute the asynctask the right way. That's why no data has been updated?
This should be run only once at the end of onpostexecute without the need of runonuithread.
ListAdapter.notifyDataSetChanged();
remove this:
ListActivity.this.runOnUiThread(new Runnable(){
#Override
public void run() {
ListAdapter.notifyDataSetChanged();
}
});
and put this:
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
findViewById(R.id.progress_bar).setVisibility(View.GONE);
ListAdapter.notifyDataSetChanged();
}
Don't write any code in doInBackground(Void... params) which relates or update the UI. For updation on UI Async's onPostExecute(Void result) method is used.
So remove the adatper's notification from doInbackGround(Void... params){}
Related
I have a custom List which I want to refresh when a action bar button is clicked.
This List is within a Fragment.
I want to show a ProgressBar until the List is refreshed.
Currently Iam doing this:-
private class RefreshList extends AsyncTask<Void, Void, Void>
{
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if(refDialog!=null)
{
refDialog.dismiss();
}
}
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
if(refDialog!=null)
{
refDialog =null;
}
refDialog = WaitProgressFragment.newInstance();
refDialog.show(getFragmentManager(), "Wait");
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
//stagAdaper.notifyDataSetChanged();
itemsAdapter.notifyDataSetChanged();
return null;
}
}
But I get this error
Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
How can I refresh the List while showing the progress dialog?
Put
itemsAdapter.notifyDataSetChanged();
Inside onPostExecute.
notifyDataSetChanged is an UI operation and doInBackground does not work on UI thread, whereas onPostExecute does.
You have to move the portion of the background task to the UI thread,so notify itemsAdapter in onPostExecute method as
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
itemsAdapter.notifyDataSetChanged()
if(refDialog!=null)
{
refDialog.dismiss();
}
}
Supposing I'm using the following AsycTask (called A) for sending data over internet:
private class A extends AsyncTask<Void, Void, Void>{
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
super.onPreExecute();
pd = ProgressDialog.show(context, "Notification", "Sending message...");
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
sendMessage();
return null;
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
if(pd!=null && pd.isShowing())
pd.dismiss();
}
}
Inside the doInbackground is called the my method sendMessage(). I will not go into details of this method, just immagine that this method executes a Thread (suppouse that it is called B).
So, the question is:
The code inside the onPostExecute() of AsyncTask A will be executed after the end of the Thread B?
If not, how can make possible that the onPostExecute will be executed only when the AsyncTask inside the sendMessage() will be terminated?
you could let the sendMessage() return the Thread object and then call
Thread.join()
This will cause the doInBackground to wait until the thread is finished.
Well it is pretty simple. Just call that AsyncTask B in onPostExecute of AsyncTask A.
Edit : Thread inside thread is not possible from ICS onwards. It will not give you any error, but it will not work too and your app will stuck there.
In my acivity, i need to run two background service.
main background service:
class loadingTask extends AsyncTask<Void, Void,Void> {
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
pd = ProgressDialog.show(context, "", "Chargement en cours..", true, false);
super.onPreExecute();
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
pd.dismiss();
}
...
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
getxml = Util.CallWebService("");
return null;
}
}
Second background service.
class mloadingTask extends AsyncTask<String, Void, String> {
protected String doInBackground(String... urls) {
SAXHelper sh = null;
try {
sh = new SAXHelper(urls[0]);
} catch (MalformedURLException e) {
e.printStackTrace();
}
sh.parseContent("");
return "";
}
protected void onPostExecute(String s) {
pd.dismiss();
}
}
In my onCreate() method I want to call the first background and when it finished loading the second background service starts. Part of my background service is as follows.
AsyncTask<Void, Void,Void> loadTask = new loadingTask().execute();
if(loadTask.getStatus()==AsyncTask.Status.FINISHED){
new mloadingTask().execute(getxml);
System.out.println("getxml: "+getxml);
}
However the second background service doesn't seem to start. i am not getting the a print also. I think I miss a step or android doesn't allow more than one background service in the same activity. A help please.
AsyncTask<Void, Void,Void> loadTask = new loadingTask().execute();
if(loadTask.getStatus()==AsyncTask.Status.FINISHED){
new mloadingTask().execute(getxml);
System.out.println("getxml: "+getxml);
}
there is a 99% chance that the if() block will never pass.
You first execute the first asyncTask named loadTask and RIGHT after check if it finished.
Small chance that it actually finishes by then.
The easy approach:
Use only one async task. You want to finish asyncTask 1 and then fire asyncTask 2, its exactly the same as having only one asyncTask doing both operations.
class loadingTask extends AsyncTask<Void, Void,Void> {
#Override
protected void onPreExecute() {
// TODO Auto-generated method stub
pd = ProgressDialog.show(context, "", "Chargement en cours..", true, false);
super.onPreExecute();
}
#Override
protected void onPostExecute(Void result) {
// TODO Auto-generated method stub
super.onPostExecute(result);
pd.dismiss();
}
...
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
getxml = Util.CallWebService("");
SAXHelper sh = null;
try {
sh = new SAXHelper(urls[0]);
} catch (MalformedURLException e) {
e.printStackTrace();
}
sh.parseContent("");
return null;
}
}
The difficult approach:
How I solve this kind of thing is(our situation is different, but it should do)
Create an interface, with one method. for example:
public interface OnDataChangedListner {
void dataChanged(Class listener);
}
Then somewhere (i use my repository classes)
write a method to add and remove entires to a list of OnDataChangedListener interfaces
private ArrayList<OnDataChangedListner> mOnDataChangedListners;
public void addOnDataChangedListner(OnDataChangedListner onDataChangedListner){
synchronized (mOnDataChangedListners){
mOnDataChangedListners.add(onDataChangedListner);
}
}
public void removeOnDataChangedListner(OnDataChangedListner onyDataChangedListner){
if (mOnDataChangedListners != null){
synchronized (mOnDataChangedListners){
for (Iterator<OnDataChangedListner> it = mOnDataChangedListners.iterator(); it.hasNext();) {
OnDataChangedListner listener = it.next();
if (listener .equals(onDataChangedListner))
it.remove();
}
}
}
}
This might be overkill. But this example should help you with updating the UI while your tasks are running. A free extra! :))
With the above code in place, create an onDataChanged() method in the same class where u defined the add and remove listener methods.
In that code call a handler
// Need handler for callbacks to the UI thread
final static Handler mHandler = new Handler();
// Create runnable for posting
final Runnable mNotifyDataChanged = new Runnable() {
public void run() {
if (mOnDataChangedListners != null){
synchronized (mOnDataChangedListners){
for (Iterator<OnDataChangedListner> it = mOnDataChangedListners.iterator(); it.hasNext();) {
OnDataChangedListner listener = it.next();
if (listener != null)
listener.dataChanged(this.getClass());
else
it.remove();
}
}
}
}
};
/**
* will notify registerred delegates on the main (UI) thread
*/
public void notifyDataChanged(){
mHandler.post(mNotifyDataChanged);
}
Ok, so I ended up giving out an entire sample.
Where you place this code is upto you. But when you call notifyDataChanged() it will fire the handler, which in turn will loop trough all current registered listeners for this class
then in turn it will call the listeners datachanged method.
To make this all work, all you have to do is have a class implement the interface
call addOnDataChangedListener(this);
and implement the method provided in the interface.
Note that this is deffinetly not the easiest way to do things.
I don't know what the easiest way to do what you want is. I never have been in that situation before.
but you can execute the second task in the implemented method. Should work..
AsyncTask is Asynchronous as its name suggest. You need to call your 2nd AsyncTask in onPostExecute of your 1st AsyncTask
OR
Loop and wait till AsyncTask is finished in your activity, but it may take more time and you can get Application Not Responding message.
As soon as you call execute() method, control comes to next statement which loadTask.getStatus(). As task is just started, loadTask.getStatus() will not return FINISH and your 2nd task is never getting executed.
I'm coding a program which fetches the data from MySql from server (using JSON) and it updates the UI accordingly,
I'm fetching two types of data using AsyncTask from Server
1) Bubble Answers
2) Comments
The parseBubbleAnswers method successfully runs and Updates UI,
but parseComments class which is AsyncTask, and which call parseComments method in doInBackground, is not running runOnUiThread(new Runnable() { run() });
Can anyone help me in solving this
Here is my code :
public class FetchServer extends Activity
{
protected void onCreate(Bundle savedInstanceState)
{
String photoId = "1"; // photo id for which the data is fetched
checkBubbleData(photoId); // which call AsyncTask - 2 differnt calls
}
public void checkBubbleData(String photoId)
{
new parseBubbleAnswers().execute(photoId); // to fetch bubble answers
new parseComments().execute(photoId); // to fetch comments
}
class parseBubbleAnswers extends AsyncTask<String, Integer,String>
{
#Override
protected String doInBackground(String... params)
{
// TODO Auto-generated method stub
Looper.prepare();
parseBubbleAnswers(); // which has runOnUiThread(new Runnable() which updates (successfully !) the UI
return null;
}
}
class parseComments extends AsyncTask<String, Integer,String>
{
#Override
protected String doInBackground(String... params)
{
// TODO Auto-generated method stub
Looper.prepare();
String parseComReturn = parseComments();
if(parseComReturn=="end")
{
commentBuilder(); // which will update UI after fetch data by parseComments() method
}
}
}
public void commentBuilder()
{
runOnUiThread(new Runnable() // while debugging, it comes here, on Step Over it stick for 2 times and then move at the end of method without error
{
public void run()
{
// update UI code
}
});
}
}
Try this way :
First create one Handler :
Handler mHandler = new Handler();
Change this,
public void commentBuilder()
{
runOnUiThread(new Runnable() // while debugging, it comes here, on Step Over it stick for 2 times and then move at the end of method without error
{
public void run()
{
// update UI code
}
});
}
With,
public void commentBuilder()
{
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
while (isRunning) {
try {
// Thread.sleep(10000);
mHandler.post(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
// Write your code here to update the UI.
}
});
} catch (Exception e) {
// TODO: handle exception
}
}
}
}).start();
}
Stop thread by this once you are done with UI,
isRunning = false;
EDIT :
Try to Use Async Task in this way :
class parseComments extends AsyncTask<String, Integer,String>
{
protected String doInBackground(String... params) {
String parseComReturn = parseComments();
return parseComReturn;
}
protected void onPostExecute(String result) {
if(result.equals("end"))
{
commentBuilder();
}
}
}
Thanks.
runOnUiThread is a method of Activity, AsyncTask has no reference to Activity.
however, AsyncTask already runs on the UI thread and was designed to do exactly that.
just deal with the UI changes in onPostExecute.
I faced the similar issue.
Just pass the reference of the Activity class to the parseComments class.
class parseComments extends AsyncTask<String, Integer,String>{
Activity activity;
public parseComments(Activity activity){
this.activity = activity;
}
}
After that you can use runOnUiThread as
activity.runOnUiThread(new Runnable(){
#Override
public void run(){
}
});
It will only work with Activity class. Not Context class.
In my application when i click on Button it sometimes shows the progressdialog and sometimes not show the progressdialog on click of button.
Asynchronous Task code is:
public class LoadData extends AsyncTask<Void, Void, Void>
{
ProgressDialog pd;
#Override
protected void onPreExecute()
{
pd = ProgressDialog.show(MainActivity.this, "", "Loading...");
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
LoadActivities(); // function to load data from url
}
});
return null;
}
#Override
protected void onPostExecute(Void unused)
{
pd.dismiss();
}
}
and on button click event call this as:
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new LoadMoreData().execute(null);
}
});
the wrong think you are doing is that in doInBackground you use runOnUiThreade . just remove that from your code . It solves your problem.
never use any thread in doInBackground.
Why you have taken run method again in doInBackground, doInBackground method performs computation on a background thread, so no need to take runOnUiThread
MainActivity.this.runOnUiThread(new Runnable() {
public void run() {
// TODO Auto-generated method stub
LoadActivities(); // function to load data from url
}
});
Just write
protected Boolean doInBackground(final String... args) {
try {
LoadActivities();
return true;
} catch (Exception e) {
Log.e("tag", "error", e);
return false;
}
}
And also change new LoadMoreData().execute(); don't write null
btn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new LoadMoreData().execute();
}});
Nirali's answer seems correct, just to make further explaination and some edits.
Progress Dialog will be shown by the time doInBackground method returns value. and in your code it just create another thread, and completes execution, so to display progress dialog by the time LoadActivities exectues, execute this statement in the same thread doInBackground executes, so change to following:
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
MainActivity.this.runOnUiThread(new Runnable() {
LoadActivities(); // function to load data from url
return null;
}