Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the correct order of calling superclass methods in onPause, onStop and onDestroy methods? and Why?

I was just going through the Android Developer Site, refreshing on the Activity Life cycle, and in each code example, there is a comment beside the super class methods that says "Always call the superclass method first".

Though this makes sense in the creation half cycle: onCreate, onStart and onResume, I'm a little confused as to what is the correct procedure on the destruction half cycle : onPause,onStop,onDestroy.

Destroying the instance specific resources first, before destroying superclass resources that the instance specific resources may depend upon makes sense, not the other way round.But the comments suggest otherwise. What am I missing?

Edit: Since people seem to be getting confused as to the intent in the question, what I want to know is which of the following is correct? AND WHY ?

1.Google suggests

    @Override
    protected void onStop() {
      super.onStop();  // Always call the superclass method first

      //my implementation here
    }

2.The other way

    @Override
    protected void onStop() {
       //my implementation here

       super.onStop();  
    }
like image 849
Anudeep Bulla Avatar asked Sep 16 '13 06:09

Anudeep Bulla


3 Answers

Destroying the instance specific resources first, before destroying superclass resources that the instance specific resources may depend upon makes sense, not the other way round. But the comments suggest otherwise. What am I missing?

In my opinion: not a single thing.

This answer from Mark (aka CommonsWare on SO) sheds light on the issue: Link - Should the call to the superclass method be the first statement?. But then, you can see the following comment left on his answer:

But why official doc says: "Always call the superclass method first" in onPause()?

Back to square one. Okay, let's look at this from another angle. We know that Java Language Specification does not specify an order in which the call to super.overridenMethod() must be placed (or if the call must be placed at all).

In case of class Activity, super.overridenMethod() calls are required and enforced:

if (!mCalled) {
    throw new SuperNotCalledException(
        "Activity " + mComponent.toShortString() +
            " did not call through to super.onStop()");
}

mCalled is set to true in Activity.onStop().

Now, the only detail left to debate on is the ordering.

I also know that both work

Sure. Look at the method body for Activity.onPause():

protected void onPause() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this);

    // This is to invoke 
    // Application.ActivityLifecyleCallbacks.onActivityPaused(Activity)
    getApplication().dispatchActivityPaused(this);

    // The flag to enforce calling of this method
    mCalled = true;
}

Whichever way you sandwich the call to super.onPause(), you'll be ok. Activity.onStop() has a similar method body. But take a look at Activity.onDestroy():

protected void onDestroy() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this);
    mCalled = true;

    // dismiss any dialogs we are managing.
    if (mManagedDialogs != null) {
        final int numDialogs = mManagedDialogs.size();
        for (int i = 0; i < numDialogs; i++) {
            final ManagedDialog md = mManagedDialogs.valueAt(i);
            if (md.mDialog.isShowing()) {
                md.mDialog.dismiss();
            }
        }
        mManagedDialogs = null;
    }

    // close any cursors we are managing.
    synchronized (mManagedCursors) {
        int numCursors = mManagedCursors.size();
        for (int i = 0; i < numCursors; i++) {
            ManagedCursor c = mManagedCursors.get(i);
            if (c != null) {
                c.mCursor.close();
            }
        }
        mManagedCursors.clear();
    }

    // Close any open search dialog
    if (mSearchManager != null) {
        mSearchManager.stopSearch();
    }

    getApplication().dispatchActivityDestroyed(this);
}

Here, the ordering could possibly matter depending on how your activity is setup, and whether calling super.onDestroy() would interfere with the code that follows.

As a final word, the statement Always call the superclass method first doesn't seem to have much evidence to back it up. What's worse (for the statement) is that the following code has been taken from android.app.ListActivity:

public class ListActivity extends Activity {

    ....

    @Override
    protected void onDestroy() {
        mHandler.removeCallbacks(mRequestFocus);
        super.onDestroy();
    }
    ....    
}

And, from LunarLander sample application included in android sdk:

public class LunarLander extends Activity {

    ....

    @Override
    protected void onPause() {
        mLunarView.getThread().pause(); // pause game when Activity pauses
        super.onPause();
    }
    ....
}

Summary and worthy mentions:

User Philip Sheard : Provides a scenario where a call to super.onPause() must be delayed in case of an Activity started using startActivityForResult(Intent). Setting the result using setResult(...) after super.onPause() will not work. He later clarifies on this in the comments to his answer.

User Sherif elKhatib : Explains why letting superclass initialize its resources first and destroy its resources last follows from logic:

Let us consider a library you downloaded which has a LocationActivity that contains a getLocation() function that provides the location. Most probably, this activity will need to initialize its stuff in the onCreate() which will force you to call the super.onCreate first. You already do that because you feel it makes sense. Now, in your onDestroy, you decide you want to save the Location somewhere in the SharedPreferences. If you call super.onDestroy first, it is to a certain extent possible that getLocation will return a null value after this call because the implementation of LocationActivity nullifies the location value in the onDestroy. The idea is that you wouldn't blame it if this happens. Therefore, you would call super.onDestroy at the end after you're done with your own onDestroy.

He goes on to point out: if a child class is suitably isolated (in terms of resource dependency) from the parent class, the super.X() calls need not adhere to any order specification.

See his answer on this page to read through a scenario where placement of super.onDestroy() call does affect the program logic.

From an answer by Mark:

Methods you override that are part of component creation (onCreate(), onStart(), onResume(), etc.), you should chain to the superclass as the first statement, to ensure that Android has its chance to do its work before you attempt to do something that relies upon that work having been done.

Methods you override that are part of component destruction (onPause(), onStop(), onDestroy(), etc.), you should do your work first and chain to the superclass as the last thing. That way, in case Android cleans up something that your work depends upon, you will have done your work first.

Methods that return something other than void (onCreateOptionsMenu(), etc.), sometimes you chain to the superclass in the return statement, assuming that you are not specifically doing something that needs to force a particular return value.

Everything else -- such as onActivityResult() -- is up to you, on the whole. I tend to chain to the superclass as the first thing, but unless you are running into problems, chaining later should be fine.

Bob Kerns from this thread:

It's a good pattern [(the pattern that Mark suggests above)], but I've found some exceptions. For example, the theme I wanted to apply to my PreferenceActivity wouldn't take effect unless I put it before the superclass's onCreate().

User Steve Benett also brings attention to this:

I only know one situation, where the timing of the super call is necessary. If you wanna alter the standard behavior of the theme or the display and such in onCreate, you have to do it before you call super to see an effect. Otherwise AFAIK there is no difference at which time you call it.

User Sunil Mishra confirms that order (most likely) does not play a role when calling Activity class' methods. He also claims that calling superclass methods first is considered a best practice. However, I could not corroborate this.

User LOG_TAG : Explains why a call to superclass constructor needs to be the before everything else. In my opinion, this explanation does not add to the question being asked.

End note: Trust, but verify. Most of the answers on this page follow this approach to see if the statement Always call the superclass method first has logical backing. As it turns out, it does not; at least, not in the case of class Activity . Generally, one should read through the superclass' source code to determine if ordering calls to super's methods is a requirement.

like image 119
Vikram Avatar answered Oct 20 '22 10:10

Vikram


Since (you say) it makes sense to call super onCreate first: Think about it.

When I want to create, My super creates its resources > I create my resources.

Inversely: (sort of a stack)

When I want to destroy, I destroy my resources > My super destroys his resources.


In this sense, it applies to any couple of functions (onCreate/onDestroy, onResume/onPause, onStart/onStop). Naturally, onCreate will create resources and onDestroy will free these resources. By the way, the same proof applies to the other couples.

Let us consider a library you downloaded which has a LocationActivity that contains a getLocation() function that provides the location. Most probably, this activity will need to initialize its stuff in the onCreate() which will force you to call the super.onCreate first. You already do that because you feel it makes sense. Now, in your onDestroy, you decide you want to save the Location somewhere in the SharedPreferences. If you call super.onDestroy first, it is to a certain extent possible that getLocation will return a null value after this call because the implementation of LocationActivity nullifies the location value in the onDestroy. The idea is that you wouldn't blame it if this happens. Therefore, you would call super.onDestroy at the end after you're done with your own onDestroy. I hope this makes a bit sense.

If the above makes sense, consider that at any moment we have an activity that abides by the above concept. If I want to extend this activity, I will probably feel the same way and follow the same ordering because of the same exact argument.

By induction, any activity should do the same thing. Here is a good abstract class for an activity forced to follow these rules:

package mobi.sherif.base;

import android.app.Activity;
import android.os.Bundle;

public abstract class BaseActivity extends Activity {
    protected abstract void doCreate(Bundle savedInstanceState);
    protected abstract void doDestroy();
    protected abstract void doResume();
    protected abstract void doPause();
    protected abstract void doStart();
    protected abstract void doStop();
    protected abstract void doSaveInstanceState(Bundle outState);
    @Override
    protected final void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        doCreate(savedInstanceState);
    }
    @Override
    protected final void onDestroy() {
        doDestroy();
        super.onDestroy();
    }
    @Override
    protected final void onResume() {
        super.onResume();
        doResume();
    }
    @Override
    protected final void onPause() {
        doPause();
        super.onPause();
    }
    @Override
    protected final void onStop() {
        doStop();
        super.onStop();
    }
    @Override
    protected final void onStart() {
        super.onStart();
        doStart();
    }
    @Override
    protected final void onSaveInstanceState(Bundle outState) {
        doSaveInstanceState(outState);
        super.onSaveInstanceState(outState);
    }
}

Finally, what if your activity called AnudeepBullaActivity extends BaseActivity and later on, I want to create SherifElKhatibActivity that extends your activity? In what order should i call the super.do functions? It is ultimately the same thing.


As for your question:

I think that Google's intention is to tell us: Please call the super no matter where. As a general practice of course, call it in the beginning. Google of course has the brightest engineers and developers so they probably done a good job isolating their super calls and not interfering in the child calls.

I tried a bit and it is probably not easy (since it is Google we are trying to prove wrong) to create an activity that would crash simple because of When is super being called.

Why?

Anything done in these functions is really private to the Activity class and would never cause any conflict with your subclass. For example (onDestroy)

protected void onDestroy() {
    if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this);
    mCalled = true;

    // dismiss any dialogs we are managing.
    if (mManagedDialogs != null) {
        final int numDialogs = mManagedDialogs.size();
        for (int i = 0; i < numDialogs; i++) {
            final ManagedDialog md = mManagedDialogs.valueAt(i);
            if (md.mDialog.isShowing()) {
                md.mDialog.dismiss();
            }
        }
        mManagedDialogs = null;
    }

    // close any cursors we are managing.
    synchronized (mManagedCursors) {
        int numCursors = mManagedCursors.size();
        for (int i = 0; i < numCursors; i++) {
            ManagedCursor c = mManagedCursors.get(i);
            if (c != null) {
                c.mCursor.close();
            }
        }
        mManagedCursors.clear();
    }

    // Close any open search dialog
    if (mSearchManager != null) {
        mSearchManager.stopSearch();
    }

    getApplication().dispatchActivityDestroyed(this);
}

mManagedCursors and mManagedDialogs and mSearchManager are all private fields. And none of the public/protected api will be affected by what is done here.

However, in API 14, dispatchActivityDestroyed was added to dispatch an onActivityDestroyed to the ActivityLifecycleCallbacks registered to your Application. Therefore, any code that would depend on some logic in your ActivityLifecycleCallbacks will have a different outcome based on when you are calling the super. For example:

Create an Application Class that counts the number of currently running activities:

package mobi.shush;

import android.app.Activity;
import android.app.Application;
import android.app.Application.ActivityLifecycleCallbacks;
import android.os.Bundle;

public class SherifApplication extends Application implements ActivityLifecycleCallbacks {
    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(this);
    }
    public int getCount() {
        return count;
    }
    int count = 0;
    @Override
    public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
        count++;
    }
    @Override
    public void onActivityDestroyed(Activity activity) {
        count--;
    }
    @Override
    public void onActivityPaused(Activity activity) {}
    @Override
    public void onActivityResumed(Activity activity) {}
    @Override
    public void onActivitySaveInstanceState(Activity activity, Bundle outState)           {}
    @Override
    public void onActivityStarted(Activity activity) {}
    @Override
    public void onActivityStopped(Activity activity) {}
}

The following might not make sense or is not that of a good practice but it is just to prove a point (One might find a more real situation). Create the MainActivity that supposedly goes to GoodBye activity when it is finished and when it is the last activity:

@Override
protected void onDestroy() {
    super.onDestroy();
    if(((SherifApplication) getApplication()).getCount() == 0) {
        //i want to go to a certain activity when there are no other activities
        startActivity(new Intent(this, GoodBye.class));
    }
}

If you call super.onDestroy in the beginning of your onDestroy, the GoodBye activity will be launched. If you call super.onDestroy at the end of your onDestroy, the GoodBye activity will not be launched.

Of course, again, this is not the optimal example. However this shows that Google messed up a bit here. Any of the other variables would have not affected your app's behavior. However adding these dispatch to the onDestroy caused the super to somehow interfere with your subclass.

I say they messed for a different reason as well. Not only did they (before api 14) only touch in the super calls what is final and/or private, but they also called different internal functions (private) that really then dispatched the onPause... functions.

For example, performStop function is the function called that in turn calls the onStop function:

final void performStop() {
    if (mLoadersStarted) {
        mLoadersStarted = false;
        if (mLoaderManager != null) {
            if (!mChangingConfigurations) {
                mLoaderManager.doStop();
            } else {
                mLoaderManager.doRetain();
            }
        }
    }

    if (!mStopped) {
        if (mWindow != null) {
            mWindow.closeAllPanels();
        }

        if (mToken != null && mParent == null) {
            WindowManagerGlobal.getInstance().setStoppedState(mToken, true);
        }

        mFragments.dispatchStop();

        mCalled = false;
        mInstrumentation.callActivityOnStop(this);
        if (!mCalled) {
            throw new SuperNotCalledException(
                    "Activity " + mComponent.toShortString() +
                    " did not call through to super.onStop()");
        }

        synchronized (mManagedCursors) {
            final int N = mManagedCursors.size();
            for (int i=0; i<N; i++) {
                ManagedCursor mc = mManagedCursors.get(i);
                if (!mc.mReleased) {
                    mc.mCursor.deactivate();
                    mc.mReleased = true;
                }
            }
        }

        mStopped = true;
    }
    mResumed = false;
}

Notice that they call the Activity's onStop somewhere in this function. Therefore, they might have as well put all the code (included in super.onStop) before or after the call to onStop and then just notify subclasses about the onStop using empty onStop super functions and without even adding the SuperNotCalledException or checking for this called.

For this, if they called this dispatch to the ActivityLifeCycle in the performDestroy instead of calling it in the end of super.onDestroy, our activity's behavior would have been the same regardless of when we did call the super.

Anyway this is the first thing they do (a bit wrong) and it is only in API 14.

like image 12
Sherif elKhatib Avatar answered Oct 20 '22 12:10

Sherif elKhatib


You say that Google suggests method 1, however Dianne Hackborn, a well known Android framework engineer suggest otherwise see Google Forum Link.

It makes intuitive sense to call the super class last when destroying an instance in the onPause, onStop and onDestroy methods and first when creating an instance with the methods of onCreate, onResume and onStart.

like image 4
NickT Avatar answered Oct 20 '22 11:10

NickT