Can't touch views from onPostExecute's AsyncTask - android

I have already read all questions about this. And it's not the same case. Believe me.
I know an AsyncTask doInBackground function has his own thread, and you must touch the view in onPreExecute and onPostExecute.
Besides, I'm using isCancelled and isFinishing to really be sure the task is alive. And with all these things, the app is still crashing.
This is the code:
public class MainActivity {
private Activity activity;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.Main);
activity = this;
new MyTask(activity).execute();
}
private class MyTask extends AsyncTask<Void, Void, User[]> {
// ...
#Override
protected void onPreExecute() {
}
#Override
protected User[] doInBackground(String... params) {
// Api call
// return results
}
#Override
protected void onPostExecute(User[] users) {
if (isCancelled() || isFinishing()) return;
findViewById(R.id.panelViews).removeAllViews();
findViewById(R.id.element).setText("something");
// ... more
}
}
}
As you can see:
the touch of the view is in the onPostExecute
I use the "isCancelled" in case the user cancels the task exiting the screen
The same with "isFinishing"
And even now I'm getting the error "Only the original thread that created a view hierarchy can touch its views" in the line when I try to change something of the view (in the example a simple text)
If I cancel (pressing back for example) when it's trying to get the API results is OK, and it works.
If I cancel exactly when the API results come, and before it starts changing things, it crashes (I inserted a "sleep" in the onPostExecute to accomplish this test).
The error code is in line 112, that is the "findViewById(R.id.panelViews).removeAllViews();" described above:
android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRoot.checkThread(ViewRoot.java:2988)
at android.view.ViewRoot.requestLayout(ViewRoot.java:648)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.widget.ScrollView.requestLayout(ScrollView.java:1303)
at android.view.View.requestLayout(View.java:8416)
at android.view.View.requestLayout(View.java:8416)
at android.view.ViewGroup.removeAllViews(ViewGroup.java:2354)
at com.test.activities.MainActivity$MyTask.onPostExecute(MainActivity.java:112)
at com.test.activities.MainActivity$MyTask.onPostExecute(MainActivity.java:1)
at android.os.AsyncTask.finish(AsyncTask.java:417)
at android.os.AsyncTask.access$300(AsyncTask.java:127)
at android.os.AsyncTask$InternalHandler.handleMessage(AsyncTask.java:429)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:130)
at android.os.HandlerThread.run(HandlerThread.java:60)

Looking at the call stack, following call indicates that AsyncTask class was loaded on non-UI thread.
at android.os.HandlerThread.run(HandlerThread.java:60)
One of the Threading Rules mentioned in AsyncTask reference documentation is:
The AsyncTask class must be loaded on the UI thread. This is done
automatically as of JELLY_BEAN.
Here is a bug related to this issue.
One of the solution is, load the AsyncTask class manually in Application onCreate method. This ensures the class gets loaded on main thread, before any application components uses it.
public class MyApplication extends Application {
#Override
public void onCreate() {
try {
Class.forName("android.os.AsyncTask");
} catch (ClassNotFoundException e) {
}
super.onCreate();
}
}
Remember to specify MyApplication in AndroidManifest file.

The solution in this answer should force the code to be run on the ui thread. It uses the Activity#runOnUiThread(Runnable) function. Your code would end up like this:
#Override
protected void onPostExecute(User[] users) {
if (isCancelled() || isFinishing()) return;
runOnUiThread(new Runnable() {
#Override
public void run() {
findViewById(R.id.panelViews).removeAllViews();
findViewById(R.id.element).setText("something");
// ... more
}
});
}

This occurs when you start an AsyncTask from a background thread. The onPreExecute and onPostExecute don't automatically run on the main thread but in reality the thread the AsyncTask was started from.
In other words, if you were already within a background thread when you called execute on the AsyncTask then that same thread is where the onPostExecute method is called from meaning you would get that exception.

Try this, This should solve your problem, perform your task in runOnUiThread()
#Override
protected void onPostExecute(User[] users) {
runOnUiThread(new Runnable() {
public void run() {
if (isCancelled() || isFinishing()) return;
findViewById(R.id.panelViews).removeAllViews();
findViewById(R.id.element).setText("something");
}//run
}//runOnUiThread
}//onPostExecute

Related

updating a view from a thread in Android

I have a layout with a TextView say mainTextView.
My activity file looks something like :
public class SecondActivity extends AppCompatActivity {
private static Integer i = 0;
private TextView tv = null;
#override
public void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
tv = (TextView) findViewById(R.id.mainTextView);
new MyThread().execute();
}
private void notifyAChange () {
tv.setText(i.toString());
}
private class MyThread extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground (String... params) {
while (true) {
try {
i++;
Thread.sleep(1000);
notifyAChange();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
This returns a FATAL EXCEPTION on runtime. I know I can't touch a view from another Thread except the original one but in that case I am touching the view from the main thread so what is wrong ?
Nope you are in the doInBackground part of the AsyncTask (worker thread).
You are calling notifyAChange() form worker thread, not main UI thread.
You should update the UI from onPostExecute. Or you can also use runOnUiThread for the part updating the view.
Main use of asyncktask to perform long running task so there are three methods preExecute to set progress bar befor starting executing task, doinbackground to perform task like download data (main thread),post execute to perform task after completion of task
you can only change UI component from Post execute while using asynctask.
For more info related to asynktask refer https://developer.android.com/reference/android/os/AsyncTask.html
and for Implementation and explanation refer AsynkTask ImplementationUpdate UI from Thread
you can also use RunonUI therad method and Handler

Weird behavior with asynctask

so I am coming across a weird problem I cant find an explaination for. I have an async task in which in its doBackground method does a wait until a certain variable is set then the "wait" is notified
private class TestAsyncTask extends AsyncTask<Void, Object, Boolean> {
#Override
protected void onPreExecute() {
Log.d("Test1");
}
#Override
protected Boolean doInBackground(Void... params) {
Log.d("Test2");
while (nextCardToPlay == null) {
wait();
}
Log.d("Test3");
}
}
Activity A:
protected void onCreate(){
a = new TestAsyncTask().execute();
}
protected void onPause(){
a.cancel()
}
So as you can see when the activity starts, the asyncTask is started. When activity is closed the asyncTask is supposed to be cancelled.
What I noticed is that if I open the activity, close it, and reopen it again then the asynctask is created and in wait mode (never cancelled). No problem. Whats confusing is that when I start the activity (while the stale asyncTask is there), then it seems a new asyncTask is started ( because the logs from OnPreExecute are called) however the doInBackground in the nextAsyncTask is not executed because the Test2 log is not showing.
Any idea why?
This behavior is not at all weird if you look at the documentation, which states the AsyncTasks run on a single background thread, i.e. sequentially. If you really want your tasks to run on parallel worker threads, then use the executeOnExecutor() method instead of a simple execute() and pass it the AsyncTask.THREAD_POOL_EXECUTOR parameter.

Thread is stopping until the execution of a method is complete. (Android)

I'm trying to do an Android game using a Thread which repeats a loop for draw, move and others.
I have a problem with the execution of a method, which searches a value with a "do while" loop. When this method is executed, the thread does not continue until this process does not end.
What would be the best option for avoid this? Make another thread within that method? If you can give an example I'd really appreciate it.
Here's some pseudocode:
void mainLoop(){
drawElements();
moveElements();
//...
//...
reposition();
}
void reposition(){
// this stops my thread
do{
// do stuff
}while(!end);
// do stuff
}
As wqrahd suggested use AsyncTask.
I assume mainLoop is a main UI thread.
public class RepositionClass extends AsyncTask {
private Context mContext;
public RepositionClass(Context context) {
mContext = context;
}
#Override
protected void onPreExecute() {
// do UI related here, this function will run in main thread context.
}
#Override
protected Boolean doInBackground(String... params) {
// call non-ui(computation intensive) part of reposition function here.
return true;
}
#Override
protected void onPostExecute(Boolean result) {
// do UI related part of reposition function here.
}
}
Creating another thread won't help you if you still have to block and wait for the loop to complete the search. The problem really is what is happening in the "do stuff" loop, you just need to optimize that to solve the issue.
use asyntask and in asyntask's doInBackground , do your thread work and in asyntask's onPostExecute call your repositionMethod.

Android AsyncTask onPostExecute off of main ui thread

I'm having an issue with AsyncTask and onPostExecute. I am finding that onPostExecute is executing on a different thread than the main ui thread, which is causing a CalledFromWrongThreadException to happen when I modify any views.
I put in some logging to see what threads onPreExecute, doInBackground, and onPostExecute are running on. I would see a result like this...
onPreExecute ThreadId: 1
doInBackground ThreadId: 25
onPostExecute ThreadId: 18
I believe the main ui thread id is 1 and I would expect both onPre and onPost to both execute on thread 1. I am making sure to create and also call the execute method from the ui thread (for example in onCreate of an Activity).
Another thing to note that I have noticed is that later async tasks will run their onPostExecute method on the same thread as previous async task onPostExecute methods (in this case thread 18).
Right now in order to get around this I am wrapping the code in my onPostExecute methods in a call to runOnUiThread, but I think this is hacky and would like to get to the real issue.
I am out of ideas! Any one have any insight? I'm happy to answer any questions that could helper with further investigation!
EDIT:
There are two ways that async tasks are being run in the code. I am wondering if the latter in these examples is causing something weird to happen?
public class SomeActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
new SomeAsyncTask().execute();
}
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> {
#Override
public void onPreExecute() {
Thread.currentThread().getId() // 1
//Show a dialog
}
#Override
public Integer doInBackground(String... params) {
Thread.currentThread().getId() // 25
return 0;
}
#Override
public void onPostExecute(Integer result) {
Thread.currentThread().getId() // 18
//hide dialog
//update text view -> CalledFromWrongThreadException!!!
}
}
}
The above seems like a vanilla use of AsyncTask, but I still see this issue occurring even in simple cases like this. The next example uses an async task to run other async tasks. Maybe there is something I don't know about what happens when an async task gets constructed that is causing some weird behavior?
public class SomeActivity extends Activity implements TaskRunner.OnFinishListener {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
TaskRunner taskRunner = new TaskRunner();
taskRunner.setOnFinishListener(this);
taskRunner.addTask(new SingleTask());
taskRunner.addTask(new SingleTask());
taskRunner.execute();
}
#Override
public void onTaskFinish(List<Integer> results) {
//Thread id is 18 when it should be 1
//do something to a view - CalledFromWrongThreadException!!
}
}
//In a different file
public class SingleTask extends AsyncTask<String, Void, Integer> {
//This is a an async task so we can run it separately as an asynctask
//Or run it on whatever thread runnerExecute is called on
#Override
public Integer doInBackground(String... params) {
return runnerExecute(params);
}
//Can be called outside of doInBackground
public Integer runnerExecute(String... params) {
//some long running task
return 0;
}
}
//In a different file
public class TaskRunner {
private List<SingleTask> tasks;
private OnFinishListener onFinishListener;
public interface OnFinishListener {
public void onTaskFinish(List<Integer> results);
}
public TaskRunner() {
this.tasks = new ArrayList<SingleTask>();
}
public void setOnFinishListener(OnFinishListener listener) {
this.onFinishListener = listener;
}
public void addTask(SingleTask task) {
tasks.add(task);
}
public void executeTasks() {
new RunnerTask().execute((SingleTask[]) tasks.toArray());
}
//Calls the runnerExecute method on each SingleTask
private class RunnerTask extends AsyncTask<SingleTask, Integer, List<Integer>> {
#Override
public void onPreExecute() {
//Runs on thread 1
}
#Override
public List<Integer> doInBackground(SingleTask... params) {
//Runs on arbitrary thread
List<Integer> results = new ArrayList<Integer>();
for(SingleTask task : params) {
int result =task.runnerExecute(task.getParams());
results.add(result);
}
return results;
}
#Override
public void onPostExecute(List<Integer> results) {
//Runs on thread 18
onFinishListener.onTaskFinish(results);
}
}
}
Maybe what is going on here is just super weird, and not at all how async tasks are meant to be used, either way it would be nice to get to the bottom of the issue.
Let me know if you need any more context.
I have been experiencing the same problem and it turned out the the issue was using Flurry 3.2.1. However, the issue is not limited to the Flurry library.
The issue behind the scenes is having the first ever (when the app is loaded for the first time) AsyncTask call from a looper thread which is not the Main UI thread. This call initializes a sHandler static variable in AsyncTask to the wrong thread id, and this id is then used in all subsequent AsyncTask$onPostExecute() calls.
To solve the problem, I call an empty (do-nothing) AsyncTask on first app load, just to initialize AsyncTask correctly.
try using:
getBaseContext().runOnUiThread(new Runnable()
{
#override
public void run()
{
}
});
and write your code inside the run function
The AsyncTask is designed to be used from the main thread. Your problem is the second case, and is that you call execute on the SingleTask from a background thread. You call it in the doInBackground method of RunnerTask. The onPostExecute is then run from the backgroundthread of RunnerTask
Two options for you.
1: Trash RunnerTask, and execute the SingleTasks from you main thread, they'll all run in parallell and you won't know which finishes first, but onPreExecute and onPostExecute is called on the main thread
2: Trash the SingleTask and define them as Runnables instead, then you can run them in sequence in the RunnerTask's doInBackground. They'll all run in the background thread of RunnerTask, in the order you call Run. When it is finished, the onPostExecute of RunnerTask is run on the main thread.
i just tried your code and onPreExecute and onPostExecute does run on the same thread, how do you output the thread id ? try:
Log.d("THREADTEST","PRE"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","BACKGROUND"+Long.toString(Thread.currentThread().getId()));
Log.d("THREADTEST","POST"+Long.toString(Thread.currentThread().getId()));
P.S. it should be:
new SomeAsyncTask().execute();
and
private class SomeAsyncTask extends AsyncTask<String, Void, Integer> { ... }
you are actually executing the SingleTask from RunnerTask's doinbackground method which is incorrect as asynctask should be executed from a main thread only. You need to relook into the logic which runs the set of SingleTasks from RunnerTask.

AsyncTask cannot work in thread android

I use AsyncTask to change text of TextView like this:
private class LongOperation extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... urls) {
String response = "";
for (String url : urls) {
response += url;
}
return response;
}
#Override
protected void onPostExecute(String result) {
textView.setText(result);
}
}
Everything will fine if I call it in OnClick event:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
textView = (TextView) findViewById(R.id.txt);
Button button = (Button)this.findViewById(R.id.button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new LongOperation().execute(new String[]{"Hello"});
}
});
}
But the problem when I called it in my thread, the program forced close
this.closeButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Thread t= new Thread(){
#Override
public void run(){
try{
//Do something
//Then call AsyncTask
new LongOperation().execute(new String[]{"Hello"});
}catch(Exception e){}
}
};
t.start();
}
});
Where am I wrong? I dont' understand how difference call AsyncTask in thread or not.
I recommend you consult the AsyncTask documentation and Processes and Threads for a better understanding of how it works. Essentially, you should create your AsyncTask subclass on the main thread.
When you call AsyncTask.execute(), your provided, AsyncTask.onPreExecute is called on the main thread, so you can do UI setup.
Next AsyncTask.doInBackground method is called, and runs in its own thread.
Finally, when your AsyncTask.doInBackground method completes, a call is made to AsyncTask.onPostExecute on the main thread, and you can do any UI cleanup.
If you need to update the UI from within your AsyncTask.doInBackground method, call AsyncTask.publishProgress, which will invoke onProgressUpdate in the main thread.
When you call it from the UI thread, the associated Context is the running Activity. When you call it from a regular thread, there is no valid Context associated with that thread. AsyncTask executes in its own thread, you shouldn't be creating its own thread. If that is actual code, then you have missunderstood the point of AsyncTask. Search for tutorials on how to use it.
Adding to what the others have said: I think you can use AsyncTask to launch off a task in another thread, even if you start the AsyncTask from a different thread than the UI already.
But in that case, the only way you'll only be able to modify the UI indirectly, for example: pass the handler of the current Activity somehow to this AsyncTask instance, and send messages to it (handler messages get processed on the UI thread). Or use broadcast intents that the Activity catches and updates the UI accordingly, etc. These solutions seem to be overkills though.

Categories

Resources