I have an Android application which sends some data to a webservice. I need to send this data as soon as the application is closed or sent to background.. But how do I accomplish this?
My current solution is to run it on the OnPause() on my home activity, but I need this to run no matter which activity the user is on when closing the app.. Is this possible or do I have to add the OnPause method to all activities?
Check this solution first https://stackoverflow.com/a/5862048/1037294 before you decide to use the code below!
To check if your application is sent to background, you can call this code on onPause() or onStop() on every activity in your application:
/**
* Checks if the application is being sent in the background (i.e behind
* another application's Activity).
*
* #param context the context
* #return <code>true</code> if another application will be above this one.
*/
public static boolean isApplicationSentToBackground(final Context context) {
ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> tasks = am.getRunningTasks(1);
if (!tasks.isEmpty()) {
ComponentName topActivity = tasks.get(0).topActivity;
if (!topActivity.getPackageName().equals(context.getPackageName())) {
return true;
}
}
return false;
}
For this to work you should include this in your AndroidManifest.xml
<uses-permission android:name="android.permission.GET_TASKS" />
This is the method that I used and it seems to work pretty well:
I have a top level Application class of my own that extends Application as such
public class MyApplication extends Application implements Application.ActivityLifecycleCallbacks {
You also need to register this Application object in your manifest file:
<application android:label="#string/app_name" android:icon="#drawable/ic_launcher" android:name=".MyApplication">
Notice how I also implement the ActivityLifeCycleCallbacks interface. This interface has the following methods:
public static interface ActivityLifecycleCallbacks {
void onActivityCreated(android.app.Activity activity, android.os.Bundle bundle);
void onActivityStarted(android.app.Activity activity);
void onActivityResumed(android.app.Activity activity);
void onActivityPaused(android.app.Activity activity);
void onActivityStopped(android.app.Activity activity);
void onActivitySaveInstanceState(android.app.Activity activity, android.os.Bundle bundle);
void onActivityDestroyed(android.app.Activity activity);
}
You need to implement those methods and then register for these events in your applications onCreate() as follows
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
This will then call the callback (the MyApplication object) whenever an activity lifecycle method happens such as onCreate(), onPause etc.
In your onActivityPaused() you can then check if the app was backgrounded or not by calling #peceps method : isApplicationSentToBackground(...)
This is what my code looks like then...
/**
* Application.ActivityLifecycleCallbacks methods
*/
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
try {
boolean foreground = new ForegroundCheckTask().execute(getApplicationContext()).get();
if(!foreground) {
//App is in Background - do what you want
}
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
Create a new class to go the Foreground checking (which is an async. task). See check android application is in foreground or not? for more.
class ForegroundCheckTask extends AsyncTask<Context, Void, Boolean> {
#Override
protected Boolean doInBackground(Context... params) {
final Context context = params[0];
return isAppOnForeground(context);
}
private boolean isAppOnForeground(Context context) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningAppProcessInfo> appProcesses = activityManager.getRunningAppProcesses();
if (appProcesses == null) {
return false;
}
final String packageName = context.getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
if (appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND && appProcess.processName.equals(packageName)) {
return true;
}
}
return false;
}
}
Edit
This answer only serves for one purpose, that is, running a code in onPause() for all activities. It doesn't let you run a code when your app is sent to background.
Original Answer
Make an Activity named YourBasicActivity and override its onPause() method and extend every Activity from YourBasicActivity
Maybe this can be helpfull, tell me if it worked for you.
only when you return from background the value of activities would be 0 (zero)
the rest of the times would be a number higher than 0(zero) when the onRestart()
is executed.
public class FatherClass extends Activity {
private static int activities = 0;
public void onCreate(Bundle savedInstanceState, String clase) {
super.onCreate(savedInstanceState);
}
protected void onRestart()
{
super.onRestart();
if(activities == 0){
Log.i("APP","BACK FROM BACKGROUND");
}
}
protected void onStop(){
super.onStop();
activities = activities - 1;
}
protected void onStart(){
super.onStart();
activities = activities + 1;
}
}
All of your classes must extend from this class for this to work.
Explanation: The onStart is executed one the activity is "visible" and the onStop when the activity is "not visible". So when your APP (it says APP not activity) goes to background all the activities are "not visible" so they execute the onStop method, so the idea behind this is to ADD ONE each time an activity es started, and SUBTRACT ONE each time an activity es hided, so if the value of the variable "activities" is zero that means that all the activities that where started in some point are now not visible, so when you APP returns from background and executes the onRestart method on the activity in "front" you can check whether comes from background or is just restarting an activity.
you can use onAppForegroundStateChange() method which call when app is open and closed.this method is only called when your app comes in foreground/background.
onAppForegroundStateChange() method is better then you used onPause() method because onPause method is also called every time when you go to other activity.
you can use this method like that
public class MyApplication extends Application {
#Override
public void onCreate() {
super.onCreate();
AppForegroundStateManager.getInstance().addListener(this);
}
#Override
public void onAppForegroundStateChange(AppForegroundStateManager.AppForegroundState newState) {
if (AppForegroundStateManager.AppForegroundState.IN_FOREGROUND == newState) {
// App just entered the foreground. Do something here!
} else {
// App just entered the background. Do something here!
}
}
}
override the onStop() method of your Home activity and run the code there.
I think you need to run your own thread which will check whether all running activities in background or destroyed.
MyBasicActivity extends Activity
{
private static ArrayList<MyBasicActivity> activities=new ArrayList<MyBasicActivities);
private boolean started;
public void onCreate()
{
activities.add(this);
}
public void onDestroy()
{
activities.remove(this);
}
public void onStart()
{
this.started=true;
}
public void onPause()
{
this.started=false;
}
public boolean isStarted()
{
return started;
}
}
MyThread implements Runnable
{
private ArrayList<MyBasicActivity> activities;
public MyThread(ArrayList<MyBasicActivity> activities)
{
this.activities=activities;
}
void run()
{
while(!stopped)
{
boolean inBackground=true;
for(MyBasicActivity activity:activities)
{
if(activity.isStarted())
{
inBackground=false;
break;
}
}
if(inBackground)
//run your code here;
sleep(10000); //10 secs
}
}
}
If you are trying to submit/save data the user input, there are better ways to go about it than doing it when he tries to close the app. There are many ways to close an app. The user could even turn down the phone. So it's hard to take precautions against all of them.
I'd suggest you submit the data everytime the user stops writing, every any number of seconds or when he presses a button for example if your call to the webservice is too slow.
This way it's more safeproof and it's easier to implement.
Related
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
}
}
}
}
}
Problem
The idea is very simple. Whenever an user comes back to my app from the Recents I want to show a simple dialog prompting with the password.
I know how to prompt the dialog with password, but my problem is how do I understand that the user has entered my app from the recents. If I put the prompt in the onResume in every activity, then it will get triggered everytime even if the user doesn't enter from the Recents menu.
There are lots of activities and fragments in my app. So, I would love to have a more generic or application level solution.
Implement Application.ActivityLifecycleCallbacks, that will provide all activity callback in your application class.
public class AppController extends Application implements
Application.ActivityLifecycleCallbacks
{
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
#Override
public void onActivityCreated(Activity activity, Bundle bundle) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle bundle) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
}
You could try with this flag FLAG_ACTIVITY_LAUNCHER_FROM _HISTORY:
if((getIntent().getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY )!=0){
Log.d(TAG, "Called from history");
//clear flag from history
Intent intent = getIntent().setFlags( getIntent().getFlags() & (~ Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY));
setIntent(intent);
}
Source : Android - detecting application launch from home or history
When "A" Activity is start from recent, this flag is present.
Now this flag will be also present if "A" activity call "B" activity and on "B" user press back.
So you have to check flag and when you detect it you have clear intent by removing this flag, source: Remove a Paint Flag in Android
Try below sample
/**
* TODO : After update to API level 14 (Android 4.0),
* We should implement Application.ActivityLifecycleCallbacks
*/
public class GlobalApplication extends android.app.Application
{
private boolean inForeground = true;
private int resumed = 0;
private int paused = 0;
public void onActivityResumed( Activity activity )
{
++resumed;
if( !inForeground )
{
// Don't check for foreground or background right away
// finishing an activity and starting a new one will trigger to many
// foreground <---> background switches
//
// In half a second call foregroundOrBackground
}
}
public void onActivityPaused( Activity activity )
{
++paused;
if( inForeground )
{
// Don't check for foreground or background right away
// finishing an activity and starting a new one will trigger to many
// foreground <---> background switches
//
// In half a second call foregroundOrBackground
}
}
public void foregroundOrBackground()
{
if( paused >= resumed && inForeground )
{
inForeground = false;
}
else if( resumed > paused && !inForeground )
{
inForeground = true;
}
}
}
Put below code in your all activities.
public class BaseActivity extends android.app.Activity
{
private GlobalApplication globalApplication;
#Override
protected void onCreate()
{
globalApplication = (GlobalApplication) getApplication();
}
#Override
protected void onResume()
{
super.onResume();
globalApplication.onActivityResumed(this);
}
#Override
protected void onPause()
{
super.onPause();
globalApplication.onActivityPaused(this);
}
#Override
public void onDestroy()
{
super.onDestroy();
}
}
I would suggest using LifecycleObserver. If your Application class implements this interface it marks a class as a LifecycleObserver, it does not have any methods, instead, it relies on OnLifecycleEvent annotated methods. The usage is simple:
public class AndroidApplication extends Application implements LifecycleObserver {
#OnLifecycleEvent(Lifecycle.Event.ON_START)
public void onAppStart() {
//enter code here
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
public void onAppStop() {
//enter code here
}
...etc
}
With Lifecycle.Event you can access all lifecycle states through Enum. It is part of androidx.
I want to record state of application when it is resumed/created from different point of entry.
app is opened from app drawer
app is opened from notification
app is opened from open apps (Long home press)
app is resumed from other screen
I know it can be traced by generating a base activity and overriding resume/pause events, but I have bulk of activity present in app. so is there any short method to get the state of application?
I thought about creating a service and continuously checking current running tasks, but I can only use this approach if I found a way to run the service only when my activity is in visible state. (for that to bind service in each activity is not practical for me)
Your suggested solution sounds good to me:
public class YourBaseActivity extends Activity {
#Override
protected void onResume() {
super.onResume();
ActivityManager mgr = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
List<ActivityManager.RunningTaskInfo> tasks = mgr.getRunningTasks(1);
tasks.get(0).topActivity.getClassName();
//Do what you need to do with it...
}
}
And let all your activities extends this one instead of Activity should work for you
EDIT
Another way to do it:
Create your own application class and implement Application.ActivityLifecycleCallbacks, a working example of the code:
public class ApplicationTest extends Application implements Application.ActivityLifecycleCallbacks {
#Override
public void onCreate() {
super.onCreate();
registerActivityLifecycleCallbacks(this);
}
#Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
}
#Override
public void onActivityDestroyed(Activity activity) {
}
#Override
public void onActivityPaused(Activity activity) {
}
#Override
public void onActivityResumed(Activity activity) {
Log.d("testing", "onActivityResumed");
}
#Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
#Override
public void onActivityStarted(Activity activity) {
}
#Override
public void onActivityStopped(Activity activity) {
}
}
And reference this class in your manifest:
<application
...
android:name="com.example.testing.ApplicationTest" >
I'm trying to call a method when a user launch my application (no matter it is a fresh launch, or a return to it after hiding by home buttton)
For iOS, we can put the method in "applicationDidBecomeActive" in AppDelegate.m, so the method will be called when app launches.
However, for Android, if we put the method in onResume() in the 1st activity, not only app launch will call the method, backing to the the 1st activity from other activities in the same app will also call the method. I don't want this happen, I just want the method to be called when app launches.
I've asked it in the past but seems no answer on Android.
Any solutions? Thanks a lot.
You can implement your own Application class. MyApplication extends Application and set it as your Application in the manifest file AndroidManifest.xml.
<application
android:name="MyApplication"
.
.
. >
</application>
In MyApplication Class, implement onCreate() and onTerminate() methods.
onCreate() method called when the application is starting, before any activity, service, or receiver objects (excluding content providers) have been created.
See the docs for Application.
There is no method that is called only when the app returns from background, but you could implement something that you could implement something like this to see if the app started from background or it was first started. Create a general activity that will be extended by all the other activities and override onStart():
public abstract class CustomActivity extends FragmentActivity {
public static int ACTIVITIES_RUNNING = 0;
#Override
protected void onStart() {
super.onStart();
if (ACTIVITIES_RUNNING == 0) {
//app came from background
//start whatever you want
}
Const.ACTIVITIES_RUNNING++;
}
}
First create singleton for counting activities in foreground
public class ActivitiesCounter {
public interface ApplicationLaunchListener{
public void onLaunch();
}
private int mCounter = 0;
private ApplicationLaunchListener mListener;
private static ActivitiesCounter mInstance = new ActivitiesCounter();
public static ActivitiesCounter getInstance(){
return mInstance;
}
public void increase(){
if(mCounter == 0){
if(mListener != null){
mListener.onLaunch();
}
}
mCounter++;
}
public void decrease(){
mCounter--;
}
public void setApplicationLaunchListener(ApplicationLaunchListener listener){
mListener = listener;
}
}
Then activity:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ActivitiesCounter.getInstance().setApplicationLaunchListener(new ActivitiesCounter.ApplicationLaunchListener() {
#Override
public void onLaunch() {
Toast.makeText(MyActivity.this, "launched", Toast.LENGTH_LONG).show();
}
});
findViewById(R.id.btn_activity_b).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(MyActivity.this, ActivityB.class);
startActivity(intent);
}
});
}
#Override
protected void onStart() {
super.onStart();
ActivitiesCounter.getInstance().increase();
}
#Override
protected void onStop() {
ActivitiesCounter.getInstance().decrease();
super.onStop();
}
}
Activity B also should increase and decrease counter
public class ActivityB extends Activity {
#Override
protected void onStart() {
super.onStart();
ActivitiesCounter.getInstance().increase();
}
#Override
protected void onStop() {
ActivitiesCounter.getInstance().decrease();
super.onStop();
}
}
It's better create BaseActivity (with onStart, onStop) for all activities in your app (then, you don't need override onStart, onStop every time)
You can define a superclass for all your activities and track the state of the app. If all activities are in stopped state - app in the background, otherwise - in the foreground. In onStart() and onStop() methods of your super activity you can increment and decrement the number of visible activites. Then in onStart() check if there was any currently visible activites. If no - app becomes active and you can call your method:
public class SuperActivity extends Activity {
private static int mVisibleActivitiesCount;
#Override
public void onStart(){
super.onStart();
if (mVisibleActivitiesCount == 0) {
onAppBecomesActive();
}
mVisibleActivitiesCount++;
}
#Override
public void onStop(){
super.onStart();
mVisibleActivitiesCount--;
}
private void onAppBecomesActive() {
// Do some staff
}
}
Open the androidManifest.xml from the Manifests Folder
Choose the Activity you want to open when app start
Add these codes to that Activity
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
Remember to removes this code from the activity in which its already present(usually MainActivity)
My application has a refresh button on the main activity. When the user presses that button, a new thread is created which starts updating the SQLite database. When this thread started, user could possibly get into another activies of the application.
The problem is these other activities(ListActivity) should be updated according to the DB when that background thread is completed. How could I provide that. I tried getting current task with ActivityManager but It requires extra permission which I dont want.
Edit:
Sorry seems I misunderstood you. Please take a look at the following code, it is similar to Chinaski's (you just use an interface for the callback methods) but I added a bit more to ensure you know how to use it in a way that will avoid memory leaks.
Note how the activity detaches during onDestroy -- alternatively you could use a WeakReference, however these days you'd use a Fragment with setRetainInstance(true) and completely avoid the detaching/attaching as the fragment would be retained.
MyAsyncTask
public class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private Callback mCallback;
private boolean mIsComplete = false;
private boolean mHasCallbackBeenCalled = false;
public MyBackgroundTask(Callback callback) {
mCallback = callback;
}
/** Only safe to call this from the UI thread */
public void attach(Callback callback) {
mCallback = callback;
if (mIsComplete && !mHasCallbackBeenCalled) {
fireCallback();
}
}
/** Only safe to call this from the UI thread */
public void detach() {
mCallback = callback;
}
#Override
public void doInBackground() {
// do the heavy stuff here
return null;
}
#Override
public void onPostExecute(Void result) {
mIsComplete = true;
fireCallback();
}
private void fireCallback() {
if (mCallback != null) {
mCallback.callbackMethod();
mHasCallbackBeenCalled = true;
}
}
public static interface Callback {
public void callbackMethod();
}
}
MyActivity
public class MyActivity extends Activity implements MyAsyncTask.Callback {
private MyAsyncTask mTask;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Check for a retained task after a configuration change
// e.g. a rotation
if (getLastNonConfigurationInstance() != null) {
mTask = (MyAsyncTask) getLastNonConfigurationInstance();
// Re-attach the task
mTask.attach(this);
}
}
#Override
public void onDestroy() {
// Detach from task to avoid memory leak
if (mTask != null) {
mTask.detach();
}
super.onDestroy();
}
#Override
public Object onRetainNonConfigurationInstance() {
// Retain the async task duration a rotation
return mTask;
}
/** Callback method */
#Override
public void callbackMethod() {
// Do something here
}
}
You could make a singleton in which you will have your thread and a queue of "tasks". When a task is finished, you check / launch the next task, and when you add a task, you launch it, or add it in the queue if a task is already running.
I don't say this is the best solution, but it's one.