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

时间:2013-09-16 06:17:41

标签: java android android-lifecycle

我刚刚浏览了Android开发者网站,在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
    }

2.另一种方式

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

       super.onStop();  
    }

7 个答案:

答案 0 :(得分:89)

  

在销毁之前首先销毁实例特定资源   实例特定资源可能依赖的超类资源   有道理,而不是相反。但评论表明   除此以外。 我缺少什么?

在我看来:不是一件事。

Mark(又名CommonsWare on SO)的回答揭示了这个问题:Link - Should the call to the superclass method be the first statement?。但是,你可以在他的回答中看到以下评论:

  

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

回到原点。好的,让我们从另一个角度来看待这个问题。我们知道Java语言规范指定必须放置对super.overridenMethod()的调用的顺序(或者必须放置调用)。

如果是课程活动,则需要super.overridenMethod()次来电并强制执行

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

mCalledActivity.onStop()中设置为true。

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

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;
}

无论您将呼叫夹在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);
}

此处,排序可能可能很重要,具体取决于您的活动设置方式,以及调用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();
    }
    ....    
}

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

public class LunarLander extends Activity {

    ....

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

摘要和有价值的提及:

用户Philip Sheard :提供一个方案,如果使用super.onPause()启动活动,则必须延迟对startActivityForResult(Intent)的调用。在 setResult(...)之后使用super.onPause() 设置结果将不起作用。他后来在评论中澄清了他的回答。

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

  

让我们考虑一下您下载的具有LocationActivity的库   包含提供位置的getLocation()函数。   最有可能的是,此活动需要初始化其中的内容   onCreate()会强制你先调用super.onCreate 。您   已经这样做,因为你觉得它是有道理的。现在,在你的   onDestroy,你决定要将位置保存在某处   SharedPreferences。如果你先调用super.onDestroy,那就是a   在某种程度上,getLocation将返回null值   之后这个调用因为执行了LocationActivity   取消onDestroy中的位置值。这个想法就是你   如果发生这种情况,我不会责备它。 因此,你会打电话   在您完成自己的onDestroy之后,最终使用super.onDestroy。

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

请参阅此页面上的答案,了解super.onDestroy()调用的位置会影响程序逻辑的情况。

来自Mark的答案

  

覆盖的方法是组件创建的一部分(onCreate(),   onStart(),onResume()等),你应该链接到超类   第一个声明,以确保Android有机会做到这一点   在你尝试做一些依赖于这项工作的事情之前工作   已经完成了。

     

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

     

返回某些内容的方法   除了void(onCreateOptionsMenu()等),有时你链接到   返回语句中的超类,假设你不是   专门做一些需要强迫特定回报的事情   值。

     

其他所有内容 - 例如onActivityResult() - 取决于您,   总体上。我倾向于链接到超类作为第一件事,   但除非你遇到问题,否则以后应该链接   细

来自this thread

Bob Kerns

  

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

用户Steve Benett 也引起了人们的注意:

  

我只知道一种情况,超级电话的时间是   必要。 如果您想改变主题的标准行为或   在onCreate中显示等等,你必须在打电话之前这样做   超级看效果。否则AFAIK就没有区别了   哪个时候你称之为。

用户Sunil Mishra 确认订单(很可能)在调用活动类&#39;时没有发挥作用。方法。他还声称首先调用超类方法被视为最佳实践。但是,我无法证实这一点。

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

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

答案 1 :(得分:11)

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

当我想要创建时,我的超级创建其资源&gt;我创造了我的资源。

反过来:(某种堆叠)

当我想要毁灭时,我会破坏我的资源&gt;我的超级摧毁了他的资源。


从这个意义上说,它适用于任何一些函数(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);
    }
}

最后,如果您的活动名为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);
}

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) {}
}

以下可能没有意义,或者不是一个好的做法,但它只是为了证明一个观点(人们可能会发现更真实的情况)。创建MainActivity,它应该在GoodBye活动结束时以及最后一次活动时进入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));
    }
}

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

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

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

例如,performStop函数是被调用的函数,它依次调用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;
}

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

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

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

答案 2 :(得分:1)

两者都是正确的IMO

根据文档

  

派生类必须调用超类的此方法实现。       如果他们不这样做,将抛出异常。

当文档明确说明时,应始终调用

Super方法。

然而,您可以选择何时调用超级方法。

查看onPause

的来源
protected void onPause() {
    getApplication().dispatchActivityPaused(this);
    mCalled = true;
}

因此无论是在调用之前还是之后。你应该很好。

但是,对于最佳实践,您应首先调用它。

我推荐它主要作为一种保护机制:如果有异常,那么super实例方法就已经被调用了。

同样将这些调用放在第一行将有助于避免将来出错,例如删除方法中的代码并意外删除对超类的调用。

答案 3 :(得分:1)

要记住的最重要的事情是super.onPause()隐含地调用setResult(Activity.RESULT_CANCELED)。但setResult只能调用一次,所有后续调用都会被忽略。因此,如果您想将任何类型的结果反馈回父活动,则必须自己致电setResult之前致电super.onPause()。据我所知,这是最大的陷阱。

答案 4 :(得分:1)

你说Google建议使用方法1,但着名的Android框架工程师Dianne Hackborn建议不要看Google Forum Link

onPause onDestroy 中强销毁实例时,直接调用超级最后当使用 onCreate,onResume onStart 方法创建实例时,强>方法和第一

答案 5 :(得分:0)

需要回调的超级以将Activity置于系统内部的正确状态。

假设您启动了Activity并且系统调用了onCreate。现在你可以覆盖它,例如加载你的布局。但是为了系统流程你必须调用super,系统可以继续使用标准程序。这就是为什么如果你不打电话就会抛出异常。

这与onCreate中的实现无关。它只是系统的重要内容。如果没有ANR,你可以在任何回调中拥有无限循环,并且Activity将被捕获。因此,系统知道回调何时终止,而不是调用下一个回调。

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

但是,如果你没有充分的理由打破它,那么让系统做最好的事情就是将super放在回调的第一行,然后是你的代码。

答案 6 :(得分:0)

从java的角度来看,这里存在一些混淆的解决方案:

Why does this() and super() have to be the first statement in a constructor?

需要在子类的构造函数之前调用父类的构造函数。这将确保如果在构造函数中调用父类的任何方法,则父类已经正确设置。

你正在尝试做的,通过参数传递给超级构造是完全合法的,你只需要构建那些ARGS在线,你在做什么,或者将它们传递到您的构造函数,然后将它们传递到超:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                super(myArray);
        }
}

如果编译器没有强制执行此操作,您可以执行此操作:

public MySubClassB extends MyClass {
        public MySubClassB(Object[] myArray) {
                someMethodOnSuper(); //ERROR super not yet constructed
                super(myArray);
        }
}

它表明实际上,子域必须在超级类之前被初始化!同时,java要求通过专门化超级构造函数参数

来“保护”我们从专门化类

如果父类具有默认构造函数,则编译器会自动为您插入对super的调用。由于Java中的每个类都继承自Object,因此必须以某种方式调用对象构造函数,并且必须首先执行它。编译器自动插入super()允许这样做。强制执行super以首先出现,强制执行构造函数体的正确顺序是:Object - &gt;家长 - &gt;孩子 - &gt; ChildOfChild - &gt; SoOnSoForth

(1)检查super是第一个语句不足以防止该问题。例如,你可以把“super(someMethodInSuper());”在你的构造函数中。这会尝试在构造超类之前访问它,即使super是第一个语句。

(2)编译器似乎实现了一个不同的检查,它本身就足以防止出现这个问题。消息是“在调用超类型构造函数之前无法引用xxx”。因此,检查super是第一个语句是没有必要的

请完成此http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html