web-dev-qa-db-ja.com

onRequestPermissionsResult()内からDialogFragmentのshow()を呼び出すと、MarshmallowでIllegalStateExceptionが発生する

手順:

  1. FragmentまたはActivityに許可をリクエストする
  2. onRequestPermissionsResult()内からDialogFragmentを表示します
  3. _Java.lang.IllegalStateException_がスローされます:onSaveInstanceStateの後でこのアクションを実行できません

(postDelayedを使用して)少し遅れてダイアログを表示すると、これは起こりません。 http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html )によると、ポストハニカムデバイスでは次のようにcommit()を実行できますonPause()およびonStop()には、STATE LOSSまたはEXCEPTIONはありません。以下は、サンプルプロジェクトソース、ログファイル、記録された問題へのリンクです。 https://drive.google.com/folderview?id=0BwvvuYbQTUl6STVSZF9TX2VUeHM&usp=sharing

また、私は問題をオープンしました https://code.google.com/p/Android/issues/detail?id=190966 がWorkingAsIntendedとマークされ、例外をキャッチすることを提案しています。しかし、これは問題を解決しません。私はそれを解決する他の方法を知っていますが、これはAndroidバグではありませんか?

[〜#〜] update [〜#〜]バグのステータスが再び「割り当て済み」になります。すぐに修正されることを願っています。私の一時的な解決策は

_new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // do your fragment transaction here
    }
}, 200);
_
39
android_dev

バグは受け入れられ、修正される予定ですが、私はpostDelayedとタイマーソリューションに強く同意しません。これを行う最良の方法は、アクティビティに状態フラグを導入することです。この状態フラグでは、コールバックで設定し、onResumeなどで使用します。例えば:

private boolean someFlag;

public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
// some code checking status
    someFlag = true;
}

そしてonResumeで:

protected void onResume() {
    if(someFlag == true) {
        doSomething();
        someFlag = false;
    }
}
24
Kenneth

また、これはAndroidバグです。彼らがあなたの問題をマークしたとは信じられませんWorkingAsIntended。今のところ唯一の解決策は、onRequestPermissionsResult()までAndroid=人々はこれを適切に修正します。

実行を遅らせる方法を誰かが疑問に思っている場合、これはこの問題を解決する私の方法です:

 @Override public void onRequestPermissionsResult(int requestCode、String [] permissions、int [] grantResults){
 
 if(requestCode == PERMISSION_CODE){
 if(/*許可/拒否* /){
 new Timer()。schedule(new TimerTask(){
 @Override public void run(){
 //アクションを実行( LIKE FRAGMENT TRANSACTION ETC。)
} 
}、0); 
} 
} 

これは基本的にonRequestPermissionsResult()が完了するまで実行を遅らせるため、Java.lang.IllegalStateExceptionは取得されません。これは私のアプリで動作します。

6
ldulcic

DialogFragmentを使用しているため、フラグまたは状態を保持し、onPostResumeにダイアログを表示する必要があります。これは、onPostResumeや他のライフサイクルメソッドではなく、onResumeで行う必要があります。

documentation に明記されているように、onPostResumeまたはonResumeFragments(FragmentActivityの場合)以外のアクティビティライフサイクルメソッドでトランザクションをコミットすると、場合によってはメソッドを呼び出すことができますアクティビティの状態が完全に復元される前。

したがって、PostResumeにダイアログを表示すると、問題ありません。

4
CanC

次のようなものを試してください:

// ...
private Runnable mRunnable;

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

   if (mRunnable != null) {
       mRunnable.run();
       mRunnable = null;
   }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                           @NonNull int[] grantResults) {
   super.onRequestPermissionsResult(requestCode, permissions, grantResults);

   if (/* PERMISSION_DENIED */) {
      mRunnable = /* new Runnable which show dialogFragment*/;
   }
}
4
Scott Key

これはAndroid= https://code.google.com/p/Android/issues/detail?id=190966&q=label%3AReportedBy-Developer&colspec=ID%20Typeのバグです。 %20Status%20Owner%20Summary%20Stars

したがって、現時点では、回避策が必要です。 @ldulcicのソリューションを使用できます。または、HandlerのpostDelayメソッドを使用することもできます。 2番目のオプションが推奨されます。

私はこれをはるかに確定的な方法であると信じている方法で回避しました。タイマーを使用するのではなく、基本的に、Activity.onResumeFragments()まで結果をキューに入れます(フラグメントを使用しない場合は、Activity.onResume()で行うこともできます)。 onResumeFragments()を実行したのは、アクセス許可を要求した特定のフラグメントにも結果をルーティングするため、フラグメントの準備ができていることを確認する必要があるためです。

次に、onRequestPermissionsResult()を示します。

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
                                       @NonNull int[] grantResults) {
    // This is super ugly, but google has a bug that they are calling this method BEFORE
    // onResume... which screws up fragment lifecycle big time.  So work around it.  But also
    // be robust enough to still work if/when they fix the bug.
    if (mFragmentsResumed) {
        routePermissionsResult(requestCode, permissions, grantResults);
    } else {
        mQueuedPermissionGrantResults = grantResults;
        mQueuedPermissionRequestCode = requestCode;
        mQueuedPermissions = permissions;
    }
}

次に、onResumeFragments()を次に示します。

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

    mFragmentsResumed = true;

    if (mQueuedPermissionGrantResults != null) {
        routePermissionsResult(mQueuedPermissionRequestCode, mQueuedPermissions,
                mQueuedPermissionGrantResults);
    }

完全を期すために、mFragmentsResumedフラグをクリアするonPause()を次に示します。

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

    mFragmentsResumed = false;
}

Googleがバグを修正したときにこのコードが機能を停止したくないので、mFragmentsResumedフラグを使用しました(onRequestPermissionsResultでキューに入れられた変数を設定しているだけで、onResumeFragmentsがこの前に呼び出されます)。

2
pdub

また、ハンドラーで解決しましたが、ルーパーをMainLooperに設定するタイマーの使用を回避できました。したがって、ビューがレンダリングされると実行されます。

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        // do your fragment transaction here
    }
});
0
Rubén Viguera

issue のコメントの1つで説明されているように、これはサポートライブラリのDialogFragment(つまり、Android.support.v4.app.DialogFragment)。 「ネイティブ」を使用するようにコードを変更できますDialogFragmentAndroid.app.DialogFragment)そしてそれはうまくいきます。

ネイティブDialogFragmentを使用できない場合があることを知っていますが、この場合、ランタイム権限(最新バージョンのAndroidでのみ利用できる機能)を参照していることを考えると、おそらくこれを行うことができます。私はこの正確な問題を抱えていて、この方法でそれを修正することができました。

0
Franco