programing

onPause, onStop 및 onDestroy 메서드에서 슈퍼 클래스 메서드를 호출하는 올바른 순서는 무엇입니까?

nasanasas 2020. 9. 22. 08:18
반응형

onPause, onStop 및 onDestroy 메서드에서 슈퍼 클래스 메서드를 호출하는 올바른 순서는 무엇입니까? 그리고 왜?


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

인스턴스 특정 리소스가 의존 할 수있는 수퍼 클래스 리소스를 파괴하기 전에 먼저 인스턴스 특정 리소스를 파괴하는 것이 합리적입니다. 그러나 의견은 그렇지 않다는 것을 암시합니다. 내가 무엇을 놓치고 있습니까?

제 생각에는 단 하나도 아닙니다.

Mark (일명 CommonsWare on SO)의이 답변은 문제에 대해 밝힙니다. Link- 수퍼 클래스 메서드에 대한 호출이 첫 번째 문이어야합니까? . 그러나 그의 답변에 다음과 같은 댓글이 남았습니다.

그러나 왜 공식 문서에서 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();
    }
    ....    
}

그리고 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(...) after super.onPause()사용한 결과 설정은 작동하지 않습니다. 그는 나중에 그의 답변에 대한 의견에서 이에 대해 명확히 설명합니다.

사용자 Sherif elKhatib : 수퍼 클래스가 먼저 리소스를 초기화하고 마지막에 논리에서 리소스를 파괴하도록하는 이유를 설명합니다.

위치를 제공하는 getLocation () 함수를 포함하는 LocationActivity가있는 다운로드 한 라이브러리를 고려해 보겠습니다. 대부분의 경우이 액티비티는 onCreate ()에서 항목을 초기화해야하므로 super.onCreate를 먼저 호출해야합니다 . 당신은 그것이 의미가 있다고 느끼기 때문에 이미 그렇게하고 있습니다. 이제 onDestroy에서 SharedPreferences 어딘가에 위치를 저장하기로 결정했습니다. super.onDestroy를 먼저 호출하면 LocationActivity의 구현이 onDestroy의 위치 값을 무효화하기 때문에 getLocation이이 호출 후에 null 값을 반환 할 가능성이 어느 정도 있습니다. 아이디어는 이런 일이 발생하더라도 당신이 그것을 비난하지 않을 것이라는 것입니다. 따라서 자신의 onDestroy를 완료 한 후 마지막에 super.onDestroy를 호출합니다.

그는 계속해서 지적합니다. 만약 자식 클래스가 부모 클래스로부터 적절하게 분리되어 있다면 (자원 의존성 측면에서), super.X()호출은 어떤 주문 사양을 따를 필요가 없습니다.

배치 어디 시나리오를 읽고이 페이지에 자신의 답변을 참조 super.onDestroy()호출 않는 프로그램 로직에 영향을 미칩니다.

Mark의 답변에서 :

구성 요소 생성의 일부인 재정의하는 메서드 (onCreate (), onStart (), onResume () 등), 첫 번째 문으로 수퍼 클래스에 연결하여 Android가 먼저 작업을 수행 할 수 있도록해야합니다. 수행 된 작업에 의존하는 작업을 시도합니다.

컴포넌트 파괴 (onPause (), onStop (), onDestroy () 등)의 일부인 메서드를 재정의하면 먼저 작업을 수행하고 마지막으로 수퍼 클래스에 연결해야합니다 . 이렇게하면 Android가 작업에 의존하는 항목을 정리하는 경우 먼저 작업을 수행 한 것입니다.

void (onCreateOptionsMenu () 등) 이외의 것을 반환하는 메서드는 특정 반환 값을 강제하는 데 필요한 작업을 특별히 수행하지 않는다고 가정하여 때때로 return 문에서 수퍼 클래스에 연결합니다.

onActivityResult ()와 같은 다른 모든 것은 전체적으로 귀하에게 달려 있습니다. 우선 수퍼 클래스에 연결하는 경향이 있지만 문제가 발생하지 않는 한 나중에 연결하는 것이 좋습니다.

밥 컨스 에서 이 스레드 :

좋은 패턴 [(Mark가 위에서 제안한 패턴)]이지만 몇 가지 예외를 발견했습니다. 예를 들어, PreferenceActivity에 적용하려는 테마는 수퍼 클래스의 onCreate () 앞에 놓지 않으면 효과가 없습니다.

사용자 Steve Benett 도 이에 대해주의를 기울입니다.

나는 슈퍼 콜의 타이밍이 필요한 한 가지 상황만을 알고 있습니다. onCreate에서 테마 또는 디스플레이 등의 표준 동작을 변경하려면 super를 호출하여 효과를 확인하기 전에 변경해야합니다 . 그렇지 않으면 AFAIK를 호출하는 시간에 차이가 없습니다.

사용자 Sunil Mishra 는 Activity 클래스의 메소드를 호출 할 때 순서 (거의 가능성이 높음)가 역할을하지 않음을 확인합니다. 그는 또한 슈퍼 클래스 메서드를 먼저 호출하는 것이 모범 사례라고 주장합니다 . 그러나 나는 이것을 확증 할 수 없었다.

사용자 LOG_TAG : 슈퍼 클래스 생성자에 대한 호출이 다른 모든 것보다 우선해야하는 이유를 설명합니다 . 제 생각에이 설명은 질문에 추가되지 않습니다.

끝 메모 : 신뢰 하지만 확인하십시오. 이 페이지에있는 대부분의 답변은이 접근 방식을 따라 진술 Always call the superclass method first에 논리적 근거가 있는지 확인합니다 . 밝혀진대로 그렇지 않습니다. 적어도 Activity 클래스의 경우는 아닙니다. 일반적으로 super의 메서드에 대한 호출 순서가 요구 사항인지 확인하려면 수퍼 클래스의 소스 코드를 읽어야합니다.


(당신이 말했듯이) super onCreate를 먼저 호출하는 것이 합리적이므로 생각해보십시오.

내가 만들고 싶을 때 내 수퍼가 리소스를 생성하고> 내 리소스를 생성합니다.

반대로 : (일종의 스택)

내가 파괴하고 싶을 때, 나는 내 자원을 파괴합니다.> 나의 수퍼가 그의 자원을 파괴합니다.


이러한 의미에서 두 가지 함수 (onCreate / onDestroy, onResume / onPause, onStart / onStop)에 적용됩니다. 당연히 onCreate는 리소스를 생성하고 onDestroy는 이러한 리소스를 해제합니다. 그건 그렇고, 다른 커플에게도 같은 증거가 적용됩니다.

위치를 제공하는 getLocation () 함수를 포함하는 LocationActivity가있는 다운로드 한 라이브러리를 고려해 보겠습니다. 대부분의 경우이 액티비티는 onCreate ()에서 항목을 초기화해야하므로 super.onCreate를 먼저 호출해야합니다. 당신은 그것이 의미가 있다고 느끼기 때문에 이미 그렇게하고 있습니다. 이제 onDestroy에서 SharedPreferences 어딘가에 위치를 저장하기로 결정했습니다. super.onDestroy를 먼저 호출하면 LocationActivity의 구현이 onDestroy의 위치 값을 무효화하기 때문에 getLocation이이 호출 후에 null 값을 반환 할 가능성이 어느 정도 있습니다. 아이디어는 이런 일이 발생하더라도 당신이 그것을 비난하지 않을 것이라는 것입니다. 따라서 자신의 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);
    }
}

마지막으로,라는 활동이 AnudeepBullaActivityBaseActivity를 확장하고 나중에 SherifElKhatibActivity활동을 확장하는 것을 만들고 싶습니다 . super.do함수를 어떤 순서로 호출해야 합니까? 궁극적으로 같은 것입니다.


질문 :

Google의 의도는 다음과 같이 말하는 것입니다. 어디에 있든 상관없이 수퍼에 전화하십시오. 물론 일반적인 관행으로 처음에는 호출하십시오. 물론 Google에는 가장 뛰어난 엔지니어와 개발자가 있으므로 그들은 아마도 슈퍼 통화를 분리하고 어린이 통화를 방해하지 않는 좋은 작업을 수행했을 것입니다.

나는 조금 시도했지만 When is super가 호출되기 때문에 간단하게 충돌하는 활동을 만드는 것이 (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에서는 애플리케이션에 등록 된 ActivityLifecycleCallbacks에 onActivityDestroyed를 디스패치하기 위해 dispatchActivityDestroyed가 추가되었습니다. 따라서 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) {}
}

다음은 말이되지 않거나 좋은 관행이 아닐 수도 있지만 단지 요점을 증명하기위한 것입니다 (더 실제 상황을 찾을 수 있음). GoodBye 활동이 완료되고 마지막 활동 일 때 이동하는 MainActivity를 만듭니다.

@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 활동이 시작되지 않습니다.

물론 이것은 최적의 예가 아닙니다. 그러나 이것은 Google이 여기에서 약간 엉망임을 보여줍니다. 다른 변수는 앱의 동작에 영향을주지 않았습니다. 그러나 이러한 디스패치를 ​​onDestroy에 추가하면 수퍼가 어떻게 든 하위 클래스를 방해합니다.

나는 그들이 다른 이유로도 엉망이라고 말합니다. 그들은 (api 14 이전에) 최종 및 / 또는 비공개 인 슈퍼 호출 만 터치했을뿐만 아니라 실제로 onPause ... 함수를 전달하는 다른 내부 함수 (비공개)를 호출했습니다.

예를 들어 performStopfunction은 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;
}

Notice that they call the Activity's onStop somewhere in this function. Therefore, they might have as well put all the code (included in super.onStop) before or after the call to onStop and then just notify subclasses about the onStop using empty onStop super functions and without even adding the SuperNotCalledException or checking for this called.

For this, if they called this dispatch to the ActivityLifeCycle in the performDestroy instead of calling it in the end of super.onDestroy, our activity's behavior would have been the same regardless of when we did call the super.

Anyway this is the first thing they do (a bit wrong) and it is only in API 14.


From java perspective here is some solution for this confusion:

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

The parent class' constructor needs to be called before the subclass' constructor. This will ensure that if you call any methods on the parent class in your constructor, the parent class has already been set up correctly.

What you are trying to do, pass args to the super constructor is perfectly legal, you just need to construct those args inline as you are doing, or pass them in to your constructor and then pass them to super:

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

If the compiler did not enforce this you could do this:

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

It shows that actually, subfields have to be inilialized before the supreclass! Meantime, java requirement "defends" us from specializing the class by specializing what the super constructor argument

In cases where a parent class has a default constructor the call to super is inserted for you automatically by the compiler. Since every class in Java inherits from Object, objects constructor must be called somehow and it must be executed first. The automatic insertion of super() by the compiler allows this. Enforcing super to appear first, enforces that constructor bodies are executed in the correct order which would be: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

(1) Checking that super is the first statement is not sufficient to prevent that problem. For example, you could put "super(someMethodInSuper());" in your constructor. This attempts to access a method in the superclass before it is constructed, even though super is the first statement.

(2) The compiler appears to implement a different check which is, by itself, sufficient to prevent this problem. The message is "cannot reference xxx before supertype constructor has been called". Therefore, checking that super is the first statement is not necessary

Please go through this http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html


The most important thing to bear in mind is that super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED). But setResult can only be called once, and all subsequent calls are ignored. So if you want to push any kind of result back to the parent activity, you have to call setResult yourself, before you call super.onPause(). That is the biggest gotcha, as far as I know.


BOTH are correct IMO

According to the docs

Derived classes must call through to the super class's implementation of this method. If they do not, an exception will be thrown.

Super method should always be called when documentation explicitly says so.

You can however choose when to call the super method.

Looking at the source of onPause

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

Hence no matter before or after it is called. You should be good.

But for best practice, you should be calling it first.

I recommend it mostly as a protection mechanism: if there is an exception then the super instance method will already have been called.

Also putting these calls on the first line will help you avoid making mistakes in the future such as deleting code in the method and accidentally deleting the call to the super class.


You say that Google suggests method 1, however Dianne Hackborn, a well known Android framework engineer suggest otherwise see Google Forum Link.

It makes intuitive sense to call the super class last when destroying an instance in the onPause, onStop and onDestroy methods and first when creating an instance with the methods of onCreate, onResume and onStart.


The super of the callbacks is needed to put the Activity in the right state internally for the system.

Let's say you start your Activity and onCreate is invoked by the system. Now you can override it and e.g. load your layout. But in sake of the system flow you have to call super, that the system can continue with the standard procedure. That's why an exception will be thrown if you don't call it.

This happens independent of your implementation in onCreate. It's only importend for the system. If there would be no ANR you could have an endless loop in any callback and the Activity would be caught in that one. So, the system knows when the callback has been terminated and than calls the next one.

I only know one situation, where the timing of the super call is necessary. If you wanna alter the standard behavior of the theme or the display and such in onCreate, you have to do it before you call super to see an effect. Otherwise AFAIK there is no difference at which time you call it.

But to let the system do what it can best put the super in the first line of a callback followed by your code, if you don't have a good reason to break with it.

참고URL : https://stackoverflow.com/questions/18821481/what-is-the-correct-order-of-calling-superclass-methods-in-onpause-onstop-and-o

반응형