14

I have an Activity that starts an AsyncTask. The activity is allowed to show in Portrait or Landscape orientation. When the orientation is changed, the Activity is destroyed and recreated. The task continues working no matter how many times the orientation is changed. It also returns the results to the activity successfully (according to CommonsWare's answer here http://goo.gl/WF1yW).

What I want to achieve is: when the activity is destroyed because the application is closing - the task should be cancelled. However, when the activity is destroyed because of an orientation change - the task should NOT be cancelled.

Basically the question is how to distinguish between the two cases: application is closing / orientation change. In both cases the onDestroy() method is called and there is no easy way to check something like isChangingOrientation()...

P.S. I can also consider a totally different approach if necessary.

Stan
  • 2,151
  • 1
  • 25
  • 33

6 Answers6

10

you can use isFinishing() method , to check if the activity is going to be killed or onDestroy() method just called due to change in orientation

@Override
protected void onDestroy() {
    super.onDestroy();
    if(isFinishing()){
        Log.i("DEBUG", "App will Terminate ");
    }else{
        Log.i("DEBUG", "Orientation changed");
    }

}
Mina Fawzy
  • 20,852
  • 17
  • 133
  • 156
7

I found a somewhat satisfying solution, which I want to share.

The method onRetainNonConfigurationInstance() is called only when the Activity is being recreated. More specifically: Called by the system, as part of destroying an activity due to a configuration change, when it is known that a new instance will immediately be created for the new configuration.

I override this method and store a flag (stating whether it has been called or not). Then in onDestroy (called shortly after) I check this flag and if it's false I cancel the background task, because the Activity is being destroyed for good. If it's true this means that onRetainNonConfigurationInstance() has been called, which means that the Activity is being recreated, so I leave the task running.

I would have liked a better solution, but could not find such. The problems with this solution are two: the method is deprecated; there is no guarantee that the method will be called (according to the documentation). In practice the solution works for me, so I'll use it...

Stan
  • 2,151
  • 1
  • 25
  • 33
3

In general, you don't want to define onConfigurationChanged() because it's so hard to get everything right. The best approach is to let the app be killed and recreated when the orientation changes.

To make the transition easier, you can implement onRetainNonConfigurationInstance(). This method will be called by the system when it knows that your app will be restarted almost immediately. In onRetainNonConfigurationInstance(), you pass any arbitrary object back to the system ('this' is a reasonable choice). Then, in your onCreate() method, you call getLastNonConfigurationInstance() to get the previously-saved object. This allows you to very quickly and easily restore your state from the previous invocation. I believe even running threads and open sockets can be passed across this way.

See Save cache when rotate device for more info.

Community
  • 1
  • 1
Edward Falk
  • 9,991
  • 11
  • 77
  • 112
  • Thanks! That's almost exactly what I am doing. I use onRetainNonConfigurationInstance() to store the reference to the thread during the Activity recreation. It's a bit unfortunate that there are two downsides to this... I'll post it in a new answer. – Stan Mar 06 '12 at 20:16
  • 1
    Why is this answer accepted? The question was about how to how to distinguish between configuration change and app termination when `onDestroy` is called. And you are just telling how to obtain a previously-saved object. I think Mina Fawzy's answer is more appropriate. Also take into account that `onDestroy` is not guaranteed to be called. – Stan Mots Nov 06 '16 at 10:55
2

Take a look here for a better understanding of the Android lifecycle: http://developer.android.com/reference/android/app/Activity.html#ActivityLifecycle

You can use onConfigurationChanged() to detect orientation changes in your Activity. You can use the onDestroy() method to determine when your Activity is about to be killed.

triad
  • 20,407
  • 13
  • 45
  • 50
  • Thanks. However, I know about the Activity Lifecycle. That's why I don't want to use the onConfigurationChanged() method. Using it means that I have to disable destroying and recreation on orientation change. However, I want to keep the default behaviour about the activity lifecycle. – Stan Mar 05 '12 at 22:53
  • I believe you can use `onConfigurationChanged()` and have it perform it's default behavior if you call `super.onConfigurationChanged()` inside it ? – triad Mar 05 '12 at 22:57
  • You declare in the manifest whether you want the default behaviour or not. From the documentation: `Note that this will only be called if you have selected configurations you would like to handle with the configChanges attribute in your manifest. If any configuration change occurs that is not selected to be reported by that attribute, then instead of reporting it the system will stop and restart the activity` – Stan Mar 05 '12 at 23:07
  • I found a solution that works, but is not ideal. I'll share it later as an answer. – Stan Mar 06 '12 at 00:30
0

The best way I found after some research was to create an application class and rely on onTrimMemory() method. A problem with this approach - onTrimMemory() does not get called if screen times out or screen gets locked by pressing power button so I had to implement that logic separately.

/**
 * When your app's process resides in the background LRU list:
 * TRIM_MEMORY_BACKGROUND
 * TRIM_MEMORY_MODERATE
 * TRIM_MEMORY_COMPLETE
 *
 * When your app's visibility changes:
 * TRIM_MEMORY_UI_HIDDEN
 */
@Override
public void onTrimMemory(final int level) {
    super.onTrimMemory(level);
    if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
            || level == ComponentCallbacks2.TRIM_MEMORY_BACKGROUND
            || level == ComponentCallbacks2.TRIM_MEMORY_COMPLETE
            || level == ComponentCallbacks2.TRIM_MEMORY_MODERATE) {

        // App went in background

    }
}

Following code is to detect screen lock. I implemented this code in one of the ActivityLifecycleCallbacks method - onActivityStopped()

final PowerManager powerManager = (PowerManager) getAppContext().getSystemService(Context.POWER_SERVICE);
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
    if (!powerManager.isScreenOn()) {
        // Screen locked
    }
} else {
    if (!powerManager.isInteractive()) {
        // Screen locked    
    }
}
Sohail
  • 1,137
  • 2
  • 12
  • 22
-1

If your app targets API level 13 or higher, you should set this config in the manifest.xml

<activity
android:configChanges="orientation|screenSize"
...
/>
Sampath Pasupunuri
  • 628
  • 1
  • 13
  • 25