Каков правильный порядок вызова методов суперкласса в методах onPause, onStop и onDestroy? и почему?


Я просто просматривал сайт разработчика Android, обновляя жизненный цикл активности, и в каждом примере кода есть комментарий рядом с методами суперкласса, который говорит: "Всегда сначала вызывайте метод суперкласса".

хотя это имеет смысл в полупериоде создания: onCreate, onStart и onResume, я немного запутался в том,что такое правильная процедура по разрушению полупериода : onPause,onStop, onDestroy.

уничтожение экземпляра конкретные ресурсы во-первых, прежде чем уничтожать ресурсы суперкласса, от которых могут зависеть конкретные ресурсы экземпляра, имеет смысл, а не наоборот.Но комментарии говорят об обратном. Чего мне не хватает?

Edit: поскольку люди, похоже, путаются в отношении намерения в вопросе, я хочу знать, что из следующего правильно? А ПОЧЕМУ ?

1.Гуглить предлагает

    @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 67

7 ответов:

сначала уничтожить ресурсы, относящиеся к экземпляру, перед уничтожением ресурсы суперкласса, которые могут зависеть от конкретных ресурсов экземпляра на смысл, а не наоборот. Но комментарии предполагают иначе. что я упустил?

по-моему: ни одной вещи.

этот ответ от Марка (он же CommonsWare на SO) проливает свет на этот вопрос:Link - должен ли вызов метода суперкласса быть первое утверждение?. Но тогда вы можете увидеть следующий комментарий, оставленный на его ответ:

но почему официальный doc говорит:" всегда вызывайте метод суперкласса первым " в onPause()?

назад к квадрату один. Ладно, давайте посмотрим на это с другой стороны. Мы знаем, что спецификация языка Java не укажите порядок, в котором вызов super.overridenMethod() должен быть размещен (или если вызов должен быть размещен вообще).

In случай классной деятельности,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

конечно. Посмотрите на тело метода для активности.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(), вы будете в порядке. Деятельность.onStop () имеет аналогичное тело метода. Но взгляните на Деятельность.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();
    }
    ....
}

резюме и достойные упоминания:

Пользователь Philip Sheard : представлен сценарий, в котором вызов super.onPause() должно быть отложено в случае начала работы с помощью startActivityForResult(Intent). Установка результата с помощью setResult(...)послеsuper.onPause() не будет работать. Позже он уточняет это в комментариях к своему ответу.

пользователь Sherif elKhatib : объясняет, почему позволяешь суперкласс инициализирует свои ресурсы первым и уничтожает свои ресурсы последним следует из логики:

давайте рассмотрим библиотеку, которую вы загрузили, которая имеет LocationActivity это содержит функцию getLocation (), которая предоставляет расположение. Скорее всего, эта деятельность должна будет инициализировать свой материал в onCreate () который заставит вас позвонить супер.onCreate first. Вы уже сделайте это, потому что вы чувствуете, что это имеет смысл. Теперь, в вашем onDestroy, вы решаете, что хотите сохранить местоположение где-то в SharedPreferences. Если вы позвоните супер.onDestroy во-первых, это к a в определенной степени возможно, что getLocation вернет нулевое значение после этого вызова, потому что реализация LocationActivity обнуляет значение местоположения в onDestroy. Идея в том, что вы не буду винить его, если это произойдет. поэтому, вы бы позвонили супер.onDestroy в конце после того как вы закончите с вашим собственным onDestroy.

он продолжает указывать: если дочерний класс надлежащим образом изолирован (с точки зрения зависимости от ресурсов) от родительского класса, то super.X() звонки не должны придерживаться какой-либо спецификации заказа.

посмотреть свой ответ на этой странице, чтобы прочитать сценарий, где размещение super.onDestroy() вызов тут влияет на логику программы.

из ответа Марка:

методы, которые вы переопределяете, которые являются частью создания компонента (onCreate(), onStart (), onResume () и др.), вы должны приковаться к суперклассу как первое утверждение, чтобы убедиться, что Android имеет свой шанс сделать ее работайте, прежде чем пытаться сделать что-то, что зависит от этой работы это было сделано.

методы, которые вы переопределяете, которые являются частью компонента разрушение (onPause (), onStop (), onDestroy () и др.), вы должны сделать ваша работа в первую очередь и цепочка к суперклассу как последнее. Что кстати, в случае, если Android очищает то, от чего зависит ваша работа, сначала ты сделаешь свою работу.

методы, которые возвращают что-то кроме void (onCreateOptionsMenu () и др.), иногда вы цепляетесь к суперкласс в инструкции return, предполагая, что вы не являетесь в частности, делать что-то, что должно заставить конкретный возврат значение.

все остальное -- например onActivityResult () -- это зависит от вас, в целом. Я, как правило, цепочка к суперклассу, как первое, что, но если вы не сталкиваетесь с проблемами, цепочка позже должна быть штраф.

Боб Кернс С этой теме:

это хороший шаблон [(шаблон, который предлагает Марк выше)], но я нашел некоторые исключения. Например, тема, которую я хотел применить к моей PreferenceActivity не будет принимать эффект, если я не поставлю его перед суперклассом onCreate ().

Пользователь Steve Benett также обращает на это внимание:

я знаю только одну ситуацию, где время Super вызова необходимый. если вы хотите изменить стандартное поведение тема или дисплей и такие в onCreate, вы должны сделать это перед вызовом супер, чтобы увидеть эффект. В противном случае, насколько мне известно, нет никакой разницы который пора тебе позвонить.

Пользователь Сунил Мишра подтверждает, что порядок (скорее всего) не играет роли при вызове методов класса Activity. Он также утверждает, что вызов методов суперкласса сначала считается лучшей практикой. Однако я не мог это подтвердить.

User LOG_TAG : объясняет, почему вызов суперкласса конструктор должен быть перед всем остальным. На мой взгляд, это объяснение не вопрос.

конец Примечания: доверие, но проверка. Большинство ответов на этой странице следовать этому подходу, чтобы увидеть, если заявление Always call the superclass method first имеет логической основы. Как оказалось, это не так; по крайней мере, не в случае классовой деятельности . Как правило, следует прочитать исходный код суперкласса, чтобы определить, является ли требование упорядочивания вызовов методов super.

поскольку (вы говорите) имеет смысл сначала вызвать super onCreate: подумайте об этом.

когда я хочу создать, мой супер создает свои ресурсы > я создаю свои ресурсы.

наоборот: (вроде стека)

когда я хочу уничтожить, я уничтожаю свои ресурсы > мой супер уничтожает свои ресурсы.


в этом смысле он применяется к любой паре функций (onCreate/onDestroy, onResume/onPause, onStart/onStop). Естественно, onCreate будет создавать ресурсы и onDestroy освободит эти ресурсы. Кстати, то же самое доказательство применимо и к другим парам.

давайте рассмотрим библиотеку, которую вы загрузили, которая имеет LocationActivity, которая содержит функцию getLocation (), которая предоставляет местоположение. Скорее всего, эта активность должна будет инициализировать свой материал в onCreate (), который заставит вас вызвать super.onCreate сначала. Вы уже делаете это, потому что вы чувствуете, что это делает чувство. Теперь, в вашем onDestroy, вы решите, что хотите сохранить местоположение где-то в SharedPreferences. Если вы позвоните супер.onDestroy во-первых, в определенной степени возможно, что getLocation вернет значение null после этого вызова, поскольку реализация LocationActivity обнуляет значение location в onDestroy. Идея в том, что вы не будете винить его, если это произойдет. Поэтому вы бы назвали Супер.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 расширяет BaseActivity и позже, я хочу создать SherifElKhatibActivity что расширяет вашу деятельность? В каком порядке я должен называть super.do функции? В конечном счете это одно и то же.


что касается вашего вопроса:

Я думаю, что намерение Google состоит в том, чтобы сказать нам: звоните супер независимо от того, где. В качестве общей практики, конечно, назовите это в начале. У Google, конечно, есть самые яркие инженеры и разработчики, поэтому они, вероятно, проделали хорошую работу по изоляции их супер звонит и не вмешивается в звонки ребенка.

я попробовал немного, и это, вероятно, не легко (так как это Google, мы пытаемся доказать, что это не так), чтобы создать деятельность, которая будет сбой просто из-за того, когда супер вызывается.

почему?

все, что сделано в этих функциях, действительно является частным для класса 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 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));
    }
}

если вы называете супер.onDestroy в начале вашего onDestroy, the Прощай деятельность будет запущена. Если вы позвоните супер.onDestroy в конце вашего onDestroy, прощание деятельность не будет запущена.

конечно, это не оптимальный пример. Однако это показывает, что Google облажались немного. Любая из других переменных не повлияла бы на поведение вашего приложения. Однако добавление этих отправлений в onDestroy заставило супер каким-то образом вмешаться в ваш подкласс.

Я говорю, что они испортили по другой причине, как что ж. Мало того, что они (до 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 где-то в этой функции. Поэтому они могли бы также поместить весь код (включенный в супер.onStop) до или после вызова onStop, а затем просто уведомляет подклассы о onStop, используя пустые суперфункции onStop и даже не добавляя исключение SuperNotCalledException или проверку этого вызова.

для этого, если они вызвали эту отправку в ActivityLifeCycle в performDestroy вместо вызова его в конце super.onDestroy, поведение нашей деятельности было бы таким же, независимо от того, когда мы позвонили супер.

в любом случае это первое, что они делают (немного ошибся) и это только в API 14.

самое главное, чтобы иметь в виду, что super.onPause() неявно называет setResult(Activity.RESULT_CANCELED). Но setResult можно назвать только один раз, и все последующие вызовы игнорируются. Поэтому, если вы хотите вернуть какой-либо результат обратно в родительскую активность, вам нужно позвонить setResult себя, до вы называете super.onPause(). Это самая большая проблема, насколько я знаю.

оба верны ИМО

согласно документам

производные классы должны вызывать реализацию суперкласса этого метода. Если они этого не сделают, будет выдано исключение.

Super метод всегда должен вызываться, когда документация явно говорит об этом.

однако вы можете выбрать, когда вызывать супер метод.

глядя на источник onPause

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

следовательно, независимо от того, до или после того, как он называется. Ты должен быть хорошим.

но для лучшей практики, вы должны называть его первым.

Я рекомендую его в основном как механизм защиты: если есть исключение, то super метод экземпляра уже были названы.

кроме того, размещение этих вызовов на первой строке поможет вам избежать ошибок в будущем, таких как удаление кода в метод и случайное удаление вызова суперкласса.

вы говорите, что Google предлагает метод 1, однако Дайанн Хакборн, известный инженер Android framework, предлагает иначе посмотреть Ссылка На Форум Google.

имеет смысл интуитивно назвать супер класс последние, когда уничтожение экземпляр в onPause, onStop и onDestroy методы и первый, когда создания экземпляр с методами onCreate, onResume и onStart.

супер обратных вызовов необходимо поставить деятельность в правильном состоянии внутренне для системы.

допустим, вы начинаете свою деятельность и onCreate вызывается системой. Теперь вы можете переопределить его и загрузить свой макет. Но ради системного потока вы должны вызвать super, чтобы система могла продолжить стандартную процедуру. Вот почему исключение будет выдано, если вы не вызываете его.

Это происходит независимо от вашей реализации в onCreate. Это только importend для системы. Если бы не было ANR, вы могли бы иметь бесконечный цикл в любом обратном вызове, и активность была бы поймана в этом. Таким образом, система знает, когда обратный вызов был завершен, и чем вызывает следующий.

Я знаю только одну ситуацию, где время Super звонить надо. Если вы хотите изменить стандартное поведение темы или дисплея и т. д. в onCreate, вы должны сделать это перед вызовом super, чтобы увидеть эффект. В противном случае AFAIK нет никакой разницы, в какое время вы его называете.

но чтобы позволить системе делать то, что она может лучше всего поставить супер в первой строке обратного вызова, за которым следует ваш код, если у вас нет веских оснований для разрыва с ним.

С точки зрения java вот некоторое решение для этой путаницы:

почему это() и Super() должен быть первым оператором в конструкторе?

конструктор родительского класса должен быть вызван перед конструктором подкласса. Это гарантирует, что при вызове любых методов родительского класса в конструкторе родительский класс уже настроен правильно.

что вы пытаетесь сделать, передать args супер конструктор совершенно законен, вам просто нужно построить эти args inline, как вы делаете, или передать их в свой конструктор, а затем передать их в 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);
        }
}

это показывает, что на самом деле, подполя должны быть inilialized перед суперклассом! Тем временем, требование java "защищает" нас от специализации класса, специализируя то, что аргумент super constructor

в тех случаях, когда родительский класс имеет конструктор по умолчанию, вызов super автоматически вставляется компилятором. Поскольку каждый класс в Java наследуется от объекта, конструктор объектов должен быть вызван каким-то образом, и он должен быть выполнен первым. Автоматическая вставка 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