While reading the documentation, I found that The AsyncTask class must be loaded on the UI thread. But I am surprised that AsyncTask can also be executed from the worker thread.
So the question is:
If AsyncTask can also execute from the background thread, Why in the documentation they are saying just opposite to it.
How could it possible to have context on onPostExecute.?
new Thread(new Runnable() {
#Override
public void run() {
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... voids) {
return null;
}
#Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
Toast.makeText(getBaseContext(), "in AsyncTask...", Toast.LENGTH_SHORT).show();
}
}.execute();
}
}).start();
The AsyncTask Android class lets us sort of bind background tasks to the UI thread. So using this class, you can perform background operations and then publish the results to the UI thread that updates the UI components.
you can understand AsyncTask execution briefly : http://codetheory.in/android-asynctask/
There is two Threads Main Thread and Worker Thread. Asyntasck works on worker thread.Mostly Asynctask used for background Task.It works on worker thread and publish the result on Main thread.
If Main thread block for 5 sec or more than that Application not responding (ANR) dialogs comes so to avoid this scenario for background task Asynctask used
Related
I have written the following code within the main class for an async worker
private class Renderer extends AsyncTask<Void, Void, Void>{
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
Toast.makeText(getApplicationContext(), "works... ", Toast.LENGTH_LONG).show();
return null;
}
protected void onProgressUpdate(Integer... progress) {
}
protected void onPostExecute(Long result) {
}
}
But calling this crashes the app
public void render(View v)
{
new Renderer().execute();
}
Can some one tell me what's going wrong?
You can't do a Toast in doInBackground, you will get Can't create handler inside thread that has not called Looper.prepare().
If it's just for testing at this stage (which your code seems to imply), consider instead just writing some log output for now.
Alternatively put your Toast either onPreExecute or onPostExecute
More info: How to raise a toast in AsyncTask, I am prompted to used the Looper
You are showing toast in doInbackground which is not possible. Do it in onPostExecute
protected void onPostExecute(Long result) {
Toast.makeText(getApplicationContext(), "works... ", Toast.LENGTH_LONG).show();
}
doInbackground is invoked on the background thread. You should update ui on the ui thread. You can alos display toast in onProgressUpdate.
More info at
http://developer.android.com/reference/android/os/AsyncTask.html
Check the topic under the section The 4 steps.
You can't access UI thread from background thread.
An AsyncTask is a helper around Threads and Handlers. It helps you to perform a background task and update the UI regularly.
Thus, the methods of AsyncTask run on different Threads:
doInBackground runs in a background Thread
onProgressUpdate and onPostExecute run on the UI Thread.
Now, you can only modify the UI in the UI Thread (=main Thread). This is why your code leads to a crash. Simply moving your Toast into onPostExecute will solve your problem.
runOnUiThread(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "works... ", Toast.LENGTH_LONG).show();
}
});`
Try to do all ui work on main thread.
The following were supposed to be the same if I am not mistaking.
Using AsyncTask:
private class GetDataTask extends AsyncTask<String, Void, String>{
#Override
protected void onPreExecute() {
}
#Override
protected String doInBackground(String... params) {
return NetConnection.getRecordData(mUserId, mUserPassword);
}
#Override
protected void onPostExecute(String result) {
parseJson(result);
}
}
Using a Thread:
new Thread( new Runnable() {
#Override
public void run() {
String res = NetConnection. getRecordData(mUserId, mUserPassword);
parseJson(res);
}
}).start();
But when uploading a file, the AsyncTask runs synchronously while the Thread run asynchronously(in parallel).
Why is so? Why AsyncTask behaves like this? Isn't AsyncTask supposed to run asynchronously?
I am little confused so I need your help.
This is how I invoke the GetDataTask:
new GetDataTask().execute()
I prefer using AsyncTask but it is not doing the job for me. Please refer to my early question for more details
As of 4.x calling 2 AsyncTasks will cause them to be executed serially.
One way to fix this is using the following code
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.HONEYCOMB) {
myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
}
else {
myTask.execute();
}
You can read more at: http://commonsware.com/blog/2012/04/20/asynctask-threading-regression-confirmed.html
Ok following are the notes from the official java doc...
Order of execution
When first introduced, AsyncTasks were executed serially on a single
background thread. Starting with DONUT, this was changed to a pool of
threads allowing multiple tasks to operate in parallel. Starting with
HONEYCOMB, tasks are executed on a single thread to avoid common
application errors caused by parallel execution.
If you truly want parallel execution, you can invoke
executeOnExecutor(java.util.concurrent.Executor, Object[]) with THREAD_POOL_EXECUTOR.
SO if you invoke two AsyncTask together.. they would not be executed in parallel (exception is donut, encliar and gingerbread)... You can use executeOnExecutor to execute them in parallel...
From your code we can see you have called parseJson(result); in onPostExecute() of AsyncTask which runs in MainUIThread of Applications. So at that point your code runs Synchronously..
Put parseJson(result); method in doInBackGround() Which runs only in other worker thread.
While you have called same thing in Thread. So both
String res = NetConnection. getRecordData(mUserId, mUserPassword);
parseJson(res);
Runs in other worker thread out of MAinUiThread on which you experienced Asynchronously.
Note:
But be sure your parseJson(res); doesn't update UI while it is in doInBackground().
Looks like that actual problem is not in file uploading but in parseJson method.
In your Thread example you parsing Json in separate thread while in AsyncTask case you parsing Json in UI thread.
Any suggestions for this kind of error which occurs when a UI thread is loaded with lots of loops and logical operations?
get the error message through data/anr/trace.txt from File explorer. And, also put all logical operations and loops into separate threads.
That ANR error happens when you are doing intensive work on UI thread and do not allow the user interface to refresh. Your description is a perfect match for this case.
To fix it run those operations on a different thread. You can also use AsyncTask if that's convenient in your situation. See http://developer.android.com/reference/android/os/AsyncTask.html
private class LongWork extends AsyncTask<Void, Integer, Void>
{
#Override
protected Void doInBackground(Void... arg0) {
//Do a long Task here
return null;
}
#Override
protected void onPostExecute(Void result) {
//Do what you have to do on the UI Thread
}
}
Then
LongWork work=new LongWork();
work.execute();
I created an asynctask and in its doInBackground() method i started a thread like this:
private class myAsyntask extends Asynctask{
doInBackground(){
Thread t = new Thread(new Runnable(){
public void run()
{
while(someBoolean!=true){
Thread.currentThread.sleep(100);
}
}
});
}
onPostExecute(){
//do something related to that variable
}
}
problem I am facing is after 1st iteration of Thread.sleep() , onPostExecute() is called , instead I thought that asynctask will run this thread on background and when that boolean is true onPostexecute() is called.I am not able to understand why this happens ?
AsyncTask automatically creates a new Thread for you, so everything you do in doInBackground() is on another thread.
What you are doing is this:
AsyncTask creates a new Thread and runs doInBackground().
a new Thread (t) is created from the AsyncTask-Thread.
doInBackground() is completed, as all it does is create the Thread t and thus jumps to onPostExecute().
Thread t would still be running in the background (however, you do not call start() on t, meaning that it is not started).
Instead you want your doInBackground() method to look something like this:
doInBackground(){
while(someBoolean!=true){
//Perform some repeating action.
Thread.sleep(100);
}
}
First of all, in your code you don't even start thread t, so all that happens in doInBackground is creation of new thread and then moving on to onPostExecute().
Secondly, you don't even need separate thread, since doInBackground() handles this for you, so you can just use something like
doInBackground(){
while(someBoolean!=true){
Thread.currentThread.sleep(100);
}
}
if you wish, however, to stick with separate thread, you can start thread and wait for it's completion by using .join(); like
doInBackground(){
Thread t = new Thread(new Runnable(){
public void run() {
while(someBoolean!=true){
Thread.currentThread.sleep(100);
}
}
});
t.start();
t.join();
}
onPostExecute can only be called when doInBackground has return-ed. In your code, the only possible way this can happen is sleep throwing an Exception (InterruptedException??)
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.