DialogFragmentをホストするFragmentActivityがあります。
DialogFragmentはネットワーク要求を実行し、Facebook認証を処理するため、ローテーション中も保持する必要があります。
この問題に関連する他のすべての質問を読みましたが、実際に問題を解決したものはありません。
PutFragmentとgetFragmentを使用してFragmentインスタンスを保存し、アクティビティの再作成中に再度取得しています。
ただし、onRestoreInstanceStateでgetFragmentを呼び出すと、常にNULLポインター例外が発生します。また、回転中にダイアログが閉じられないようにしたいのですが、今のところ、そのインスタンスを保持することさえできません。
何が間違っているのでしょうか?
現在、私のコードは次のようになっています。
public class OKLoginActivity extends FragmentActivity implements OKLoginDialogListener
{
private OKLoginFragment loginDialog;
private static final String TAG_LOGINFRAGMENT = "OKLoginFragment";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getSupportFragmentManager();
if(savedInstanceState == null)
{
loginDialog = new OKLoginFragment();
loginDialog.show(fm, TAG_LOGINFRAGMENT);
}
}
@Override
public void onSaveInstanceState(Bundle outState)
{
getSupportFragmentManager().putFragment(outState,TAG_LOGINFRAGMENT, loginDialog);
}
@Override
public void onRestoreInstanceState(Bundle inState)
{
FragmentManager fm = getSupportFragmentManager();
loginDialog = (OKLoginFragment) fm.getFragment(inState, TAG_LOGINFRAGMENT);
}
}
これは例外スタックトレースです。
02-01 16:31:13.684: E/AndroidRuntime(9739): FATAL EXCEPTION: main
02-01 16:31:13.684: E/AndroidRuntime(9739): Java.lang.RuntimeException: Unable to start activity ComponentInfo{io.openkit.example.sampleokapp/io.openkit.OKLoginActivity}: Java.lang.NullPointerException
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:2180)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.ActivityThread.handleLaunchActivity(ActivityThread.Java:2230)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.ActivityThread.handleRelaunchActivity(ActivityThread.Java:3692)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.ActivityThread.access$700(ActivityThread.Java:141)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1240)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.os.Handler.dispatchMessage(Handler.Java:99)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.os.Looper.loop(Looper.Java:137)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.ActivityThread.main(ActivityThread.Java:5039)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Java.lang.reflect.Method.invokeNative(Native Method)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Java.lang.reflect.Method.invoke(Method.Java:511)
02-01 16:31:13.684: E/AndroidRuntime(9739): at com.Android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.Java:793)
02-01 16:31:13.684: E/AndroidRuntime(9739): at com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:560)
02-01 16:31:13.684: E/AndroidRuntime(9739): at dalvik.system.NativeStart.main(Native Method)
02-01 16:31:13.684: E/AndroidRuntime(9739): Caused by: Java.lang.NullPointerException
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.Java:528)
02-01 16:31:13.684: E/AndroidRuntime(9739): at io.openkit.OKLoginActivity.onRestoreInstanceState(OKLoginActivity.Java:62)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.Activity.performRestoreInstanceState(Activity.Java:910)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.Java:1131)
02-01 16:31:13.684: E/AndroidRuntime(9739): at Android.app.ActivityThread.performLaunchActivity(ActivityThread.Java:2158)
DialogFragment
内で、値true
で Fragment.setRetainInstance(boolean)
を呼び出します。フラグメントを手動で保存する必要はありません。フレームワークはすでにこれらすべてを処理しています。これを呼び出すと、ローテーションでフラグメントが破壊されるのを防ぐことができ、ネットワーク要求は影響を受けません。
互換性ライブラリでの bug のため、回転時にダイアログが消えないようにするには、このコードを追加する必要があります。
@Override
public void onDestroyView() {
Dialog dialog = getDialog();
// handles https://code.google.com/p/Android/issues/detail?id=17423
if (dialog != null && getRetainInstance()) {
dialog.setDismissMessage(null);
}
super.onDestroyView();
}
dialogFragment
を使用する場合と比較してalertDialogBuilder
を使用する利点の1つは、dialogfragmentがユーザーの介入なしに回転時に自動的に再作成できるためです。
ただし、dialogfragmentがそれ自体を再作成しない場合、onSaveInstanceState
を上書きしても、super
を呼び出さなかった可能性があります。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState); // <-- must call this if you want to retain dialogFragment upon rotation
...
}
これは、antonytの答えからの修正を使用した便利な方法です。
public class RetainableDialogFragment extends DialogFragment {
public RetainableDialogFragment() {
setRetainInstance(true);
}
@Override
public void onDestroyView() {
Dialog dialog = getDialog();
// handles https://code.google.com/p/Android/issues/detail?id=17423
if (dialog != null && getRetainInstance()) {
dialog.setDismissMessage(null);
}
super.onDestroyView();
}
}
DialogFragment
でこのクラスを拡張すると、すべてが正常になります。これは、プロジェクトに複数のDialogFragments
があり、すべてこの修正が必要な場合に特に便利です。