Updating TextView with setText() doesn't work in AsyncTask onProgressUpdate() - android

I'm trying to update my user interface with information about a background task that's going on with an AsyncTask. Here's my onProgressUpdate() method.
#Override
protected void onProgressUpdate(final String... params) {
Log.d(TAG, "onProgressUpdate()");
StringBuilder currentParam = new StringBuilder();
StringBuilder statusMessage = new StringBuilder();
txtInputFile.setText("Input: " + params[0]);
txtOutputFile.setText("Output: " + params[1]);
txtPercentDone.setText("" + MainActivity.getProgressStatus(statusMessage, currentParam) + "%");
txtStatusMessage.setText(statusMessage);
txtParamName.setText(currentParam);
}
However, none of the TextViews update their text. I am calling publishProgress() in my doInBackground() method. And the log shows that onProgressUpdate() is getting called correctly. I know that the TextViews have been instantiated and are not null. Does anyone know what's going on?
UPDATE:
This may have something to do with the fact that this code is in a nested fragment (the ui being updated). When I show this same fragment as the root, everything is working fine. But I need to have it nested in this situation. Why is that making a difference?

Pass your parameters and context from AsyncTask and then set them using runOnUiThread. If you can't then put your log here to know the issue.

Every publishProgress() are enqueue on the UI thread, so if your doInBackground ends before all yours onProgressUpdate(), you only see your last ui update.
Try to test adding Thread.sleep(MILLISECONDS) to check the concurrency of the ui thread.
#Override
protected Void doInBackground(Void... params) {
for (int i = 0; i < SOME_VALUE ;i++) {
publishProgress(""+i);
try {
Thread.sleep(MILLISECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return null;
}

Related

Class Asyntask error

I'm developing an android application , my problem is that I can't execute my asyntask class after clicking on a button but it works normally when I called it in my program
I have in logcat the error : "Only the original thread that created a view hierarchy can touch its views.”
here is my class :
ts.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
jr=2;
emp trs=new emp();
trs.execute();
}
});
emp t=new emp();
t.execute();
private class emp extends AsyncTask<Void,Void,Void>{
#Override
protected Void doInBackground(Void... params) {
try{
url = new URL("....");
HttpURLConnection httpconn = (HttpURLConnection)url.openConnection();
httpconn.connect();
if (httpconn.getResponseCode() == HttpURLConnection.HTTP_OK){
BufferedReader input = new BufferedReader(new InputStreamReader(httpconn.getInputStream()),8192);
while ((line = input.readLine()) != null) {
ja = new JSONArray(line);}
for (int i = 0; i < ja.length(); i++) {
JSONObject jo = null;j=0;
jo = (JSONObject) ja.getJSONObject(i);
ch = jo.getString("bgcolor");
ch1=jo.getString("duree_heure");
ch2=jo.getString("debut_heure");
ch4=jo.getString("matiere");
j=Integer.parseInt(ch2);
ch2=trans(j,ch1);
ch5=jo.getString("idsalle");
ch6=salle(ch5);
addvi(v,ch,ch6,ch2,ch4);
}
input.close();
}
}catch (JSONException e){
System.out.print("vérifier !");e.printStackTrace();} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}
return null;
}
}
so can anyone helps me please ?
It's caused by the fact that when you are inside doInBackground you are inside another thread too and since it's forbidden to edit/remove/etc views create from another thread (in this case UI thread) it throw this error.
Since you didn't posted the full code, the only thing which could case this problem is addvi(v,ch,ch6,ch2,ch4); so you should use runOnUiThread method of Activity to execute the method from the main thread.
But you should rethink your logic to work better with Asynctask methods onPreExecute / onPostExecute which is used to work with UI and are called and execute in the main thread (UI thread).
P.S To work better with the methods i said above, you should know what means the three generic in the extendsAsyncTask<Params, Progress, Result>
The three types used by an asynchronous task are the following:
Params, the type of the parameters sent to the task upon execution.
Progress, the type of the progress units published during the background computation.
Result, the type of the result of the background computation.
Edit: As other noticed in comments you have onProgressUpdate too which is invoked by you from doBackground method using publishProgress
you cannot touch or modify view in the doInBackground function of asynctask all UI work need to be done on UI thread or main thread.I think you are doing some UI work so do it in onPostExecute() method

continue ProgressDialog until onPostExecute finishes

I know that progress dialog will stop spinning if it's not terminated immediately inside onPostExecute(). However, I have some methods to call inside onPostExecute() and I cannot transfer them to doInBackground() because these methods should run on UI. Is there a possible way to continue the progressDialog after calling these methods without stopping it to spin?
Here is the onPostExecute of my AsyncTask:
protected String doInBackground(final String... strings) {
//Network activity here
}
protected void onPostExecute(String unused){
//progressdialog stops spinning here, cannot change the message also
try {
if(response.equals("HOST ERROR") || response.equals("CONNECTION ERROR") || response.equals("ERROR")){
new AlertDialog.Builder(context).setTitle("Error").setMessage("Cannot connect to the internet.").setNeutralButton("Close", null).setIcon(android.R.drawable.ic_delete).show();
}
else{
doc = Jsoup.parse(response);
Intent cc = new Intent(activity,com.sblive.aufschoolbliz.GradeBook.class);
subjectCodes = getSubjectCodes(); //this parsing method should run on UI
professors = getProfs(); //this parsing method should run on UI
grades = getGrades(); //this parsing method should run on UI
cc.putExtra("subjectCodes", subjectCodes);
cc.putExtra("professors", professors);
cc.putExtra("grades", grades);
if(this.pd.isShowing()) {
this.pd.dismiss();
}
context.startActivity(cc);
}
}
catch (NullPointerException e) {
}
}
<<< EDIT: >>>
Ok forget about what I posted, silly me. Of course anything that modifies user interface needs to be called/dismissed on the UI thread.
So what I would do is run everything possible during doInBackground(), and creating/dissmissing the dialog or anything that requires to be run on the UI thread explicitly like this:
this.runOnUiThread(new Runnable() {
public void run() {
/*Code required to run on UI thread*/ }
});
And im afraid that's as much as you can do without overdoing way too much.

Android: Communication and coordination between UI-thread and other thread

I'm using this AsyncTask for calling the skype page https://login.skype.com/json/validator?new_username=username for understand if a certain skype contact already exsists.
public class SkypeValidateUserTask extends AsyncTask<String, Void, String>{
protected String doInBackground(String...urls){
String response = "";
for(String url:urls){
DefaultHttpClient client = new DefaultHttpClient();
HttpGet httpGet = new HttpGet(url);
try{
HttpResponse execute = client.execute(httpGet);
InputStream content = execute.getEntity().getContent();
BufferedReader buffer = new BufferedReader(new InputStreamReader(content));
String s="";
while((s=buffer.readLine()) != null){
response+=s;
}
}catch(Exception ex){
ex.printStackTrace();
}
}
return response;
}
public void onPostExecute(String result){
String status="";
try{
JSONObject obj=new JSONObject(result);
status=obj.getString("status");
}catch(Exception ex){
ex.printStackTrace();
}
//Log.i("RISULTATO: ","STATO: "+status);
}
}
The main activity call this task for getting skype validation user result. The code is:
String skype = "name.surname.example";
if(!skype.equalsIgnoreCase("")){
// check if the skype contact exists
SkypeValidateUserTask task = new SkypeValidateUserTask();
task.execute(new String[] {"https://login.skype.com/json/validator?new_username="+skype});
// here I need to obtain the result of the thread
}
My problems are:
I need to get the result of the task (the String status) in the main activity.
After the task.execute call, the next code in main activity is executed without wait for result returned from asynctask.
It is dengerious to use get() method to get the result from async task because It blocks the UI Thread.
use This Thread where I provided a reusable solutionCallback mechanism to get result from async thread without blocking UI Thread
I have implemented that with the help of lapslaz
public JsonData(YourActivityClass activity)
{
this.activity=activity;
mProgressDialog = new ProgressDialog(activity);
}
protected void onPostExecute(String jsondata) {
if (mProgressDialog != null || mProgressDialog.isShowing()){
mProgressDialog.dismiss();
}
if(jsondata!=null) {
activity.yourcallback(jsondata)
}
}
And define the yourcallback() in YourActivityClass
private void yourcallback(String data) {
jsonRecordsData=data;
showRecordsFromJson();
}
Start your AsyncTask on the main thread. In the preExecute method of the AsyncTask, you can start a ProgressDialog to indicate to the user that you're doing something that takes a few seconds. Use doInBackground to perform the long-running task (checking for valid Skype username, in your case). When it is complete, onPostExecute will be called. Since this runs on the UI thread, you can handle the result and perform further actions depending on it. Don't forget to close the ProgressDialog in onPostExecute.
That's why asyncTask is here. You can not make a blocking function call in UI thread because that will make your app unresponsive.
OnPostExcute Method is called on the UI/Main thread. you need to move your logic there to continue analyzing the result.
If your AsyncTask is an inner class of your main activity then you can just call a function in the main activity and pass it the result
Since it looks like it isn't, you can create constructor in your Async and pass it the Context from your main activity so you can pass the variable back to main activity
Also, the purpose of the AyncTask is to not block your UI thread, put your logic in a separate function that the AsyncTask will call
You need to implement a call-back mechanism in your AsyncTask. So instead of this:
task.execute(...);
// use results of task
Your structure should be:
task.execute(...);
// go do something else while task has a chance to execute
public void statusAvailable(String status) {
// use results of task
}
and in onPostExecute:
public void onPostExecute(String result) {
. . .
status=obj.getString("status");
activity.statusAvailable(status);
}
Getting the result out of the AsyncTask is easy . . . just the Google docs don't make it clear how to do it. Here's a quick run down.
task.execute(params);
will start the AsyncTask and pass to the doInBackground method whatever parameters you include. Once doInBackground finishes, it passes the result of its functions to onPostExecute. One would think that onPostExecute would be able to just return whatever you sent it. It doesn't. To get the result of doInBackground, which was sent to onPostExecute you need to call
task.get();
the .get() method automaticlly waits until the task has completed execution and then returns whatever the result of onPostExecute is back to the UI thread. You can assign the result to whatever variable you want and use normally after that - for example
JSONObject skypeStuff = task.get()
Put another way - just like the AsynTask does not start on it's own, it does not return on its own. The same way you need to .execute() from the UI thread, you need to call .get() from the UI thread to extract the result of the task.

AsyncTask to call another function within class

Basically, do I have to put code I want to run on another thread inside doInBackground, or can I call another function/class/whatever-it-is-functions-are-called-in-JAVA within doInBackground and have it run asynchronously? IE: (example code I found online)
protected String doInBackground(String... params) {
for(int i=0;i<5;i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
return null;
}
is how I have seen it done, but can I instead do:
protected String doInBackground(String... params) {
postToServer(x,y,z,h);
}
and have it call a function I already wrote and then have that function run in another thread? Sometimes my HTTP server is a bit slow to respond (it is but a lowly testing server at the moment) and Android automatically pops up the kill process box if my postToServer() call takes more than 5 seconds, and also disables my UI until the postToServer() call finishes. This is a problem because I am developing a GPS tracking app (internally for the company I work for) and the UI option to shut the tracking off freezes until my postToServer() finishes, which sometimes doesn't ever happen. I apologize if this has been answered, I tried searching but haven't found any examples that work the way I'm hoping to make this work.
You can do that, but you will have to move the UI updates to onPostExecute as it is run on the UI thread.
public MyAsyncTask extends AsyncTask<foo, bar, baz> {
...
protected String doInBackground(String... params) {
postToServer(x,y,z,h);
}
protected void onPostExecute(Long result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
}
....
}
You may want to pass in the TextView to the constructor of the AsyncTask and store it as a WeakReference.
private final WeakReference textViewReference;
public MyAsyncTask(TextView txt) {
textViewReference = new WeakReference<TextView>(txt);
}
And then in onPostExecute you would make sure that the TextView reference still exists.
protected void onPostExecute(Long result) {
TextView txt = textViewReference.get();
if (txt != null)
txt.setText("Executed");
}
If you want to notify the user that the task is executing I would put that before invoking the AsyncTask.
myTextView.setText("Update in progress...");
new MyAsyncTask().execute();
then in onPostExecute set the TextView to say "Update complete."
Have you tried it the second way?
From what you've posted it seems like it should work fine how you have it in the second example.
However (perhaps unrelated to your question?) in your first example I think it will fail because you are trying to change the UI from a background thread. You'd want to put the parts that manipulate the TextView inside of onPostExecute() rather than doInBackground()
Yes you can, the call to your postToServer method (that's the name in java) will run off the main thread.
Everything inside the doInBackground method of an AsyncTask is run on a pooled thread, but be sure to NOT invoke it directly! Call execute on your asynktask instead, the android framework will do the work for you and run doInBackground on another thread.
try doing something like this:
new AsyncTask<Void, Void, Void>() {
#Override
// this runs on another thread
protected Void doInBackground(Void... params) {
// assuming x, y, z, h are visible here
postToServer(x, y, z, h);
return null;
}
#Override
// this runs on main thread
protected void onPostExecute(Void result) {
TextView txt = (TextView) findViewById(R.id.output);
txt.setText("Executed");
}
}.execute(); // call execute, NOT doInBackGround
Also, notice that every other method of AsyncTask, such as onPostExecute runs on the main thread, so avoid heavy loading them.
Basically The Bottom Line Is the doInBackground() method is Can't interact with The Ui Thread Or The Main thread. that's Why When You are Try To Interact With The TextView in doInBackground () it Will Crash the UI Thread Cuz It's Illegal.
so if anytime You want to Interact with the UI Thread,When You are Working on doInBackground You need to Override
OnPostExecute() //this Function is Called when The doInBackground Function job is Done.
So You can Update The UI Thread Content By this When You're Job is Done In doInBackground () or You are In doInBackground ()

Is there anything wrong with AsyncTask?

#Override
protected InputStream doInBackground(String... url){
try {
InputStream stream = downloadXml(url[0]);
new ParseXml(stream); //for testing porpuses: outputs ok to logcat
return stream;
} catch (IOException e) {
Log.d("dbg","exception");
e.printStackTrace();
return null;
}
}
#Override
protected void onPostExecute(InputStream result) {
if (result != null) {
new ParseXml(result); //crashes the app
}
Log.d("dbg","postexecute triggered ok");
}
Code is pretty self explanatory i think, i tried changing the passing type to just Object and type casted it where needed but it didn't worked either.
Is there anything undocumented in sdk that i should know of ?
obviously, Crash.. You are doing lengthy (also may be network related) operation in MainUI Thread. as onPostExecute() of AsyncTask runs on In MainUI Thread only. So always keep it in doInBackground().
This code line new ParseXml(result); should be in doInBackground() of AsyncTask.
Update:
So complete the Parsing of XML in doInBackground() and only pass the result in onPostExecute() if only you want to reflect the updation on Application UI.

Categories

Resources