I have an AsyncTask where I make request to server and download some data. And according to received data I start Activity A or Activity B in onPostExecute().
The problem is that doesn't work. No errors, simply activity doesn't start. Seems like code isn't executed there at all.
private class LoadingAsyncTask extends AsyncTask<Boolean, Void, Boolean> {
protected Void doInBackground(Boolean... params) {
if (InternetConnection.isConnected(ActivityLoading.this)) {
.....load data
} else {
Toast.makeText(ActivityLoading.this, getString(R.string.no_internet_connection), Toast.LENGTH_SHORT).show();
}
isDone = true;
return isDone;
}
protected void onPostExecute(Void isDone) {
super.onPostExecute(isDone);
//if work is done
if (isDone) {
if (dwnlData.equals("success")) {
getApplicationContext().startActivity(new Intent(ActivityLoading.this, ActivityNavigation.class));
} else {
getApplicationContext().startActivity(new Intent(ActivityLoading.this, ActivityAuthorization.class));
}
}
}
I call AsyncTask in onCreate:
public class ActivityLoading extends ActionBarActivity {
static boolean isDone = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_loading);
new LoadingAsyncTask().execute();
}
Change the argument data type void to boolean
protected void onPostExecute(boolean isDone) {
super.onPostExecute(isDone);
//if work is done
if (isDone) {
if (dwnlData.equals("success")) {
getApplicationContext().startActivity(new Intent(ActivityLoading.this, ActivityNavigation.class));
} else {
getApplicationContext().startActivity(new Intent(ActivityLoading.this, ActivityAuthorization.class));
}
}
Did you try making isDone a boolean?
Edit - You could just make doInBackground return nothing, and stop using the argument in onPostExecute.
I suggest using Log.d() or Toast() inside those if{} blocks to verify that your conditions are true
Related
I have a method which has a async task and it is called from several different services and those services runs periodically . I want to make sure when one task is on going , no other thing can call it unless the task is finished.
public class Helper {
public static void doSomethingAsync() {
new AsyncTask<String, Void, Integer>() {
#Override
protected void onPreExecute() {
}
#Override
protected Integer doInBackground(String... strings) {
//doing something
}
#Override
protected void onPostExecute(Integer resultCode) {
}
}.execute();
}
public static void someOtherMethod(){
}
}
AsyncTask.getStatus() will give the status of the task, i.e. whether finished or not.
Declare a member variable
private static AsyncTask<String, Void, Integer> mTask;
Modify your method as,
public static void doSomethingAsync() {
if(null != mTask && mTask.getStatus() != AsyncTask.Status.FINISHED){
return; //Returning as the current task execution is not finished yet.
}
mTask = new AsyncTask<String, Void, Integer>() {
#Override
protected void onPreExecute() {
}
#Override
protected Integer doInBackground(String... strings) {
//doing something
}
#Override
protected void onPostExecute(Integer resultCode) {
}
};
mTask.execute();
}
You can try this by making your "doSomethingAsync()" as synchronized method.
public synchronized static void doSomethingAsync() {
new AsyncTask<String, Void, Integer>() {
#Override
protected void onPreExecute() {
}
#Override
protected Integer doInBackground(String... strings) {
//doing something
}
#Override
protected void onPostExecute(Integer resultCode) {
}
}.execute();
}
Note:
static synchronized method will lock the class instead of the object, and it will lock the class because the keyword static means: "class instead of instance". The keyword synchronized means that only one thread can access the method at a time.
And together they mean: "Only one can access class at one time".
Make sure all your services has access to same Mutex which can be accesses throughout your application.
Now before accessing the Async Task, do as follows
public class Mutex {
public void acquire() throws InterruptedException { }
public void release() { }
public boolean attempt(long msec) throws InterruptedException { }
}
Which then can be used as:
try {
mutex.acquire();
try {
// do something
} finally {
mutex.release();
}
} catch(InterruptedException ie) {
// ...
}
Check out more details over here
In Android we have Semaphore. For which the following will be the steps
java.util.concurrent.Semaphore semaphore=new Semaphore(1);
boolean isAvailable = semaphore.tryAcquire();
if(isAvailable){
// Access AsyncTask
}
Once all actions are done, till onPostExecute
semaphore.release();
Try using an IntentService instead as this supports queuing and runs in a background thread
You should refer Synchronization concept, it will help u.
Link
In the OnCreate method, I have invoked 3 AsyncTask which basically fills data for the 3 Spinners. What I need is that I should have the Login button disabled till all 3 tasks finish. How can I achieve that ?
new SpinnerDataFetcher("GetFreeDrivers1",(Spinner)findViewById(R.id.Spinner_1)).execute();
new SpinnerDataFetcher("GetFreeDrivers2",(Spinner)findViewById(R.id.Spinner_2)).execute();
new SpinnerDataFetcher("GetFreeDrivers3",(Spinner)findViewById(R.id.Spinner_3)).execute();
Just increment a number that corresponds on how many AsyncTask are done.
int s = 0;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
new SpinnerDataFetcher(){
#Override
protected void onPostExecute(....) {
super.onPostExecute(...);
s++;
check();
}
}.execute();
new SpinnerDataFetcher(){
#Override
protected void onPostExecute(....) {
super.onPostExecute(...);
s++;
check();
}
}.execute();
new SpinnerDataFetcher(){
#Override
protected void onPostExecute(....) {
super.onPostExecute(...);
s++;
check();
}
}.execute();
}
public void check(){
if(s >=3){
s= 0;
// enable button here
}
}
Initialize your AsyncTask instance with a reference to the Activity/Fragment that creates it. Then signal back in onPostExecute when its done
e.g.
class Spinner1DataFetcher extends AsyncTask<...> {
public Spinner1DataFetch(YourActivityOrFragment activity) {
_activity = activity;
}
protected void onPostExecute(...) {
_activity.spinner_1_is_done();
}
}
For that you have to Disable Button before calling new Spinner1DateFetcher and call Second from Spinner1DateFetcher method onPostExecute and same as Third Spinner method and in Third Spinner onPostExecute set Button to Enable..
For Disable Button use
Button.setEnabled(false);
and For Enable Button use
Button.setEnabled(true);
Edit :
for the parameter check you have to add Constuctor and check the condition like below way.
private class MyAsyncTask extends AsyncTask<Void, Void, Void> {
public MyAsyncTask(boolean showLoading) {
super();
// do stuff
}
// doInBackground() et al.
}
There are multiple ways how you can achieve this.
The straightforward way to implement this is create a counter which will trigger UI update.
final InterfaceTrigger trigger = new InterfaceTrigger();
new AsyncTask<>() {
#Override
protected void onPostExecute(Object o) {
super.onPostExecute(o);
trigger.finishJob();
if (trigger.isTimeToUpdateUi()) {
// TODO update your UI
}
}
};
public class InterfaceTrigger {
private static final int THRESHOLD = 3;
private int counter;
public synchronized void finishJob() {
counter++;
}
public synchronized boolean isTimeToUpdateUi() {
return counter == THRESHOLD;
}
}
Another way is to use CyclicBarier and ExcutorService mechanism.
I've been using AsyncTasks for a while however, I've recently encountered a scenario where I'm unsure of how to handle correctly. Since I thought it would be a somewhat common scenario I decided to ask the question here.
So, I'm trying to use an AsyncTask to make a simple call to sign a user in to the app. After the call completes, if it succeeds, the user should be taken to another activity. This logic is simple. The problem arrises when the user navigates away from the app before the sign in call returns. In such a case, what should I do in onPostExecute()?
What I've seen some apps do is they continue with the call anyways, as long as the activity is still around, and will launch the next activity. However this creates a weird experience where the user navigates away from the app, then several seconds later, the app just pops back up in their face. Of course, I would like to avoid doing this.
Update
Example code:
public class ExampleActivity extends Activity {
private boolean mIsPaused;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}
made your AsyncTask class as static inner class.
Pretty interesting problem... Going with what you've started by using booleans, you could save the response the Activity receives to the SharedPreferences in the event it is paused, or continue processing normally if it is not. If the Activity later resumes (or is recreated), check whether or not there is a saved response and handle accordingly. I was thinking something along the lines of:
import org.json.JSONObject;
import android.app.Activity;
import android.os.Bundle;
public class TaskActivity extends Activity {
private static final String KEY_RESPONSE_JSON = "returned_response";
private boolean paused = false;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
// don't setup here, wait for onPostResume() to figure out what to do
}
#Override
public void onPostResume(){
super.onPostResume();
paused = false;
if(isSavedResponseAvailable()) processResponse(getSavedResponse());
else setup();
}
#Override
public void onPause(){
paused = true;
super.onPause();
}
private void setup(){
// normal setup
}
public void onReceiveResponse(JSONObject response){
if(paused) setSavedResponse(response);
else processResponse(response);
}
private void processResponse(JSONObject response){
// Continue with processing as if they never left
getSharedPreferences(this.getClass().getName(), 0).edit().clear().commit(); // Clear everything so re-entering won't parse old data
}
private boolean isSavedResponseAvailable(){
return getSavedResponse() != null;
}
private JSONObject getSavedResponse(){
try{
return new JSONObject(getSharedPreferences(this.getClass().getName(), 0).getString(KEY_RESPONSE_JSON, ""));
}
catch(Exception e){ }
return null;
}
private void setSavedResponse(JSONObject response){
getSharedPreferences(this.getClass().getName(), 0).edit().putString(KEY_RESPONSE_JSON, response.toString()).commit();
}
}
Clearly that's assuming your response from the task is JSON, but there's no reason you couldn't extend that to save the data individually and rebuild the necessary response object from the saved preference data.
As far as clean approaches go, though... I give this about a 3/10, but I can't think of anything better (well, other than making the TaskActivity abstract and forcing implementations to override setup(), processResponse(), isResponseAvailable(), getSavedResponse(), and setSavedResponse(), but that would only be mildly better for like a 4/10)
I would suggest putting a try/catch statement in the post execute - as far as I know what would happen in this situation is that you would get some kind of Window Manager exception.
What I would STRONGLY recommend, however, is stopping any async tasks (with the cancel method) on the onPause method, meaning that you won't interrupt them.
http://developer.android.com/reference/android/os/AsyncTask.html#cancel(boolean)
public final boolean cancel (boolean mayInterruptIfRunning)
Added in API level 3
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
Parameters
mayInterruptIfRunning true if the thread executing this task should be interrupted; otherwise, in-progress tasks are allowed to complete.
Returns
false if the task could not be cancelled, typically because it has already completed normally; true otherwise
See Also
isCancelled()
onCancelled(Object)
boolean isRunning; //set it to true in onResume, and false in onStop
boolean isWaiting; // set it to true in onPostExecute, if "isRunning" is false
check in onResume whether isWaiting is true, if yes, take user to another screen.
Use the cancel() of AsynchTask class onBackPress() of Activty class
public class ExampleActivity extends Activity {
private boolean mIsPaused;
SignInTask singleTaskObj;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Button btnSignIn = (Button) findViewById(R.id.btn_sign_in);
btnSignIn.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
singleTaskObj = new SignInTask(ExampleActivity.this).execute();
}
});
...
}
#Override
protected void onPause() {
super.onPause();
mIsPaused = true;
}
#Override
protected void onResume() {
super.onResume();
mIsPaused = false;
}
protected void onBackPressed()
{
singleTaskObj.cancel();
}
private boolean isPaused() {
return mIsPaused;
}
...
private static class SignInTask extends AsyncTask<Void, Void, SomeResult> {
private final WeakReference<ExampleActivity> mAct;
public SignInTask(ExampleActivity act) {
mAct = new WeakReference<ExampleActivity>(act);
}
#Override
protected SomeResult doInBackground(Void... params) {
return mApi.signIn(creds);
}
#Override
protected void onPostExecute(SomeResult result) {
if (result.getCode() == OK) {
ExampleActivity act = mAct.get();
if (act != null) {
if (act.isPaused()) {
// do something
} else {
startActivity(new Intent(act, NextActivity.class));
}
} else {
// do something
}
}
}
}
}
I need to cancel my asyncthread . In my application I am doing some heavy calculations, and I want to give user ability to cancel calculations(and then retry). I read on forums, that you can't just stop task from what is it doing, and that you need to check if task isCancelled=true inside your DoinBackground code. But that doesn't work for me.
Task itself is working great and it outputs correct data if I leaved it to end on itself.
In my App first I call function naredi_pdf_start(view), then when the task is running, if I call close_pdf1(view), it gives me an error.(I am changing views and app can't find my pdf_text1 Textview when calling publishProgress- null pointer exception). I really dont know how to use task.cancel(true) method (in my case: start_pdf.cancel(true))).
Here is my code:
String progress_pdf;
naredi_pdf start_pdf;
public void naredi_pdf_start(View view) {
start_pdf=new naredi_pdf();
start_pdf.execute();
}
public void close_pdf1(View view) {
if(start_pdf!=null) {
Log.v("not null","not null");
start_pdf.cancel(true);
setContentView(R.layout.other_view); //This is where
//I don't have TextView pdf_text1
}
}
private class naredi_pdf extends AsyncTask<Void, String, Void> {
protected Void doInBackground( Void... ignoredParams ) {
progress_pdf="Calculating Statistical Data";
//A LOT OF CODING
for(int i = 0; i < 1; i++) {
if(isCancelled()) {
break;
}
else {
publishProgress("Calculating team statistics");
}
}
//MORE OF CODING
for (int i = 0; i < 1; i++) {
if (isCancelled()) {
break;
}
else {
publishProgress("Calculating player's BIO");
}
}
//MORE OF CODING
for (int i = 0; i < 1; i++) {
if (isCancelled()) {
break;
}
else {
publishProgress("Calculating player's individual performance");
}
}
return null;
}
protected void onPostExecute( Void array ) {
//saving to database
}
protected void onProgressUpdate(String... values) {
progress_pdf=values[0]+"\n"+progress_pdf;
if (isCancelled()) {
}
else {
TextView pdf_text1 = (TextView) findViewById (R.id.pdf_text1);
pdf_text1.setText(progress_pdf);
// dialog(view);
}
}
}
Your problem is not that you can't cancel the AsyncTask. You probably get NullPointerException because your call to setContentView() goes through before AsyncTask.cancel() has been successful. A onProgressUpdate() gets called, only to find that the layout is now changed and there is no Viewwith id=R.id.pdf_text1!
From documentation on AsyncTask.
A task can be cancelled at any time by invoking cancel(boolean). Invoking this method will cause subsequent calls to isCancelled() to return true. After invoking this method, onCancelled(Object), instead of onPostExecute(Object) will be invoked after doInBackground(Object[]) returns. To ensure that a task is cancelled as quickly as possible, you should always check the return value of isCancelled() periodically from doInBackground(Object[]), if possible (inside a loop for instance.)
Since onCancelled() runs on the UI thread, and you are certain that no subsequent calls to onProgressUpdate() will occure, it's is a great place to call setContentView().
Override onCancelled() in you AsyncTask
private class naredi_pdf extends AsyncTask<Void, String, Void> {
protected Void doInBackground( Void... ignoredParams ) { // YOUR CODE HERE}
protected void onPostExecute( Void array ) { // YOUR CODE HERE}
protected void onProgressUpdate(String... values) {// YOUR CODE HERE}
// ADD THIS
#Override
protected void onCancelled() {
// Do not call super.onCancelled()!
// Set the new layout
setContentView(R.id.other_layout);
}
}
Change close_pdf1()
public void close_pdf1(View view) {
if(start_pdf!=null) {
Log.v("not null","not null");
start_pdf.cancel(true);
}
}
And you should have an AsyncTask that automatically changes your layout when cancelled. Hopefully you should not encounter any NullPointerException either. Haven't tried the code though :)
Edit
If you feel fancy, follow Rezooms advice on using return.
for(int i = 0; i < 1; i++) {
if(isCancelled()) {
return null;
}
.
.
.
}
The return statement cancels the execution of the doInBackground method, not break.
isCancelled is a propietary method of AsyncTask class.
You should define a private boolean property on your extended class, do something like this
private class myAsyncTask extends AsyncTask<Void, String, Void> {
private boolean isTaskCancelled = false;
public void cancelTask(){
isTaskCancelled = true;
}
private boolean isTaskCancelled(){
return isTaskCancelled;
}
protected Void doInBackground( Void... ignoredParams ) {
//Do some stuff
if (isTaskCancelled()){
return;
}
}
protected void onPostExecute( Void array )
{
//Do something
}
protected void onProgressUpdate(String... values)
{
//Do something
}
}
I have AsyncTask and my progress in background is in infinite loop. But I need to stop AsyncTask when user stop my app or go back from my app (my app go to foreground). How can I do it?
Solution:
private boolean done = false;
private class CurTask extends AsyncTask<String, Void, Object> {
protected Void doInBackground(String... args) {
while(!done){
DefaultCurProgress();
publishProgress();
}
}
protected void onProgressUpdate(Void...unused) {
textCur = (TextView)findViewById(R.id.text_cur);
SharedPreferences myPrefs = MyActivity.this.getSharedPreferences("myPrefs", MODE_PRIVATE);
String prefNameDefaultCur = myPrefs.getString(DefaultCur, "");
textCur.setText(prefNameDefaultCur);
}
}
#Override
public void onPause(){
super.onPause();
done=true;
}
#Override
public void onResume(){
super.onResume();
done=false;
}
Declare a method within your AsyncTask that tells it to end. Something like so:
private class CurTask extends AsyncTask<String, Void, Object> {
private boolean done = false;
public void quit() {
done = true;
}
protected Void doInBackground(String... args) {
while(!done){
DefaultCurProgress();
publishProgress();
}
}
Add a boolean member variable and use that in your while condition instead of true.
Then when you need to cancel it you can just call an instance method to set this variable to false, the doInBackground method will return and the task will finish.