Retain Dialog verschwindet beim drehen


#1

Ich haben ein DialogFragment, dass mit einem AsyncTask verbunden ist, weswegen ich retain für das Fragment auf TRUE setze.

DialogFragment.java ```public class Dialog extends DialogFragment{
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);// Flag setzen
}

@Override
public Dialog onCreateDialog(final Bundle savedInstanceState) {
  final LayoutInflater inflater = getActivity().getLayoutInflater();
  final View dialogContent = inflater.inflate(R.layout.dialog, null);
  final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
  builder.setPositiveButton("Close", null);

  //TODO: hier task starten
  
  return builder.create();
}

}


MyActivity.java ```public class MyActivity extends Activity {

public boolean onOptionsItemSelected(final MenuItem item) {
   // Show dialog on any selection (for testing)
   final DialogFragment dialog = new Dialog();
   dialog.show(getFragmentManager(), "dialog");
   return true;
}

protected void onRestoreInstanceState(final Bundle state) {
  super.onRestoreInstanceState(state);  

  final DialogFragment dialog = (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
  if (dialog != null) { // WTF? dialog ist IMMER NULL
    dialog.show(getFragmentManager(), "dialog");//re-show dialog
  }
}```

Ich zeige den Dialog an, und wenn sich das Tablet dreht, zeige ich ihn erneut an... theoretisch. Praktisch scheint der FragmentManager aber gar nichts von meinem DialogFragment zu wissen und gibt jedesmal null zurück, womit der Dialog natürlich nicht erscheint. 

Irgend jemand eine Idee? :ka:

#2

Aha… nachdem ich heute noch ein wenig Google bemüht habe, bin ich zu folgender Erkenntnis gekommen:

Ich bin diesem Bug zum Opfer gefallen. Nachdem ich diesem Hinweis gefolgt bin, komm ich nun zu folgendem Workaround, der auch funktioniert:

Dialog.java```public class Dialog extends DialogFragment {
// Code as above

public void onDestroyView() {
if ((getDialog() != null) && getRetainInstance()) {
getDialog().setDismissMessage(null);
}
super.onDestroyView();
}
}```

Warum genau das jetzt funktioniert erschließt sich mir noch nicht gänzlich, aber es geht. Hoffen wir mal, dass es so bleibt und sich auch auf dem Produktivgerät so verhält.


#3

Find’ ich gut wenn man so gewissenhaft seine Erkenntnisse dokumentiert!


#4

So… jetzt funktioniert es, aber leider wieder nur mit einem Workaround, da der Bugfix ein Problem mit sich bringt: Öffnet man den Dialog erneut und dreht das Tablet, so kommt eine IllegalStateException, die ich auch nach einigen frustreichen Versuchen nicht wegbekommen habe. Daher hier jetzt meine aktuelle Lösung, die zwar nicht so schön ist, zumindest aber funktioniert:

activity.xml
[XML]…


[/XML]

MyActivity.java

protected void onCreate(final Bundle savedInstanceState){
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity);
  
  this.fragment = (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
  
  if (this.fragment == null) {
    this.fragment = new DialogFragment();
    getFragmentManager().beginTransaction().add(R.id.fragment_target, this.fragment, "dialog").commit();
  }
}```

DialogFragment.java
```public class DataUpdateFragment extends Fragment {
   private AsyncTask<?, ?, ?> task;
   private AlertDialog dialog;
  
    @Override
    public void onCreate(final Bundle savedState) {
        super.onCreate(savedState);
        setRetainInstance(true);
    }
    
    @Override
    public void onAttach(final Activity activity) {
        super.onAttach(activity);
        if (this.task != null) {
            showDialog();
        }
    }
    
    @Override
    public void onDetach() {
        if (this.dialog != null) {this.dialog.dismiss();}
        super.onDetach();
    }

     public void showDialog() {
        final LayoutInflater inflater = getActivity().getLayoutInflater();
        final View dialogContent = inflater.inflate(R.layout.dialog, null);
        final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setPositiveButton("Close", null);
        this.dialog = builder.create();
          
        if (this.task == null) {this.task = new MyTask(this).execute();}
        this.dialog.show();
     }

     protected void closeDialog() {
        this.dialog.dismiss();
        this.task = null;
        this.dialog = null;
    }
    
   private static final class UpdateTask extends AsyncTask<...>{
        DialogFragment fragment;

         public UpdateTask(final DialogFragment fragment) {
            this.fragment = fragment;
         }
         // implement doInBackground()
         protected void onPostExecute(...) {this.fragment.closeDialog(); }
         protected void onCancelled() {this.fragment.closeDialog(); }
}```

Damit melde ich das Fragment als (unsichtbare) Komponente an, und zeige den Dialog immer neu, wenn das Tablet gedreht wird. Unschön, aber anders scheint es erstmal nicht zu gehen. Wenn jemandem da was eleganteres einfällt, dann darf er sich hier gerne melden ;)