대화 상자를 표시하면 "onSaveInstanceState 후에이 작업을 수행 할 수 없습니다"라는 메시지가 나타납니다.


121

일부 사용자는 알림 표시 줄의 빠른 작업을 사용하면 강제 종료됩니다.

"TestDialog" 클래스 를 호출하는 알림에 빠른 작업을 표시합니다 . "snooze"버튼을 누른 후 TestDialog 클래스에서 SnoozeDialog를 표시합니다.

private View.OnClickListener btnSnoozeOnClick() {
    return new View.OnClickListener() {

        public void onClick(View v) {
            showSnoozeDialog();
        }
    };
}

private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    snoozeDialog.show(fm, "snooze_dialog");
}

오류는 *IllegalStateException: Can not perform this action after onSaveInstanceState*.

IllegarStateException이 발생하는 코드 줄은 다음과 같습니다.

snoozeDialog.show(fm, "snooze_dialog");

클래스는 "FragmentActivity"를 확장하고 "SnoozeDialog"클래스는 "DialogFragment"를 확장합니다.

다음은 오류의 전체 스택 추적입니다.

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.support.v4.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1327)
at android.support.v4.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1338)
at android.support.v4.app.BackStackRecord.commitInternal(BackStackRecord.java:595)
at android.support.v4.app.BackStackRecord.commit(BackStackRecord.java:574)
at android.support.v4.app.DialogFragment.show(DialogFragment.java:127)
at com.test.testing.TestDialog.f(TestDialog.java:538)
at com.test.testing.TestDialog.e(TestDialog.java:524)
at com.test.testing.TestDialog.d(TestDialog.java:519)
at com.test.testing.g.onClick(TestDialog.java:648)
at android.view.View.performClick(View.java:3620)
at android.view.View$PerformClick.run(View.java:14292)
at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4507)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:790)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)

이 오류를 재현 할 수 없지만 많은 오류 보고서를 받고 있습니다.

아무도이 오류를 어떻게 해결할 수 있습니까?


2
해결책을 찾았습니까? 당신과 같은 문제가 있습니다. 여기에 질문했습니다 : stackoverflow.com/questions/15730878/… 제 질문을 확인하고 제 경우에 작동하지 않는 가능한 해결책을 확인하십시오. 아마 당신을 위해 일할 것입니다.
rootpanthera

해결책은 아직 :-( 그리고 당신의 제안은 이미 내 클래스에 추가되지 않습니다.
chrisonline

여기에서 수락 된 답변을 확인하십시오. : 이것은 내 문제 해결 stackoverflow.com/questions/14177781/...을
보그

4
이 대화 상자가 트리거 될 때 활동이 표시됩니까? 앱이 일시 중지 / 중지 된 활동에 연결된 대화 상자를 표시하려고했기 때문일 수 있습니다.
카이

stackoverflow.com/questions/7575921/… 당신은 이것을 시도했습니다 확실합니다.
Orion

답변:


66

이것은 일반적인 문제 입니다. show ()를 재정의하고 DialogFragment 확장 클래스에서 예외를 처리하여이 문제를 해결했습니다.

public class CustomDialogFragment extends DialogFragment {

    @Override
    public void show(FragmentManager manager, String tag) {
        try {
            FragmentTransaction ft = manager.beginTransaction();
            ft.add(this, tag);
            ft.commit();
        } catch (IllegalStateException e) {
            Log.d("ABSDIALOGFRAG", "Exception", e);
        }
    }
}

이 메서드를 적용해도 DialogFragment.class의 내부 필드는 변경되지 않습니다.

boolean mDismissed;
boolean mShownByMe;

이로 인해 경우에 따라 예기치 않은 결과가 발생할 수 있습니다. commit () 대신 commitAllowingStateLoss ()를 더 잘 사용하십시오.


3
그런데 왜이 문제가 발생합니까? 오류를 무시해도됩니까? 그렇게하면 어떻게 되나요? 어쨌든 클릭하면 활동이 잘 진행되고 있음을 의미합니다 ... 어쨌든 버그로 간주하기 때문에 여기에보고했습니다 : code.google.com/p/android/issues/detail?id= 207269
안드로이드 개발자

1
그럼 거기에 별표 나 댓글을 달아 주 시겠어요?
안드로이드 개발자

2
try-catch 절 내에서 super.show (manager, tag)를 호출하는 것이 좋습니다. DialogFragment 소유 플래그는이 방법으로 안전하게 지낼 수
Shayan_Aryan

20
이 시점에서 commit () 대신 commitAllowingStateLoss ()를 호출 할 수 있습니다. 예외가 발생하지 않습니다.
ARLabs

1
@ARLabs 여기에 표시된 것과 같이 예외를 포착하면 대화 상자가 가장 확실하게 표시되지 않기 때문에이 경우 응답자에게도 더 좋을 것이라고 생각합니다. 가능한 경우 대화 상자를 표시하는 것이 더 좋으며 상태를 복원해야하는 경우 사라질 수 있습니다. 또한 백그라운드에서 앱 메모리 사용량을 낮게 유지하여 손상되지 않도록하십시오.
androidguy

27

당신을 의미 그 commit()( show()후 조각 DialogFragment의 경우) onSaveInstanceState().

Android는 조각 상태를 onSaveInstanceState(). 따라서 commit()조각 후 onSaveInstanceState()조각화하면 상태가 손실됩니다.

결과적으로 활동이 종료되고 나중에 다시 생성되면 조각이 나쁜 사용자 경험 인 활동에 추가되지 않습니다. 그렇기 때문에 Android는 어떤 대가를 치르더라도 상태 손실을 허용하지 않습니다.

쉬운 해결책은 상태가 이미 저장되었는지 확인하는 것입니다.

boolean mIsStateAlreadySaved = false;
boolean mPendingShowDialog = false;

@Override
public void onResumeFragments(){
    super.onResumeFragments();
    mIsStateAlreadySaved = false;
    if(mPendingShowDialog){
        mPendingShowDialog = false;
        showSnoozeDialog();
    }
}

@Override
public void onPause() {
    super.onPause();
    mIsStateAlreadySaved = true;
}

private void showSnoozeDialog() {
    if(mIsStateAlreadySaved){
        mPendingShowDialog = true;
    }else{
        FragmentManager fm = getSupportFragmentManager();
        SnoozeDialog snoozeDialog = new SnoozeDialog();
        snoozeDialog.show(fm, "snooze_dialog");
    }
}

참고 : 조각이 재개되면 onResumeFragments ()가 호출됩니다.


1
다른 조각 내에 DialogFragment를 표시하려면 어떻게해야합니까?
안드로이드 개발자

우리의 솔루션은 액티비티와 프래그먼트베이스 클래스를 생성하고 onResumeFragments를 프래그먼트에 위임하는 것입니다 (프래그먼트베이스 클래스에서 onResumeFragments를 생성합니다). 좋은 해결책은 아니지만 작동합니다. 더 나은 해결책이 있으면 알려주세요 :)
Pongpat

글쎄, 나는 "onStart"에서 대화를 보여주는 것이 잘 작동 할 것이라고 생각했다. 조각이 확실히 보여지고 있기 때문이다. 그러나 나는 그것에 대한 몇몇 충돌 보고서를 볼 수있다. 대신 "onResume"에 넣으라는 지시를 받았습니다. 대안에 관해서는 twigstechtips.blogspot.co.il/2014/01/…을 보았습니다. 하지만 꽤 이상합니다.
안드로이드 개발자

twigstechtips.blogspot.co.il/2014/01/… 작동 하는 이유는 새 스레드를 시작하기 때문에 작동하므로 코드 runOnUiThread가 실행되기 전에 호출 된 onStart, onResume 등의 모든 라이프 사이클 코드가 작동합니다. 이는 runOnUiThread가 호출되기 전에 상태가 이미 복원되었음을 의미합니다.
Pongpat

2
단일 호출을 사용하여 게시합니다 (실행 가능). getFragmentManager에 관해서는 다릅니다. 해당 대화 상자를 다른 활동과 공유하려면 getFragmentManager를 사용해야하지만 해당 대화 상자가 조각 getChildFragmentManager에만 존재하는 경우 더 나은 선택 인 것 같습니다.
Pongpat

16
private void showSnoozeDialog() {
    FragmentManager fm = getSupportFragmentManager();
    SnoozeDialog snoozeDialog = new SnoozeDialog();
    // snoozeDialog.show(fm, "snooze_dialog");
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    ft.add(snoozeDialog, "snooze_dialog");
    ft.commitAllowingStateLoss();
}

참조 : 링크


11

며칠 후 내 솔루션을 어떻게 수정했는지 공유하고 DialogFragment를 표시하려면 show()메서드 를 재정의 commitAllowingStateLoss()하고 Transaction개체를 호출 해야 합니다. 다음은 Kotlin의 예입니다.

override fun show(manager: FragmentManager?, tag: String?) {
        try {
            val ft = manager?.beginTransaction()
            ft?.add(this, tag)
            ft?.commitAllowingStateLoss()
        } catch (ignored: IllegalStateException) {

        }

    }

1
개발자가 상속 할 필요가 없도록 DialogFragment다음 서명이있는 Kotlin 확장 함수로 변경할 수 있습니다 fun DialogFragment.showAllowingStateLoss(fragmentManager: FragmentManager, tag: String).. 또한 commitAllowingStateLoss()메서드가 아닌 메서드를 호출하므로 try-catch가 필요 하지 않습니다 commit().
Adil Hussain

10

대화 상자가 실제로 중요하지 않은 경우 (앱이 닫히거나 더 이상 표시되지 않을 때 표시하지 않아도 괜찮음) 다음을 사용하십시오.

boolean running = false;

@Override
public void onStart() {
    running = true;
    super.onStart();
}

@Override
public void onStop() {
    running = false;
    super.onStop();
}

그리고 실행 중일 때만 대화 상자 (조각)를 엽니 다.

if (running) {
    yourDialog.show(...);
}

아마도 더 나은 솔루션 편집 :

수명주기에서 onSaveInstanceState가 호출되는 경우 예측할 수없는 경우 다음과 같이 isSavedInstanceStateDone ()을 확인하는 것이 더 나은 해결책이라고 생각합니다.

/**
 * True if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
private boolean savedInstanceStateDone;

@Override
protected void onResume() {
    super.onResume();

    savedInstanceStateDone = false;
}

@Override
protected void onStart() {
    super.onStart();

    savedInstanceStateDone = false;
}

protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    savedInstanceStateDone = true;
}


/**
 * Returns true if SavedInstanceState was done, and activity was not restarted or resumed yet.
 */
public boolean isSavedInstanceStateDone() {
    return savedInstanceStateDone;
}

"onStart"메서드 호출에서이 예외가 발생하므로 작동하지 않는 것 같습니다 (여기에 DialogFragment 표시 시도).
안드로이드 개발자

당신은 내 하루를 구했습니다. 프랭크 감사합니다.
Cüneyt

7

나는이 문제에 수년간 뛰어 들었다.
인터넷은 이것에 대한 토론의 수 (수백? 수천?)로 흩어져 있으며, 그 안에있는 혼란과 허위 정보가 많은 것 같습니다.
상황을 더 악화시키기 위해, xkcd "14 표준"만화의 정신으로, 나는 반지에 대한 나의 대답을 던지고 있습니다.
xkcd 14 표준

cancelPendingInputEvents(), commitAllowingStateLoss(), catch (IllegalStateException e), 유사한 솔루션과 모든 극악한 보인다.

다음은 문제를 재현하고 수정하는 방법을 쉽게 보여줍니다.

private static final Handler sHandler = new Handler();
private boolean mIsAfterOnSaveInstanceState = true;

@Override
protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    mIsAfterOnSaveInstanceState = true; // <- To repro, comment out this line
}

@Override
protected void onPostResume()
{
    super.onPostResume();
    mIsAfterOnSaveInstanceState = false;
}

@Override
protected void onResume()
{
    super.onResume();
    sHandler.removeCallbacks(test);
}

@Override
protected void onPause()
{
    super.onPause();
    sHandler.postDelayed(test, 5000);
}

Runnable test = new Runnable()
{
    @Override
    public void run()
    {
        if (mIsAfterOnSaveInstanceState)
        {
            // TODO: Consider saving state so that during or after onPostResume a dialog can be shown with the latest text
            return;
        }

        FragmentManager fm = getSupportFragmentManager();
        DialogFragment dialogFragment = (DialogFragment) fm.findFragmentByTag("foo");
        if (dialogFragment != null)
        {
            dialogFragment.dismiss();
        }

        dialogFragment = GenericPromptSingleButtonDialogFragment.newInstance("title", "message", "button");
        dialogFragment.show(fm, "foo");

        sHandler.postDelayed(test, 5000);
    }
};

2
나는 설명없이 반대표를 던지는 사람들을 사랑합니다. 그냥 아래로 투표 하는 대신 내 솔루션에 결함이있는 방법을 설명하는 것이 더 좋을까요? 반대 투표자의 반대표에 반대 투표를 할 수 있습니까?
swooby

1
예, 문제입니다. 매번이 문제를 제안으로 작성하지만 해결하고 싶지 않습니다.
CoolMind

2
내 생각에 반대표는 내장 된 XKCD의 결과 일 수 있으며 답변은 실제로 소셜 댓글을위한 장소가 아닙니다 (아무리 재미 있거나 사실이든 상관없이).
RestingRobot

6

FragmentManager 대신 FragmentTransaction을 사용하십시오. 아래 코드가 문제를 해결할 것이라고 생각합니다. 그렇지 않은 경우 알려주십시오.

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
SnoozeDialog snoozeDialog = new SnoozeDialog();
snoozeDialog.show(ft, "snooze_dialog");

편집하다:

조각 거래

이 링크를 확인하십시오. 나는 그것이 당신의 질문을 해결할 것이라고 생각합니다.


4
FragmentTransaction을 사용하여 문제를 해결하는 이유에 대한 설명이 있으면 좋을 것입니다.
Hemanshu 2014

3
Dialog # show (FragmentManager, tag)는 동일한 작업을 수행합니다. 이것은 해결책이 아닙니다.
William

3
이 대답은 해결책이 아닙니다. DialogFragment # show (ft)와 show (fm)은 똑같은 일을합니다.
danijoo

@danijoo 둘 다 같은 일을한다는 말이 맞아요. 그러나 일부 전화기에서는 fragmenttransaction 대신 fragmentmanager를 사용하는 경우 이와 유사한 문제가 있습니다. 그래서 제 경우에는 이것이 제 문제를 해결했습니다.
RIJO RV

5

Activity-KTX의 새로운 수명주기 범위를 사용하면 다음 코드 샘플처럼 간단합니다.

lifecycleScope.launchWhenResumed {
   showErrorDialog(...)
}

이 메서드는 onStop () 후에 직접 호출 할 수 있으며 반환시 onResume ()이 호출되면 대화 상자를 성공적으로 표시합니다.


3

많은 뷰는 지연된 실행을 위해 클릭 핸들러와 같은 고급 이벤트를 이벤트 큐에 게시합니다. 따라서 문제는 "onSaveInstanceState"가 활동에 대해 이미 호출되었지만 이벤트 큐에 지연된 "클릭 이벤트"가 포함되어 있다는 것입니다. 따라서이 이벤트가 핸들러로 전달되면

at android.os.Handler.handleCallback(Handler.java:605)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)

코드 show에서 IllegalStateException이 발생합니다.

가장 간단한 해결책은 이벤트 큐를 정리하는 것입니다. onSaveInstanceState

protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        // ..... do some work
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            findViewById(android.R.id.content).cancelPendingInputEvents();
        }
}

이것이 문제가 해결된다는 것을 실제로 확인 했습니까?
mhsmith

Google은 현재 베타 버전 ( activityfragment) 인 androidx 라이브러리의 다음 릴리스에이를 추가했습니다 .
mhsmith

1
@mhsmith 나는이 솔루션이 IllegalStateException으로 내 코드의 문제를 해결했다는 것을 기억합니다
sim

2

대화 조각 개체를 전역으로 만들고 onPause () 메서드에서 dismissAllowingStateLoss ()를 호출합니다.

@Override
protected void onPause() {
    super.onPause();

    if (dialogFragment != null) {
        dialogFragment.dismissAllowingStateLoss();
    }
}

프래그먼트에 값을 할당하고 버튼 클릭이나 어디에서나 show ()를 호출하는 것을 잊지 마십시오.


1

공식적으로 어디에도 언급되지 않았지만 나는이 문제에 몇 번 직면했습니다. 내 경험상 이전 플랫폼에서 조각을 지원하는 호환성 라이브러리에 문제가있어이 문제가 발생합니다. 일반 조각 관리자 API를 사용하여 테스트를 사용합니다. 아무것도 작동하지 않으면 대화 조각 대신 일반 대화 상자를 사용할 수 있습니다.


1
  1. 프로젝트에이 클래스를 추가합니다. ( android.support.v4.app 패키지에 있어야 함 )
package android.support.v4.app;


/ **
 * 2017 년 8 월 16 일 Gil 작성.
 * /

공용 클래스 StatelessDialogFragment extends DialogFragment {
    / **
     * 대화 상자를 표시하고 기존 트랜잭션을 사용하여 조각을 추가 한 다음
     * 상태 손실을 허용하는 동안 트랜잭션.
* * 대부분 {@link #show (FragmentTransaction, String)}을 사용하는 것이 좋지만 * 이것은 당신이 정말로 신경 쓰지 않는 대화를위한 것입니다. (디버그 / 추적 / 광고 등) * * @param 거래 * 조각을 추가 할 기존 트랜잭션. * @param 태그 *이 조각에 대한 태그입니다. * {@link FragmentTransaction # add (Fragment, String) FragmentTransaction.add}. * @return 커밋 된 트랜잭션의 식별자를 반환합니다. * {@link FragmentTransaction # commit () FragmentTransaction.commit ()}. * @see StatelessDialogFragment # showAllowingStateLoss (FragmentManager, String) * / public int showAllowingStateLoss (FragmentTransaction 트랜잭션, 문자열 태그) { mDismissed = 거짓; mShownByMe = 참; transaction.add (this, tag); mViewDestroyed = 거짓; mBackStackId = transaction.commitAllowingStateLoss (); return mBackStackId; } / ** * 주어진 FragmentManager에 조각을 추가하여 대화 상자를 표시합니다. 이것은 편의입니다 * 명시 적으로 트랜잭션을 생성하고, 주어진 태그로 조각을 추가하고, * 상태를 신경 쓰지 않고 커밋합니다. 이것은 거래를 추가 하지 않습니다 . * 백 스택. 조각이 닫히면 제거하기 위해 새 트랜잭션이 실행됩니다. * 활동에서.
* * 대부분 {@link #show (FragmentManager, String)}을 사용하는 것이 좋지만 * 당신이 정말로 신경 쓰지 않는 대화의 경우. (디버그 / 추적 / 광고 등) * * * @param 관리자 *이 프래그먼트가 추가 될 FragmentManager. * @param 태그 *이 조각에 대한 태그입니다. * {@link FragmentTransaction # add (Fragment, String) FragmentTransaction.add}. * @ StatelessDialogFragment # showAllowingStateLoss (FragmentTransaction, String) 참조 * / public void showAllowingStateLoss (FragmentManager 관리자, 문자열 태그) { mDismissed = 거짓; mShownByMe = 참; FragmentTransaction ft = manager.beginTransaction (); ft.add (this, tag); ft.commitAllowingStateLoss (); } }
  1. DialogFragment 대신 StatelessDialogFragment 확장
  2. show 대신 showAllowingStateLoss 메소드를 사용하십시오.

  3. 즐겨 ;)


이 모든 부울 필드는 무엇입니까? 왜 클래스 멤버로 선언되지 않습니까?
undefined

1
boolean 필드는 DialogFragment의 보호 된 멤버이며, 그 이름은 분명히 그 용도를 암시하며 DialogFragment의 논리를 방해하지 않도록 업데이트해야합니다. 원래 DialogFragment 클래스에서이 함수는 존재하지만 공개 액세스는 없습니다
Gil SH

이 멤버들은 보호되지 않고 내부 StatelessDialogFragment에 있습니다. 패키지 중 하나에 넣을 때 컴파일 오류가 발생했습니다. 고마워요. 곧 프로덕션에서 테스트하겠습니다.
정의되지 않음

1

이 코드 사용

FragmentTransaction ft = fm.beginTransaction();
        ft.add(yourFragment, "fragment_tag");
        ft.commitAllowingStateLoss();

대신에

yourFragment.show(fm, "fragment_tag");

1

리플렉션을 사용하여이 문제에 대한 우아한 해결책을 찾았습니다. 위의 모든 솔루션의 문제는 mDismissedmShownByMe 필드입니다. 가 상태를 변경하지 않는다는 것입니다.

아래 샘플 (Kotlin)과 같이 사용자 지정 하단 시트 대화 상자 조각에서 "show"메서드를 재정의하면됩니다.

override fun show(manager: FragmentManager, tag: String?) {
        val mDismissedField = DialogFragment::class.java.getDeclaredField("mDismissed")
        mDismissedField.isAccessible = true
        mDismissedField.setBoolean(this, false)

        val mShownByMeField = DialogFragment::class.java.getDeclaredField("mShownByMe")
        mShownByMeField.isAccessible = true
        mShownByMeField.setBoolean(this, true)

        manager.beginTransaction()
                .add(this, tag)
                .commitAllowingStateLoss()
    }

4
"반사를 사용하여이 문제에 대한 우아한 해결책을 찾았습니다." 어떻게 우아합니까?
Mark Buikema

elegant, stylish, chic, smart, nice, graceful
Рома Богдан

1
그것은 나를 위해 일한 유일한 솔루션입니다. 나는 그것이 우아하다고 생각한다
MBH

0

다음 구현은 Activity특히 대화 상자를 표시하기 위해 수명주기 동안 안전하게 상태 변경을 수행하는 문제를 해결하는 데 사용할 수 있습니다 . 인스턴스 상태가 이미 저장되어있는 경우 (예 : 구성 변경으로 인해), 재개 된 상태가 완료 될 때까지 연기합니다. 수행되었습니다.

public abstract class XAppCompatActivity extends AppCompatActivity {

    private String TAG = this.getClass().getSimpleName();

    /** The retained fragment for this activity */
    private ActivityRetainFragment retainFragment;

    /** If true the instance state has been saved and we are going to die... */
    private boolean instanceStateSaved;

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);

        // get hold of retain Fragment we'll be using
        retainFragment = ActivityRetainFragment.get(this, "Fragment-" + this.getClass().getName());
    }

    @Override
    protected void onPostResume() {
        super.onPostResume();

        // reset instance saved state
        instanceStateSaved = false;

        // execute all the posted tasks
        for (ActivityTask task : retainFragment.tasks) task.exec(this);
        retainFragment.tasks.clear();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        instanceStateSaved = true;
    }

    /**
     * Checks if the activity state has been already saved.
     * After that event we are no longer allowed to commit fragment transactions.
     * @return true if the instance state has been saved
     */
    public boolean isInstanceStateSaved() {
        return instanceStateSaved;
    }

    /**
     * Posts a task to be executed when the activity state has not yet been saved
     * @param task The task to be executed
     * @return true if the task executed immediately, false if it has been queued
     */
    public final boolean post(ActivityTask task)
    {
        // execute it immediately if we have not been saved
        if (!isInstanceStateSaved()) {
            task.exec(this);
            return true;
        }

        // save it for better times
        retainFragment.tasks.add(task);
        return false;
    }

    /** Fragment used to retain activity data among re-instantiations */
    public static class ActivityRetainFragment extends Fragment {

        /**
         * Returns the single instance of this fragment, creating it if necessary
         * @param activity The Activity performing the request
         * @param name The name to be given to the Fragment
         * @return The Fragment
         */
        public static ActivityRetainFragment get(XAppCompatActivity activity, String name) {

            // find the retained fragment on activity restarts
            FragmentManager fm = activity.getSupportFragmentManager();
            ActivityRetainFragment fragment = (ActivityRetainFragment) fm.findFragmentByTag(name);

            // create the fragment and data the first time
            if (fragment == null) {
                // add the fragment
                fragment = new ActivityRetainFragment();
                fm.beginTransaction().add(fragment, name).commit();
            }

            return fragment;
        }

        /** The queued tasks */
        private LinkedList<ActivityTask> tasks = new LinkedList<>();

        @Override
        public void onCreate(Bundle savedInstanceState)
        {
            super.onCreate(savedInstanceState);

            // retain this fragment
            setRetainInstance(true);
        }

    }

    /** A task which needs to be performed by the activity when it is "fully operational" */
    public interface ActivityTask {

        /**
         * Executed this task on the specified activity
         * @param activity The activity
         */
        void exec(XAppCompatActivity activity);
    }
}

그런 다음 다음과 같은 클래스를 사용합니다.

/** AppCompatDialogFragment implementing additional compatibility checks */
public abstract class XAppCompatDialogFragment extends AppCompatDialogFragment {

    /**
     * Shows this dialog as soon as possible
     * @param activity The activity to which this dialog belongs to
     * @param tag The dialog fragment tag
     * @return true if the dialog has been shown immediately, false if the activity state has been saved
     *         and it is not possible to show it immediately
     */
    public boolean showRequest(XAppCompatActivity activity, final String tag) {
        return showRequest(activity, tag, null);
    }

    /**
     * Shows this dialog as soon as possible
     * @param activity The activity to which this dialog belongs to
     * @param tag The dialog fragment tag
     * @param args The dialog arguments
     * @return true if the dialog has been shown immediately, false if the activity state has been saved
     *         and it is not possible to show it immediately
     */
    public boolean showRequest(XAppCompatActivity activity, final String tag, final Bundle args)
    {
        return activity.post(new XAppCompatActivity.ActivityTask() {
            @Override
            public void exec(XAppCompatActivity activity) {
                if (args!= null) setArguments(args);
                show(activity.getSupportFragmentManager(), tag);
            }
        });
    }

    /**
     * Dismiss this dialog as soon as possible
     * @return true if the dialog has been dismissed immediately, false if the activity state has been saved
     *         and it is not possible to dismissed it immediately
     */
    public boolean dismissRequest()
    {
        return dismissRequest(null);
    }

    /**
     * Dismiss this dialog as soon as possible
     * @param runnable Actions to be performed before dialog dismissal
     * @return true if the dialog has been dismissed immediately, false if the activity state has been saved
     *         and it is not possible to dismissed it immediately
     */
    public boolean dismissRequest(final Runnable runnable)
    {
        // workaround as in rare cases the activity could be null
        XAppCompatActivity activity = (XAppCompatActivity)getActivity();
        if (activity == null) return false;

        // post the dialog dismissal
        return activity.post(new XAppCompatActivity.ActivityTask() {
            @Override
            public void exec(XAppCompatActivity activity) {
                if (runnable != null) runnable.run();
                dismiss();
            }
        });
    }
}

앱 상태에 대해 걱정하지 않고 안전하게 대화 상자를 표시 할 수 있습니다.

public class TestDialog extends XAppCompatDialogFragment {

    private final static String TEST_DIALOG = "TEST_DIALOG";

    public static void show(XAppCompatActivity activity) {
        new TestDialog().showRequest(activity, TEST_DIALOG);
    }

    public TestDialog() {}

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        return new AlertDialog.Builder(getActivity(), R.style.DialogFragmentTheme /* or null as you prefer */)
                .setTitle(R.string.title)
                // set all the other parameters you need, e.g. Message, Icon, etc.
                ).create();
    }
}

다음 호출 TestDialog.show(this)하여 내에서 XAppCompatActivity.

당신이 매개 변수를 사용하여보다 일반적인 대화 상자 클래스를 만들려면, 당신은에 저장할 수 있습니다 Bundle의 인수 show()방법으로 그들을 검색 getArguments()onCreateDialog().

전체 접근 방식은 약간 복잡해 보일 수 있지만 활동 및 대화를위한 두 개의 기본 클래스를 만든 후에는 사용하기가 매우 쉽고 완벽하게 작동합니다. Fragment동일한 문제의 영향을받을 수있는 다른 기반 작업에 사용할 수 있습니다.


0

이 오류는 입력 이벤트 (예 : 키 누름 또는 onclick 이벤트)가 다음에 전달되기 때문에 발생하는 것으로 보입니다. onSaveInstanceState 호출 .

해결책은 onSaveInstanceState활동 을 재정의 하고 보류중인 이벤트를 취소하는 것입니다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
        final View rootView = findViewById(android.R.id.content);
        if (rootView != null) {
            rootView.cancelPendingInputEvents();
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.