onActivityResult에서 DialogFragment 표시


82

내 조각에 대한 onActivityResult에 다음 코드가 있습니다.

onActivityResult(int requestCode, int resultCode, Intent data){
   //other code
   ProgressFragment progFragment = new ProgressFragment();  
   progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG);
   // other code
}

그러나 다음과 같은 오류가 발생합니다.

Caused by: java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState   

무슨 일이 일어나고 있는지 아는 사람이 있거나 어떻게 해결할 수 있습니까? Android 지원 패키지를 사용하고 있습니다.

답변:


75

Android 지원 라이브러리를 사용하는 경우 onResume 메서드는 조각으로 재생할 올바른 위치가 아닙니다. onResumeFragments 메소드에서 수행해야합니다. onResume 메소드 설명을 참조하십시오. http://developer.android.com/reference/android/support/v4/app/FragmentActivity.html#onResume%28%29

따라서 내 관점에서 올바른 코드는 다음과 같아야합니다.

private boolean mShowDialog = false;

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data){
  super.onActivityResult(requestCode, resultCode, data);

  // remember that dialog should be shown
  mShowDialog = true;
}

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

  // play with fragments here
  if (mShowDialog) {
    mShowDialog = false;

    // Show only if is necessary, otherwise FragmentManager will take care
    if (getSupportFragmentManager().findFragmentByTag(PROG_DIALOG_TAG) == null) {
      new ProgressFragment().show(getSupportFragmentManager(), PROG_DIALOG_TAG);
    }
  }
}

13
+1, 정답입니다. 참고 onResumeFragments()에 존재하지 않는 Activity클래스입니다. 기본을 사용하는 Activity경우 onPostResume()대신 사용해야 합니다.
Alex Lockwood

3
이 솔루션을 구현하기 전에이 내용을 읽고 왜 해킹인지 확인하십시오. 이 질문에 대한 다른 솔루션의 주석에는 훨씬 더 간단한 솔루션이 숨겨져 있습니다.
twig

1
super.onActivityResult를 호출해도 IllegalStateException이 방지되지 않으므로 subj가 수정되지 않습니다. 문제
demaksee

1
이 질문은이 문제에 대한 Google의 첫 번째 히트이지만 받아 들인 대답은 내 의견으로는 최선이 아닙니다. 대신이 답변을 수락해야합니다. stackoverflow.com/a/30429551/1226020
JHH

27

편집 : 버그는 아니지만 조각 프레임 워크의 결함이 더 많습니다. 이 질문에 대한 더 나은 대답은 위의 @Arcao가 제공 한 것입니다.

---- 원본 게시물 ----

실제로 지원 패키지 의 알려진 버그 입니다 (편집 : 실제로 버그가 아닙니다. @ alex-lockwood의 의견 참조). 버그 보고서의 주석에 게시 된 해결 방법은 다음과 같이 DialogFragment의 소스를 수정하는 것입니다.

public int show(FragmentTransaction transaction, String tag) {
    return show(transaction, tag, false);
}


public int show(FragmentTransaction transaction, String tag, boolean allowStateLoss) {
    transaction.add(this, tag);
    mRemoved = false;
    mBackStackId = allowStateLoss ? transaction.commitAllowingStateLoss() : transaction.commit();
    return mBackStackId;
}

이것은 거대한 해킹입니다. 내가 실제로 한 방법은 원래 조각에서 등록 할 수있는 대화 조각을 만드는 것뿐이었습니다. 다른 대화 조각이 작업을 수행 할 때 (예 : 해제 됨) 모든 청취자에게 사라질 것이라고 말했습니다. 나는 이렇게했다 :

public static class PlayerPasswordFragment extends DialogFragment{

 Player toJoin;
 EditText passwordEdit;
 Button okButton;
 PlayerListFragment playerListFragment = null;

 public void onCreate(Bundle icicle){
   super.onCreate(icicle);
   toJoin = Player.unbundle(getArguments());
   Log.d(TAG, "Player id in PasswordFragment: " + toJoin.getId());
 }

 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle icicle){
     View v = inflater.inflate(R.layout.player_password, container, false);
     passwordEdit = (EditText)v.findViewById(R.id.player_password_edit);
     okButton = (Button)v.findViewById(R.id.ok_button);
     okButton.setOnClickListener(new View.OnClickListener(){
       public void onClick(View v){
         passwordEntered();
       }
     });
     getDialog().setTitle(R.string.password_required);
     return v;
 }

 public void passwordEntered(){
   //TODO handle if they didn't type anything in
   playerListFragment.joinPlayer(toJoin, passwordEdit.getText().toString());
   dismiss();
 }

 public void registerPasswordEnteredListener(PlayerListFragment playerListFragment){
   this.playerListFragment = playerListFragment;
 }

 public void unregisterPasswordEnteredListener(){
   this.playerListFragment = null;
 }
}

이제 일이 발생하면 PlayerListFragment에 알리는 방법이 있습니다. unregisterPasswordEnteredListener를 적절하게 호출하는 것이 매우 중요합니다 (위의 경우 PlayerListFragment가 "종료"되는 경우). 그렇지 않으면이 대화 상자 조각이 해당 리스너가 더 이상 존재하지 않을 때 등록 된 리스너에서 함수를 호출하려고 할 수 있습니다.


3
소스를 복사 할 필요가없는 솔루션 ... 그냥 재정의 show()하고 IllegalStateException.
Jeffrey Blattman 2012

1
DialogFragment의 소스를 수정하는 방법은 무엇입니까? 아니면 게시물 끝에 언급 된 솔루션을 게시 할 수 있습니까?
Piotr Ślesarew

1
@PeterSlesarew 나는 내 (다소 구체적인) 솔루션을 게시했습니다.
Kurtis Nusbaum 2013

9
이건 버그 가 아니야 ! Android 프레임 워크는 내부에서 조각 트랜잭션을 수행하는 것이 안전하지 않기 때문에 의도적으로 예외를 던지고 있습니다 onActivityResult()! 대신에이 솔루션을 시도해보십시오 stackoverflow.com/questions/16265733/...
알렉스 록우드

2
@AlexLockwood이 질문을했을 때 문서에서 이에 대해 경고하지 않았습니다. 또한, 솔루션의 외모 좋은 반면 지금 , 그것은 사월 2012 년 다시 작동하지 않았다 onPostResumeonResumeFragments지원 라이브러리에 모두 비교적 새로운 추가이다.
hrnt

24

@Natix 가 남긴 댓글 은 일부 사람들이 삭제했을 수있는 빠른 한 줄입니다.

이 문제에 대한 가장 간단한 해결책은 코드를 실행하기 전에 super.onActivityResult () 를 호출 하는 것입니다. 이것은 지원 라이브러리를 사용하는지 여부에 관계없이 작동하며 활동에서 행동 일관성을 유지합니다.

다음이 있습니다.

내가 이것에 대해 더 많이 읽을수록 내가 본 더 미친 해킹.

여전히 문제가 발생하는 경우 Alex Lockwood 가 확인해야합니다.


상속이 있으면 어떻게됩니까? super를 호출하기 전에 먼저 코드를 실행하려면 super.onActivityResult () 호출이 문제가 될 수 있습니다. super 클래스는 onActivityResult 내부에 자체 코드를 가질 수 있습니다.주의하세요.
Ricard

super.onActivityResult(requestCode, resultCode, data)내 코드 앞에을 추가하고 있는데 문제가 해결되었습니다. 그러나 상속을 추가하거나 기본 onActivityResult를 재정의 할 때 onStart / onResume을 수동으로 처리해야합니다
mochadwi

14

Android 버그라고 생각합니다. 기본적으로 Android는 활동 / 조각 수명주기 (onStart () 이전)의 잘못된 지점에서 onActivityResult를 호출합니다.

버그는 https://issuetracker.google.com/issues/36929762 에서보고됩니다.

기본적으로 Intent를 나중에 onResume ()에서 처리 한 매개 변수로 저장하여 해결했습니다.

[편집] 요즘에는 2012 년에는 사용할 수 없었던이 문제에 대한 더 나은 솔루션이 있습니다. 다른 답변을 참조하십시오.


8
사실 그것은 실제로 버그가 아닙니다. 이 코멘트에 뾰족한 밖으로, 그것은 명확하게 언급 onActivityResult()하기 전에 호출onResume()
커티스 Nusbaum

2
버그에 대한 마지막 댓글을 읽었습니까? 버그는 onActivityResult ()가 onResume () 전에 호출되는 것이 아니라 onStart () 전에 호출된다는 것입니다.
hrnt

아, 맞습니다. 그것을 놓쳤다. 여전히 다른 버그 보고서가 내 문제와 좀 더 관련이 있다고 생각합니다.
Kurtis Nusbaum 2012

onActivityResult가 호출 될 때 잘 정의됩니다. 따라서 어떤 경우에는 부적절 해 보일지라도 버그가 될 수 없습니다.
sstn 2012

1
@sstn, 자세히 설명해 주시겠습니까? onActivityResult가 호출 될 때 잘 정의되어 있습니다 (= onResume 직전). Android는 onResume 직전에 onActivityResult를 호출하지 않습니다. 따라서 그것은 버그입니다.
hrnt

11

편집 : 또 다른 옵션, 그리고 아마도 최고 (또는 적어도 지원 라이브러리가 기대하는 것 ...)

Android 지원 라이브러리와 함께 DialogFragments를 사용하는 경우 FragmentActivity의 하위 클래스를 사용해야합니다. 다음을 시도하십시오.

onActivityResult(int requestCode, int resultCode, Intent data) {

   super.onActivityResult(requestCode, resultCode, intent);
   //other code

   ProgressFragment progFragment = new ProgressFragment();  
   progFragment.show(getActivity().getSupportFragmentManager(), PROG_DIALOG_TAG);

   // other code
}

FragmentActivity 의 소스 를 살펴본 결과 상태를 잃지 않고 조각을 재개하기 위해 내부 조각 관리자를 호출하는 것 같습니다.


여기에 나열되지 않은 솔루션을 찾았습니다. Handler를 만들고 Handler에서 대화 조각을 시작합니다. 따라서 코드를 약간 편집하십시오.

onActivityResult(int requestCode, int resultCode, Intent data) {

   //other code

   final FragmentManager manager = getActivity().getSupportFragmentManager();
   Handler handler = new Handler();
   handler.post(new Runnable() {
       public void run() {
           ProgressFragment progFragment = new ProgressFragment();  
           progFragment.show(manager, PROG_DIALOG_TAG);
       }
   }); 

  // other code
}

이것은 나에게 더 깨끗하고 덜 해커 보인다.


5
핸들러를 사용하여이 문제를 해결하면 지연 만 추가되므로 문제가 발생할 가능성더 적습니다 . 그러나 문제가 사라질 것이라는 보장은 없습니다! .NET을 사용하여 경쟁 조건을 해결하는 것과 비슷 Thread#sleep()합니다.
Alex Lockwood

27
전화 super.onActivityResult()는 가장 간단한 작업 솔루션이며 아마도 받아 들여진 대답 일 것입니다! 나는 우연히 누락 된 슈퍼 콜을 발견했고 그것을 추가하는 것이 효과가 있다는 것에 즐겁게 놀랐다. 이를 통해이 페이지에서 언급 한 이전 해킹 중 하나를 제거 할 수있었습니다 (대화 상자를 임시 변수에 저장하고에서 표시 onResume()).
Natix

좋은 솔루션입니다. 유일한 문제는 onActivityResult()조각이 결과를 처리했는지 여부를 나타내는 값을 반환하지 않는다는 것입니다.
Michael

super.onActivityResult () 호출은 내 프로젝트에서 IllegalStateException 충돌을 해결하지 못합니다
demaksee

9

두 가지 DialogFragment 쇼 () 방법은 - show(FragmentManager manager, String tag)show(FragmentTransaction transaction, String tag).

FragmentManager 버전의 메서드를 사용하려는 경우 (원래 질문에서와 같이) 쉬운 해결책은이 메서드를 재정의하고 commitAllowingStateLoss를 사용하는 것입니다.

public class MyDialogFragment extends DialogFragment {

  @Override 
  public void show(FragmentManager manager, String tag) {
      FragmentTransaction ft = manager.beginTransaction();
      ft.add(this, tag);
      ft.commitAllowingStateLoss();
  }

}

show(FragmentTransaction, String)이 방법을 재정의 하는 것은 원래 DialogFragment 코드 내의 일부 내부 변수도 수정해야하기 때문에 쉽지 않으므로 권장하지 않습니다. 해당 메서드를 사용하려면 허용 된 답변 (또는 Jeffrey Blattman).

commitAllowingStateLoss를 사용하는 데는 약간의 위험이 있습니다. 문서에는 "Commit ()과 유사하지만 활동의 상태가 저장된 후 커밋이 실행되도록 허용합니다. 활동이 나중에 해당 상태에서 복원되어야하는 경우 커밋이 손실 될 수 있으므로 위험합니다. 이므로 UI ​​상태가 사용자에게 예기치 않게 변경 되어도 괜찮은 경우에만 사용해야합니다. "


4

onSaveInstanceState () 메서드를 호출 한 연결된 활동 후에는 대화 상자를 표시 할 수 없습니다. 분명히 onSaveInstanceState ()는 onActivityResult () 전에 호출됩니다. 따라서이 콜백 메서드 OnResumeFragment ()에서 대화 상자를 표시해야합니다. DialogFragment의 show () 메서드를 재정의 할 필요가 없습니다. 이것이 당신을 도울 것입니다.


3

나는 hmt의 솔루션에서 부분적으로 기반을 둔 세 번째 솔루션을 생각해 냈습니다. 기본적으로 onResume ()에 표시 될 DialogFragments의 ArrayList를 생성합니다.

ArrayList<DialogFragment> dialogList=new ArrayList<DialogFragment>();

//Some function, like onActivityResults
{
    DialogFragment dialog=new DialogFragment();
    dialogList.add(dialog);
}


protected void onResume()
{
    super.onResume();
    while (!dialogList.isEmpty())
        dialogList.remove(0).show(getSupportFragmentManager(),"someDialog");
}

3

onActivityResult ()는 onResume () 전에 실행됩니다. onResume () 이상에서 UI를 수행해야합니다.

부울을 사용하거나이 두 메서드간에 결과가 반환되었음을 알리는 데 필요한 모든 것을 사용합니다.

... 그게 다야. 단순한.


2

나는 이것이 꽤 오래 전에 대답했다는 것을 알고 있지만 여기에서 본 다른 대답보다 훨씬 쉬운 방법이 있습니다 ... 내 특정 경우에는 조각 onActivityResult ()에서 DialogFragment를 표시해야했습니다. 방법.

이것은 그것을 처리하는 내 코드이며 아름답게 작동합니다.

DialogFragment myFrag; //Don't forget to instantiate this
FragmentTransaction trans = getActivity().getSupportFragmentManager().beginTransaction();
trans.add(myFrag, "MyDialogFragmentTag");
trans.commitAllowingStateLoss();

다른 게시물 중 일부에서 언급했듯이 상태 손실로 커밋하면주의하지 않으면 문제가 발생할 수 있습니다 ... 제 경우에는 대화 상자를 닫는 버튼을 사용하여 사용자에게 오류 메시지를 표시했습니다. 잃어버린 상태는 큰 문제가 아닙니다.

도움이 되었기를 바랍니다...


2

오래된 질문이지만 가장 간단한 방법으로 해결했습니다.

getActivity().runOnUiThread(new Runnable() {
    @Override
        public void run() {
            MsgUtils.toast(getString(R.string.msg_img_saved),
                    getActivity().getApplicationContext());
        }
    });

2

#onActivityResult ()가 호출되면 상위 활동이 이미 #onSaveInstanceState ()를 호출했기 때문에 발생합니다.

Runnable을 사용하여 #onActivityResult ()의 작업 (대화 상자 표시)을 "저장"하여 활동이 준비되었을 때 나중에 사용합니다.

이 접근 방식을 통해 우리는 원하는 행동이 항상 작동하는지 확인합니다.

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == YOUR_REQUEST_CODE) {
        mRunnable = new Runnable() {
            @Override
            public void run() {
                showDialog();
            }
        };
    } else {
        super.onActivityResult(requestCode, resultCode, data);
    }
}

@Override
public void onStart() {
    super.onStart();
    if (mRunnable != null) {
        mRunnable.run();
        mRunnable = null;
    }
}

0

내가 찾은 가장 깨끗한 해결책은 다음과 같습니다.

@Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            onActivityResultDelayed(requestCode, resultCode, data);
        }
    });
}

public void onActivityResultDelayed(int requestCode, int resultCode, Intent data) {
    // Move your onActivityResult() code here.
}

0

.show(getSupportFragmentManager(), "MyDialog");활동 중에이 오류가 발생했습니다 .

.show(getSupportFragmentManager().beginTransaction(), "MyDialog");먼저 시도하십시오 .

여전히 작동하지 않는 경우이 게시물 ( onActivityResult의 Show DialogFragment )이 문제를 해결하는 데 도움이됩니다.


0

또 다른 방법:

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case Activity.RESULT_OK:
            new Handler(new Handler.Callback() {
                @Override
                public boolean handleMessage(Message m) {
                    showErrorDialog(msg);
                    return false;
                }
            }).sendEmptyMessage(0);
            break;
        default:
            super.onActivityResult(requestCode, resultCode, data);
    }
}


private void showErrorDialog(String msg) {
    // build and show dialog here
}

0

super.onActivityResult(requestCode, resultCode, data);조각을 처리하기 전에 호출 하십시오.


-3

이 문제는 onActivityResult ()가 onstart () 전에 호출하기 때문입니다.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
      onStart();
      //write you code here
}

Android 수명주기 메서드를 직접 호출 해서는 안됩니다 . 시스템에서만 호출해야합니다.
Chantell Osejo 2017 년
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.