我刚刚浏览了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();
}
答案 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()");
}
mCalled
在Activity.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的答案:
来自this thread的覆盖的方法是组件创建的一部分(onCreate(), onStart(),onResume()等),你应该链接到超类 第一个声明,以确保Android有机会做到这一点 在你尝试做一些依赖于这项工作的事情之前工作 已经完成了。
您覆盖的方法是组件的一部分 破坏(onPause(),onStop(),onDestroy()等),你应该这样做 你的工作是第一次,并链接到超类作为最后一件事。那 如果Android清理了你的工作所依赖的东西, 你会先完成你的工作。
返回某些内容的方法 除了void(onCreateOptionsMenu()等),有时你链接到 返回语句中的超类,假设你不是 专门做一些需要强迫特定回报的事情 值。
其他所有内容 - 例如onActivityResult() - 取决于您, 总体上。我倾向于链接到超类作为第一件事, 但除非你遇到问题,否则以后应该链接 细
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