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.