在onPause,onStop和onDestroy方法中调用超类方法的正确顺序是什么?为什么?

Anu*_*lla 83 java android android-lifecycle

我刚刚浏览了Android Developer Site,刷新了Activity Life周期,在每个代码示例中,超类方法旁边都有一条注释,"首先调用超类方法".

虽然这在创建半周期中有意义:onCreate,onStart和onResume,但我对于破坏半周期的正确程序有点困惑:onPause,onStop,onDestroy.

在破坏实例特定资源可能依赖的超类资源之前,首先销毁实例特定资源是有意义的,而不是反过来.但是注释建议不然.我错过了什么?

编辑:由于人们似乎对问题的意图感到困惑,我想知道的是以下哪一项是正确的?为什么?

1.Google建议

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

      //my implementation here
    }
Run Code Online (Sandbox Code Playgroud)

另一种方式

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

       super.onStop();  
    }
Run Code Online (Sandbox Code Playgroud)

Vik*_*ram 97

在破坏实例特定资源可能依赖的超类资源之前,首先销毁实例特定资源是有意义的,而不是反过来.但评论意见不然.我错过了什么?

在我看来:没有一件事.

Mark(又名CommonsWare on SO)的答案揭示了这个问题:链接 - 对超类方法的调用应该是第一个语句吗?.但是,你可以在他的回答中看到以下评论:

但为什么官方文档说:"总是先在onPause()中调用超类方法"?

回到原点.好吧,让我们从另一个角度来看待这个问题.我们知道,Java语言规范没有指定在调用的命令super.overridenMethod()必须放在(或如果呼叫必须在所有的放置).

如果是类Activity,super.overridenMethod()则需要并强制执行调用:

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

mCalled设置为true Activity.onStop().

现在,剩下要讨论的唯一细节是订购.

I also know that both work

当然.查看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;
}
Run Code Online (Sandbox Code Playgroud)

无论你把电话夹在哪里super.onPause(),你都会好的.Activity.onStop()有一个类似的方法体.但是看看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);
}
Run Code Online (Sandbox Code Playgroud)

在这里,排序可能很重要,具体取决于您的活动设置方式,以及调用是否super.onDestroy()会干扰后面的代码.

最后,该声明Always call the superclass method first似乎没有太多证据支持它.更糟糕的是(对于声明)以下代码取自android.app.ListActivity:

public class ListActivity extends Activity {

    ....

    @Override
    protected void onDestroy() {
        mHandler.removeCallbacks(mRequestFocus);
        super.onDestroy();
    }
    ....    
}
Run Code Online (Sandbox Code Playgroud)

并且,来自Android sdk中包含的LunarLander示例应用程序:

public class LunarLander extends Activity {

    ....

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

总结和有价值的提及:

用户Philip Sheard:提供一个场景,super.onPause()在活动开始使用时必须延迟调用startActivityForResult(Intent).使用setResult(...) after 设置结果super.onPause()将不起作用.他后来在评论中澄清了他的回答.

用户Sherif elKhatib:解释为什么让超类首先初始化其资源并从逻辑中消除其资源:

让我们考虑一下你下载的库,它有一个LocationActivity,它包含一个提供位置的getLocation()函数.最有可能的是,此活动需要在onCreate()中初始化其内容,这将迫使您首先调用super.onCreate.你已经这样做,因为你觉得它是有道理的.现在,在您的onDestroy中,您决定要将位置保存在SharedPreferences中的某个位置.如果首先调用super.onDestroy,则在此调用之后,getLocation将在某种程度上返回null值,因为LocationActivity的实现会使onDestroy中的位置值无效.这个想法是,如果发生这种情况,你不会责怪它.因此,在完成自己的onDestroy之后,最后会调用super.onDestroy.

他接着指出:如果子类与父类适当地隔离(就资源依赖性而言),则super.X()调用不需要遵守任何订单规范.

请参阅此页面上的答案,以了解super.onDestroy()调用放置确实影响程序逻辑的场景.

马克回答:

您覆盖的方法是组件创建的一部分(onCreate(),onStart(),onResume()等),您应该链接到超类作为第一个语句,以确保Android有机会在您之前完成其工作试图做一些依赖于已经完成的工作的事情.

你覆盖的方法是组件破坏的一部分(onPause(),onStop(),onDestroy()等),你应该先做你的工作,然后链接到超类作为最后一件事.这样,如果Android清理了您的工作所依赖的东西,您将首先完成您的工作.

返回void之外的其他方法(onCreateOptionsMenu()等),有时你链接到return语句中的超类,假设你没有专门做一些需要强制特定返回值的东西.

其他一切 - 比如onActivityResult() - 总体上取决于你.我倾向于链接到超类作为第一件事,但除非你遇到问题,以后链接应该没问题.

Bob Kerns来自这个帖子:

这是一个很好的模式[(Mark建议的模式)],但我发现了一些例外.例如, 我想要应用于我的PreferenceActivity的主题不会生效,除非我把它放在超类的onCreate()之前.

用户Steve Benett也对此有所关注:

我只知道一种情况,其中超级呼叫的时间是必要的.如果你想改变主题或显示的标准行为等onCreate,你必须在调用super看到效果之前这样做.否则AFAIK在你调用它时没有区别.

用户Sunil Mishra确认订单(很可能)在调用Activity类的方法时不起作用.他还声称,首先调用超类方法被认为是最佳实践.但是,我无法证实这一点.

用户LOG_TAG:解释为什么对超类构造函数的调用需要先于其他所有内容.在我看来,这个解释并没有增加被问到的问题.

结束说明:信任,但要核实.此页面上的大多数答案都遵循此方法,以查看该语句Always call the superclass method first是否具有逻辑支持.事实证明,它没有; 至少,不是类活动的情况.通常,应该通读超类的源代码来确定是否需要对super的方法进行排序调用.

  • 哇.谢谢你的指点.这和@ Sherif的答案都提供了重要的背景.如果您中的任何一方可以在此页面上总结答案,我会将其标记为已接受.请包括:1.此页面上的答案.2. @ Philip在本页面的回答3. @ CommonsWare在[本页]的回答(http://stackoverflow.com/questions/9625920/should-the-call-to-the-superclass-method-be-the-first -statement/9626268#9626268)4.[此讨论](https://groups.google.com/forum/#!topic/android-developers/ndsrBsIchFc)_我愿意,但我不想要你精彩的学分答案.干杯和谢谢_ (2认同)

She*_*tib 11

因为(你说)首先调用super onCreate是有道理的:想一想.

当我想创建时,我的超级创建其资源>我创建我的资源.

反过来:(排序堆栈)

当我想要毁灭时,我会破坏我的资源>我的超级破坏了他的资源.


从这个意义上讲,它适用于任何一些函数(onCreate/onDestroy,onResume/onPause,onStart/onStop).当然,onCreate将创建资源,而onDestroy将释放这些资源.顺便说一下,同样的证据适用于其他夫妻.

让我们考虑一下你下载的库,它有一个LocationActivity,它包含一个提供位置的getLocation()函数.最有可能的是,此活动需要在onCreate()中初始化其内容,这将迫使您首先调用super.onCreate.你已经这样做,因为你觉得它是有道理的.现在,在您的onDestroy中,您决定要将位置保存在SharedPreferences中的某个位置.如果首先调用super.onDestroy,则在此调用之后,getLocation将在某种程度上返回null值,因为LocationActivity的实现会使onDestroy中的位置值无效.这个想法是,如果发生这种情况,你不会责怪它.因此,在完成自己的onDestroy之后,最后会调用super.onDestroy.我希望这有点意义.

如果上述内容有意义,请考虑在任何时刻我们都有一项遵守上述概念的活动.如果我想扩展这个活动,我可能会采用相同的方式并遵循相同的顺序,因为相同的参数.

通过归纳,任何活动都应该做同样的事情.对于被迫遵循这些规则的活动,这是一个很好的抽象类:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,如果您的活动调用AnudeepBullaActivity扩展BaseActivity以及稍后,我想创建SherifElKhatibActivity扩展您的活动?我应该以什么顺序调用这些super.do函数?这最终是一回事.


至于你的问题:

我认为谷歌的目的是告诉我们:无论在哪里都请打电话给超级.当然,作为一般做法,请在开头称之为.谷歌当然拥有最聪明的工程师和开发人员,所以他们可能很好地隔离了他们的超级电话,而不是干扰孩子的电话.

我尝试了一下,可能不容易(因为谷歌我们试图证明是错误的)创建一个因为超级被调用而崩溃的活动.

为什么?

在这些函数中完成的任何操作对于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);
}
Run Code Online (Sandbox Code Playgroud)

mManagedCursors和mManagedDialogs以及mSearchManager都是私有字段.并且没有公共/受保护的api会受到此处所做的影响.

但是,在API 14中,添加了dispatchActivityDestroyed以将onActivityDestroyed分派给注册到Application的ActivityLifecycleCallbacks.因此,任何依赖于ActivityLifecycleCallbacks中某些逻辑的代码都会根据您调用super时的结果而产生不同的结果.例如:

创建一个计算当前正在运行的活动数的应用程序类:

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) {}
}
Run Code Online (Sandbox Code Playgroud)

以下可能没有意义,也可能不是一个好的做法,但它只是为了证明一个观点(人们可能会发现更真实的情况).创建MainActivity,当它完成时以及它是最后一个活动时应该转到GoodBye活动:

@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));
    }
}
Run Code Online (Sandbox Code Playgroud)

如果在onDestroy的开头调用super.onDestroy,将启动GoodBye活动.如果在onDestroy结束时调用super.onDestroy,则不会启动GoodBye活动.

当然,再次,这不是最佳的例子.然而,这表明谷歌在这里搞砸了一些.任何其他变量都不会影响您应用的行为.但是,将这些调度添加到onDestroy会导致super以某种方式干扰您的子类.

我说他们也因为不同的原因而搞砸了.他们不仅(在api 14之前)只接触超级调用最终和/或私有,但他们还调用了不同的内部函数(私有),然后真正调度onPause ...函数.

例如,performStopfunction是被调用的函数,它依次调用onStop函数:

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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,他们在此函数中的某处调用了Activity的onStop.因此,他们可能在调用onStop之前或之后放置所有代码(包含在super.onStop中),然后使用空的onStop超级函数通知子类关于onStop,甚至不添加SuperNotCalledException或检查调用此函数.

为此,如果他们在performDestroy中调用此调度到ActivityLifeCycle而不是在super.onDestroy结束时调用它,那么无论我们何时调用super,我们的活动行为都是一样的.

无论如何,这是他们做的第一件事(有点不对),它只在API 14中.