Подтвердить что ты не робот

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

Я просто просматривал сайт разработчика Android, обновляясь в цикле активности 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();  
    }
4b9b3361

Ответ 1

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

По-моему: ни одна вещь.

Этот ответ от Mark (aka 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()");
}

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();
    }
    ....
}

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

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

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

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

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

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

От ответа Mark:

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

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

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

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

Боб Кернс из этот поток:

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

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

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

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

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

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

Ответ 2

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

Когда я хочу создать, My super создает свои ресурсы > Я создаю свои ресурсы.

Вперед: (тип стека)

Когда я хочу уничтожить, я уничтожаю свои ресурсы > My super уничтожает его ресурсы.


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

Рассмотрим библиотеку, которую вы загрузили, которая имеет LocationActivity, которая содержит функцию getLocation(), которая предоставляет местоположение. Скорее всего, это действие должно будет инициализировать его материал в onCreate(), который заставит вас сначала вызвать super.onCreate. Вы уже это делаете, потому что чувствуете, что это имеет смысл. Теперь, в вашем onDestroy, вы решили, что хотите сохранить местоположение где-то в 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 расширяет 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, будет иметь другой результат, основанный на том, когда вы вызываете супер. Например:

Создайте класс приложения, который подсчитывает количество текущих выполняемых действий:

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 заставило супер каким-то образом вмешаться в ваш подкласс.

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

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

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

Ответ 3

ОБА являются правильными IMO

В соответствии с документами

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

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

Однако вы можете выбрать, когда вызывать метод super.

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

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

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

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

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

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

Ответ 4

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

Ответ 5

Вы говорите, что Google предлагает метод 1, однако Dianne Hackborn, известный инженер по инфраструктуре Android, предлагает в противном случае обратиться к ссылку в Google Форум.

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

Ответ 6

Супер обратных вызовов необходимо, чтобы объект Activity был в правильном состоянии для системы.

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

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

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

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

Ответ 7

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

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

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

То, что вы пытаетесь сделать, передать аргументы супер конструктору, совершенно легально, вам просто нужно построить эти аргументы inline, как вы это делаете, или передать их вашему конструктору, а затем передать их супер:

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-требование "защищает" нас от специализации класса, специализируясь на том, что аргумент супер-конструктора

В случаях, когда родительский класс имеет конструктор по умолчанию, вызов супер автоматически устанавливается компилятором. Поскольку каждый класс в Java наследуется от Object, конструктор объектов должен быть вызван как-то, и он должен быть выполнен первым. Автоматическая вставка супер() компилятором позволяет это. Принудительное включение супер, чтобы гарантировать, что тела конструктора выполняются в правильном порядке: Object → Parent → Child → ChildOfChild → SoOnSoForth

(1) Проверка того, что super - это первое утверждение, недостаточно для предотвращения этой проблемы. Например, вы можете поставить "super (someMethodInSuper())"; в вашем конструкторе. Это пытается получить доступ к методу в суперклассе перед его построением, хотя супер является первым утверждением.

(2) Компилятор, похоже, реализует другую проверку, которая сама по себе достаточна для предотвращения этой проблемы. Сообщение "не может ссылаться на ххх до того, как был создан конструктор супертипа". Поэтому проверка того, что super является первым утверждением, не требуется

Пожалуйста, пройдите через http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html