카테고리 보관물: Android

Android

IllegalStateException : ViewPager를 사용하여 onSaveInstanceState 후에이 조치를 수행 할 수 없습니다. android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112) at

시장의 앱에서 사용자 보고서를 받고 다음 예외를 제공합니다.

java.lang.IllegalStateException: Can not perform this action after onSaveInstanceState
at android.app.FragmentManagerImpl.checkStateLoss(FragmentManager.java:1109)
at android.app.FragmentManagerImpl.popBackStackImmediate(FragmentManager.java:399)
at android.app.Activity.onBackPressed(Activity.java:2066)
at android.app.Activity.onKeyUp(Activity.java:2044)
at android.view.KeyEvent.dispatch(KeyEvent.java:2529)
at android.app.Activity.dispatchKeyEvent(Activity.java:2274)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.widget.TabHost.dispatchKeyEvent(TabHost.java:297)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at android.view.ViewGroup.dispatchKeyEvent(ViewGroup.java:1112)
at com.android.internal.policy.impl.PhoneWindow$DecorView.superDispatchKeyEvent(PhoneWindow.java:1855)
at com.android.internal.policy.impl.PhoneWindow.superDispatchKeyEvent(PhoneWindow.java:1277)
at android.app.Activity.dispatchKeyEvent(Activity.java:2269)
at com.android.internal.policy.impl.PhoneWindow$DecorView.dispatchKeyEvent(PhoneWindow.java:1803)
at android.view.ViewRoot.deliverKeyEventPostIme(ViewRoot.java:2880)
at android.view.ViewRoot.handleFinishedEvent(ViewRoot.java:2853)
at android.view.ViewRoot.handleMessage(ViewRoot.java:2028)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:132)
at android.app.ActivityThread.main(ActivityThread.java:4028)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:491)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
at dalvik.system.NativeStart.main(Native Method)

분명히 그것은 내가 사용하지 않는 FragmentManager와 관련이 있습니다. stacktrace에 내 클래스가 표시되지 않으므로이 예외가 발생하는 위치와 방지 방법을 모릅니다.

기록을 위해 : 나는 탭 호스트를 가지고 있으며 각 탭에는 활동간에 활동 그룹 전환이 있습니다.



답변

내 답변을 여기에서 확인 하십시오 . 기본적으로 나는 단지 :

@Override
protected void onSaveInstanceState(Bundle outState) {
    //No call for super(). Bug on API Level > 11.
}

에 전화를하지 마십시오 super()saveInstanceState방법. 이것은 엉망이되었습니다 …

지원 패키지 의 알려진 버그 입니다.

인스턴스를 저장하고 무언가를 추가해야하는 경우 outState Bundle다음을 사용할 수 있습니다.

@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putString("WORKAROUND_FOR_BUG_19917_KEY", "WORKAROUND_FOR_BUG_19917_VALUE");
    super.onSaveInstanceState(outState);
}

결국 적절한 해결책은 (주석에서 볼 수 있듯이) 다음을 사용하는 것입니다.

transaction.commitAllowingStateLoss();

추가하거나 수행 할 때 FragmentTransaction을 일으키는 것을 Exception.


답변

비슷한 오류 메시지와 관련된 많은 문제가 있습니다. 이 특정 스택 추적의 두 번째 줄을 확인하십시오. 이 예외는 특히에 대한 호출과 관련이 FragmentManagerImpl.popBackStackImmediate있습니다.

세션 상태가 이미 저장 되어 있으면 이 메소드 호출 popBackStack항상 실패 IllegalStateException합니다. 소스를 확인하십시오. 이 예외가 발생하는 것을 막기 위해 할 수있는 일은 없습니다.

  • 통화를 삭제 super.onSaveInstanceState해도 도움이되지 않습니다.
  • 로 조각을 만드는 commitAllowingStateLoss것은 도움이되지 않습니다.

문제를 관찰 한 방법은 다음과 같습니다.

  • 제출 버튼이있는 양식이 있습니다.
  • 버튼을 클릭하면 대화 상자가 생성되고 비동기 프로세스가 시작됩니다.
  • 사용자는 프로세스가 완료되기 전에 홈 키를 클릭합니다 onSaveInstanceState.
  • 프로세스가 완료되고 콜백이 작성되고 popBackStackImmediate시도됩니다.
  • IllegalStateException 던졌습니다.

내가 해결하기 위해 한 일은 다음과 같습니다.

IllegalStateException콜백 을 피할 수 없으므로 catch & 무시하십시오.

try {
    activity.getSupportFragmentManager().popBackStackImmediate(name);
} catch (IllegalStateException ignored) {
    // There's no way to avoid getting this if saveInstanceState has already been called.
}

앱이 중단되는 것을 막기에 충분합니다. 그러나 이제 사용자는 앱을 복원하고 자신이 눌렀다 고 생각한 버튼이 전혀 눌리지 않았 음을 알 수 있습니다 (생각합니다). 양식 조각이 여전히 표시됩니다!

이 문제를 해결하려면 대화 상자를 만들 때 프로세스가 시작되었음을 나타내는 상태를 만드십시오.

progressDialog.show(fragmentManager, TAG);
submitPressed = true;

이 상태를 번들에 저장하십시오.

@Override
public void onSaveInstanceState(Bundle outState) {
    ...
    outState.putBoolean(SUBMIT_PRESSED, submitPressed);
}

다시로드하는 것을 잊지 마십시오 onViewCreated

그런 다음 다시 시작할 때 제출이 이전에 시도 된 경우 조각을 롤백하십시오. 이를 통해 사용자는 제출되지 않은 양식으로 돌아가는 것을 방지 할 수 있습니다.

@Override
public void onResume() {
    super.onResume();
    if (submitPressed) {
        // no need to try-catch this, because we are not in a callback
        activity.getSupportFragmentManager().popBackStackImmediate(name);
        submitPressed = false;
    }
}


답변

isFinishing()프래그먼트를 표시하기 전에 활동을 확인 하고주의하십시오 commitAllowingStateLoss().

예:

if(!isFinishing()) {
FragmentManager fm = getSupportFragmentManager();
            FragmentTransaction ft = fm.beginTransaction();
            DummyFragment dummyFragment = DummyFragment.newInstance();
            ft.add(R.id.dummy_fragment_layout, dummyFragment);
            ft.commitAllowingStateLoss();
}


답변

2017 년 10 월이며 Google은 Lifecycle 구성 요소라고하는 새로운 기능으로 Android 지원 라이브러리를 만듭니다. ‘onSaveInstanceState 후에이 작업을 수행 할 수 없습니다’문제에 대한 새로운 아이디어를 제공합니다.

한마디로 :

  • 수명주기 구성 요소를 사용하여 프래그먼트를 팝업하기에 적합한 시간인지 판별하십시오.

Explain이있는 더 긴 버전 :

  • 왜이 문제가 발생합니까?

    그것은 당신이 FragmentManager당신의 조각을 위해 트랜잭션을 커밋하기 위해 당신의 활동 (내가 생각하는 조각을 보유 할 것입니까?)에서 사용하려고하기 때문 입니다. 일반적으로 이것은 호스트 조각 활동이 이미 savedInstanceState메서드를 호출 하는 동안 곧 다가오는 조각에 대한 트랜잭션을 시도하는 것처럼 보입니다 (사용자는 홈 버튼을 터치하여 활동이 호출되므로 onStop()내 경우에는 이유입니다)

    일반적으로이 문제는 발생하지 않아야합니다. 우리는 항상 처음부터 조각을 활동에로드하려고합니다 onCreate(). 그러나 때로는 이런 일이 발생합니다 . 특히 해당 활동에로드 할 조각을 결정할 수 없거나 AsyncTask블록 에서 조각을로드하려고하면 (또는 시간이 조금 걸립니다). 프래그먼트 트랜잭션이 실제로 발생하기 전의 시간이지만 액티비티의 onCreate()메소드 후에 사용자는 무엇이든 할 수 있습니다. 사용자가 활동의 onSavedInstanceState()메소드 를 트리거하는 홈 단추를 누르면 can not perform this action충돌이 발생합니다.

    이 문제에 대해 더 깊이 알고 싶다면이 블로그 게시물을 살펴 보는 것이 좋습니다 . 소스 코드 레이어 내부를 자세히 살펴보고 이에 대해 많은 것을 설명합니다. 또한 commitAllowingStateLoss()이 충돌을 해결 하기 위해 방법을 사용하지 않아야하는 이유를 제공합니다 (코드에 아무런 도움이되지 않음)

  • 이 문제를 해결하는 방법?

    • commitAllowingStateLoss()조각을로드 하는 방법을 사용해야합니까 ? 아니해서는 안됩니다 .

    • onSaveInstanceState메서드를 재정의해야하고 그 super안에있는 메서드를 무시 해야합니까 ? 아니해서는 안됩니다 .

    • isFinishing호스트 활동이 프래그먼트 트랜잭션에 적합한 순간인지 확인 하기 위해 마법의 내부 활동을 사용해야 합니까? 예, 이것은 올바른 방법 처럼 보입니다 .

  • Lifecycle 구성 요소가 수행 할 수있는 작업을 살펴보십시오 .

    기본적으로 Google은 AppCompatActivity클래스 (및 프로젝트에서 사용해야하는 다른 기본 클래스) 내부에서 일부 구현을 수행 하므로 현재 수명주기 상태를 보다 쉽게 확인할 수 있습니다. 문제를 다시 살펴보십시오. 왜이 문제가 발생합니까? 잘못된 타이밍에 무언가를하기 때문입니다. 그래서 우리는 그렇게하지 않으려 고 노력하며이 문제는 사라질 것입니다.

    나는 내 자신의 프로젝트를 위해 약간의 코드를 작성한다 LifeCycle. 나는 Kotlin으로 코딩합니다.

val hostActivity: AppCompatActivity? = null // the activity to host fragments. It's value should be properly initialized.

fun dispatchFragment(frag: Fragment) {
    hostActivity?.let {
       if(it.lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)){
           showFragment(frag)
       }
    }
}

private fun showFragment(frag: Fragment) {
    hostActivity?.let {
        Transaction.begin(it, R.id.frag_container)
                .show(frag)
                .commit()
    }

위에 보여 주듯이. 호스트 활동의 수명주기 상태를 확인합니다. 지원 라이브러리 내에 Lifecycle 구성 요소가 있으면 더 구체적 일 수 있습니다. 코드 lifecyclecurrentState.isAtLeast(Lifecycle.State.RESUMED)는 현재 상태가 적어도 onResume늦지 않은 경우를 의미 합니다. 어떤 다른 수명 상태에서 내 메소드가 실행되지 않도록합니다.onStop .

  • 다 되었습니까?

    당연히 아니지. 내가 보여준 코드는 응용 프로그램 충돌을 막는 새로운 방법을 알려줍니다. 그러나의 상태로 전환 onStop되면 해당 코드 줄은 아무런 작업을 수행하지 않으므로 화면에 아무것도 표시되지 않습니다. 사용자가 응용 프로그램으로 돌아 오면 빈 화면이 표시됩니다. 빈 화면으로 조각이 전혀 표시되지 않습니다. 나쁜 경험입니다 (예 : 충돌보다 조금 낫습니다).

    그래서 여기에 더 좋은 것이있을 수 있기를 바랍니다. 앱이 나중에 인생 상태가되면 충돌하지 않습니다. onResume . 트랜잭션 방법은 수명 상태를 인식합니다. 또한 액티비티는 사용자가 앱으로 돌아온 후 해당 조각 트랜잭션 작업을 계속 완료하려고 시도합니다.

    이 방법에 더 많은 것을 추가합니다.

class FragmentDispatcher(_host: FragmentActivity) : LifecycleObserver {
    private val hostActivity: FragmentActivity? = _host
    private val lifeCycle: Lifecycle? = _host.lifecycle
    private val profilePendingList = mutableListOf<BaseFragment>()

    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    fun resume() {
        if (profilePendingList.isNotEmpty()) {
            showFragment(profilePendingList.last())
        }
    }

    fun dispatcherFragment(frag: BaseFragment) {
        if (lifeCycle?.currentState?.isAtLeast(Lifecycle.State.RESUMED) == true) {
            showFragment(frag)
        } else {
            profilePendingList.clear()
            profilePendingList.add(frag)
        }
    }

    private fun showFragment(frag: BaseFragment) {
        hostActivity?.let {
            Transaction.begin(it, R.id.frag_container)
                    .show(frag)
                    .commit()
        }
    }
}

dispatcher클래스 내부에 목록을 유지 관리하여 해당 조각을 저장하면 거래 작업을 완료 할 기회가 없습니다. 사용자가 홈 화면에서 돌아와서 여전히 실행 대기중인 조각이 있음을 발견 resume()하면 @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)주석 아래의 방법으로 이동합니다 . 이제 예상대로 작동해야한다고 생각합니다.


답변

이 문제에 대한 다른 해결책이 있습니다.

개인 멤버 변수를 사용하면 반환 된 데이터를 의도로 설정 한 다음 super.onResume (); 이후에 처리 할 수 ​​있습니다.

이렇게 :

private Intent mOnActivityResultIntent = null;

@Override
protected void onResume() {
    super.onResume();
    if(mOnActivityResultIntent != null){
        ... do things ...
        mOnActivityResultIntent = null;
    }
 }

@Override
public void onActivityResult(int requestCode, int resultCode, Intent data){
    if(data != null){
        mOnActivityResultIntent = data;
    }
}


답변

짧고 효과적인 해결책 :

간단한 단계를 따르십시오

단계

1 단계 : onSaveInstanceState각 조각의 상태를 무시 합니다. 그리고 슈퍼 메소드를 제거하십시오.

 @Override
public void onSaveInstanceState( Bundle outState ) {

}  

2 단계 : 사용
fragmentTransaction.commitAllowingStateLoss( );

fragmentTransaction.commit( ); 조각화 작업 대신에 .


답변

주의 하여 사용 transaction.commitAllowingStateLoss()하면 사용자에게 나쁜 경험이 될 수 있습니다. 이 예외가 발생하는 이유에 대한 자세한 내용은 이 게시물을 참조하십시오 .