Я просто просматривал сайт разработчика Android, обновлял жизненный цикл деятельности, и в каждом примере кода рядом с методами суперкласса есть комментарий, в котором говорится: «Всегда сначала вызывайте метод суперкласса».
Хотя это имеет смысл в полупериоде создания: 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();
}
источник
Ответы:
На мой взгляд: ни одной вещи.
Этот ответ Марка (он же CommonsWare на SO) проливает свет на проблему: Ссылка - должен ли вызов метода суперкласса быть первым оператором? . Но затем вы можете увидеть следующий комментарий, оставленный к его ответу:
В начало. Хорошо, давайте посмотрим на это под другим углом. Мы знаем, что спецификация языка Java не определяет порядок, в котором
super.overridenMethod()
должен быть размещен вызов (или если вызов должен быть размещен вообще).В случае класса Activity
super.overridenMethod()
вызовы обязательны и выполняются :if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortString() + " did not call through to super.onStop()"); }
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; }
Каким бы способом вы ни поместили звонок
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(); } .... }
И из примера приложения LunarLander, включенного в android sdk:
public class LunarLander extends Activity { .... @Override protected void onPause() { mLunarView.getThread().pause(); // pause game when Activity pauses super.onPause(); } .... }
Резюме и достойные упоминания:
Пользователь Филип Шеард : Предоставляет сценарий, при котором вызов
super.onPause()
должен быть отложен в случае начала использования ActivitystartActivityForResult(Intent)
. Установить результат с помощьюsetResult(...)
aftersuper.onPause()
не получится. Позже он поясняет это в комментариях к своему ответу.Пользователь Шериф elKhatib : Объясняет, почему разрешение суперклассу инициализировать его ресурсы первым и уничтожать его ресурсы последним следует из логики:
Далее он указывает: если дочерний класс надлежащим образом изолирован (с точки зрения зависимости от ресурсов) от родительского класса,
super.X()
вызовы не обязательно должны соответствовать какой-либо спецификации порядка.См свой ответ на эту страницу , чтобы прочитать сценарий , при котором размещение
super.onDestroy()
вызова действительно влияет на логику программы.Из ответа Марка :
Боб Кернс из этой темы :
Пользователь Стив Бенетт также обращает на это внимание:
Пользователь Сунил Мишра подтверждает, что порядок (скорее всего) не играет роли при вызове методов класса Activity. Он также утверждает, что сначала вызов методов суперкласса считается лучшей практикой . Однако я не мог подтвердить это.
Пользователь LOG_TAG : объясняет, почему вызов конструктора суперкласса должен быть перед всем остальным. На мой взгляд, это объяснение не добавляет к задаваемому вопросу.
Конец примечания : Доверяй, но проверяй. В большинстве ответов на этой странице используется этот подход, чтобы проверить, имеет ли утверждение
Always call the superclass method first
логическое обоснование. Как оказалось, это не так; по крайней мере, не в случае класса Activity. Как правило, следует прочитать исходный код суперкласса, чтобы определить, требуется ли упорядочивание вызовов методов super.источник
onDestroy
иonStop
последний - более безопасный вариант по умолчанию, и чтоonPause
в некоторых случаях может быть сложнее? Не могли бы вы добавить это первым? Мне хотелось отредактировать ответ самостоятельно, но я не уверен, что это резюме правильное.Поскольку (вы говорите), имеет смысл сначала вызвать super onCreate: подумайте об этом.
Когда я хочу творить, Мой супер создает свои ресурсы> Я создаю свои ресурсы.
Наоборот: (своего рода стек)
Когда я хочу разрушить, я уничтожаю свои ресурсы> Мой супер уничтожает свои ресурсы.
В этом смысле это применимо к любой паре функций (onCreate / onDestroy, onResume / onPause, onStart / onStop). Естественно, onCreate создаст ресурсы, а onDestroy освободит эти ресурсы. Кстати, то же доказательство применимо и к другим парам.
Давайте рассмотрим загруженную вами библиотеку, которая имеет LocationActivity, который содержит функцию getLocation (), которая определяет местоположение. Скорее всего, для этого действия нужно будет инициализировать его содержимое в onCreate (), что заставит вас сначала вызвать super.onCreate. Вы уже делаете это, потому что чувствуете, что это имеет смысл. Теперь в своем onDestroy вы решаете, что хотите сохранить Location где-нибудь в SharedPreferences. Если вы сначала вызываете super.onDestroy, до некоторой степени возможно, что getLocation вернет нулевое значение после этого вызова, потому что реализация LocationActivity обнуляет значение местоположения в onDestroy. Идея в том, что вы не будете винить это, если это произойдет. Следовательно, вы должны вызвать super.onDestroy в конце после того, как вы закончите свой собственный 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
extends BaseActivity, а позже, я хочу создатьSherifElKhatibActivity
, расширяет вашу активность? В каком порядке мне вызыватьsuper.do
функции? В конечном итоге это одно и то же.Что касается вашего вопроса:
Я думаю, что намерение Google состоит в том, чтобы сказать нам: пожалуйста, звоните суперу, где бы то ни было. В качестве общей практики, конечно, называйте это вначале. У Google, конечно же, самые блестящие инженеры и разработчики, поэтому они, вероятно, хорошо поработали, изолировав свои супервызовы и не вмешиваясь в дочерние вызовы.
Я немного попробовал, и, вероятно, непросто (поскольку мы пытаемся доказать, что ошибается Google) создать действие, которое просто вылетало из-за вызова When, super.
Зачем?
Все, что делается в этих функциях, действительно является частным для класса 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 в 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, когда оно завершено и когда это последнее действие:
@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)); } }
Если вы вызовете super.onDestroy в начале своего onDestroy, действие GoodBye будет запущено. Если вы вызовете super.onDestroy в конце вашего onDestroy, действие GoodBye не будет запущено.
Конечно, это опять же не лучший пример. Однако это показывает, что Google здесь немного напортачил. Любая из других переменных не повлияла бы на поведение вашего приложения. Однако добавление этой отправки в 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; }
Обратите внимание, что они вызывают onStop действия где-то в этой функции. Следовательно, они могли бы также поместить весь код (включенный в super.onStop) до или после вызова onStop, а затем просто уведомить подклассы об onStop, используя пустые суперфункции onStop и даже не добавляя SuperNotCalledException или проверяя это вызванное.
Для этого, если бы они вызвали эту отправку ActivityLifeCycle в performDestroy вместо того, чтобы вызывать ее в конце super.onDestroy, поведение нашей активности было бы таким же, независимо от того, когда мы действительно вызывали super.
Во всяком случае, это первое, что они делают (немного не так), и это только в API 14.
источник
super.on
вероятно, никогда не помешает вашей деятельности.Вы говорите, что Google предлагает метод 1, однако Дайан Хакборн, известный разработчик фреймворка Android, предлагает иначе см. Ссылку на форум Google .
Интуитивно понятен вызов суперкласса последним при уничтожении экземпляра в методах onPause, onStop и onDestroy и первым при создании экземпляра с помощью методов onCreate, onResume и onStart .
источник
С точки зрения Java, вот решение этой путаницы:
Почему this () и super () должны быть первым оператором в конструкторе?
Конструктор родительского класса необходимо вызвать перед конструктором подкласса. Это гарантирует, что если вы вызовете какие-либо методы родительского класса в своем конструкторе, родительский класс уже будет правильно настроен.
То, что вы пытаетесь сделать, передать аргументы в суперконструктор совершенно законно, вам просто нужно построить эти аргументы встроенными, как вы это делаете, или передать их своему конструктору, а затем передать их супер:
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 -> Parent -> Child -> ChildOfChild -> SoOnSoForth
(1) Проверка того, что super является первым утверждением, недостаточна для предотвращения этой проблемы. Например, вы можете указать «super (someMethodInSuper ());» в вашем конструкторе. Это пытается получить доступ к методу в суперклассе до его создания, даже если super является первым оператором.
(2) Компилятор, похоже, реализует другую проверку, которая сама по себе достаточна для предотвращения этой проблемы. Сообщение: «Невозможно ссылаться на xxx до вызова конструктора супертипа». Следовательно, проверка того, что super является первым оператором, не требуется.
Просмотрите этот http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html
источник
Самое важное, что нужно иметь в виду, - это
super.onPause()
неявные вызовыsetResult(Activity.RESULT_CANCELED)
. НоsetResult
может быть вызван только один раз, и все последующие вызовы игнорируются. Поэтому, если вы хотите передать какой-либо результат обратно в родительскую операцию, вы должны позвонитьsetResult
себе, прежде чем позвонитьsuper.onPause()
. Насколько я знаю, это самая большая ошибка.источник
super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED)
. Не могли бы вы сказать, откуда вы это взяли?ОБЕИ верны ИМО
Согласно документам
Super
всегда следует вызывать, если об этом прямо сказано в документации.Однако вы можете выбрать, когда вызывать супер-метод.
Глядя на источник
onPause
protected void onPause() { getApplication().dispatchActivityPaused(this); mCalled = true; }
Следовательно, не имеет значения, до или после того, как он будет вызван. Тебе должно быть хорошо.
Но для лучшей практики вы должны сначала вызвать его.
Я рекомендую это в основном как механизм защиты: если есть исключение,
super
метод экземпляра уже будет вызван.Также размещение этих вызовов в первой строке поможет вам избежать ошибок в будущем, таких как удаление кода в методе и случайное удаление вызова суперкласса.
источник
Base Class
но если вы переопределите, необходимо, чтобы вы вызывалиsuper
else, который у вас будетandroid.app.SuperNotCalledException
Супер обратные вызовы необходимы для того, чтобы привести Activity в правильное состояние внутри системы.
Допустим, вы запускаете Activity, и система вызывает onCreate. Теперь вы можете переопределить его и, например, загрузить свой макет. Но ради системного потока вы должны вызвать super, чтобы система могла продолжить стандартную процедуру. Вот почему будет выброшено исключение, если вы его не вызовете.
Это происходит независимо от вашей реализации в onCreate. Это важно только для системы. Если бы не было ANR, у вас мог бы быть бесконечный цикл в любом обратном вызове, и Activity было бы поймано в этом. Таким образом, система знает, когда обратный вызов был завершен, и вызывает следующий.
Я знаю только одну ситуацию, когда нужен тайминг супервызова. Если вы хотите изменить стандартное поведение темы или дисплея и тому подобное в onCreate, вам нужно сделать это до того, как вы вызовете super, чтобы увидеть эффект. В противном случае, AFAIK, нет разницы, в какое время вы его вызываете.
Но чтобы позволить системе делать то, что она может, лучше всего поместить super в первую строку обратного вызова, за которым следует ваш код, если у вас нет веской причины прервать его.
источник