2012-05-10 16 views
18

Ich mache einige Hintergrundarbeit und zeigen ein DialogFragment, während ich das tue. Sobald meine Arbeit abgeschlossen ist und der entsprechende Rückruf aufgerufen wird, verlasse ich den Dialog. Wenn ich das tue, erhalte ich einen Absturz durch ein NPE verursacht in der Android-Quelle, hier:DialogFragment.dism Absturz mit NullPointerException

void dismissInternal(boolean allowStateLoss) { 
     if (mDialog != null) { 
      mDialog.dismiss(); 
      mDialog = null; 
     } 
     mRemoved = true; 
     if (mBackStackId >= 0) { 
      getFragmentManager().popBackStack(mBackStackId, 
        FragmentManager.POP_BACK_STACK_INCLUSIVE); 
      mBackStackId = -1; 
     } else { 
      FragmentTransaction ft = getFragmentManager().beginTransaction(); 
      ft.remove(this); 
      if (allowStateLoss) { 
       ft.commitAllowingStateLoss(); 
      } else { 
       ft.commit(); 
      } 
     } 
    } 

speziell an der Linie: FragmentTransaction ft = getFragmentManager().beginTransaction();

+1

Hat das Fragment/die Aktivität aus dem Sie den Dialog in den Hintergrund treten, d. h. seine 'onPause' Methode wird aufgerufen? In diesem Fall erwarte ich Probleme und tendiere dazu, solche Probleme zu umgehen, indem ich sicherstelle, dass ich keine Dialoge verwarne, bis "onResume" aufgerufen wird (durch Implementierung des pausierten Handler-Ansatzes [hier] (http://stackoverflow.com)/questions/7992496/how-to-handle-asynctask-onpostexecute-wenn-pausiert-zu-vermeiden-illegalstateexception) oder [hier] (http://stackoverflow.com/questions/8040280/how-to-handle-handler -messages-when-activity-fragment-is-paused) – PJL

+0

@PJL interessanter Punkt. Ich sollte onPause anmelden. Macht Sinn, dass onPause würde aufgerufen werden. Ich gebe Ihrem Ansatz eine Chance. – LuxuryMode

Antwort

5

Meine Wette wäre, dass der Code, den Sie Thread aus dem Hintergrund geschrieben ... Sie dürfen die Benutzeroberfläche nicht von einem anderen Ort als dem UI-Thread aktualisieren.

können Sie verwenden OnPostExecute() oder runOnUiThread() Ihr Ziel zu erreichen (wenn meine Vermutung recht ist, was geschieht)

+0

Unterstützung Sie, mein Herr, denke ich auch selbe ... –

+2

Ich dachte das. Allerdings bekomme ich das gleiche Ergebnis, auch wenn ich getActivity() aufrufen. runOnUiThread obwohl ich nicht wirklich versucht habe, eine AsyncTask – LuxuryMode

+0

Ich habe das gleiche Problem und ich rief bereits ' ablehnen() 'aus dem UI-Thread. –

14

Dies auch dann auftreten können, wenn Sie entlassen() aufrufen, bevor Sie Show() aufgerufen haben wie Sogger sagte.

Nachdem das Dialog-Objekt erstellt wurde, aber bevor der Dialog nicht angezeigt wird, kann if (mDialog! = Null) übergeben werden und NullPointerException wird ausgeführt.

Wenn Sie überprüfen, ob mDialog null ist oder nicht,

if (mDialog != null) { 
    mDialog.dismiss(); 
    mDialog = null; 
} 

weitere Bedingungen hinzufügen, wie unten,

if ((mDialog != null) && mDialog.isAdded() && mDialog.isResumed()) { 
    mDialog.dismiss(); 
    mDialog = null; 
} 

Ich denke, dass mDialog.isAdded() Bedingung genug sein könnte ...

+0

Die Überprüfung von 'isAdded()' und 'isResumed()' ist redundant - wenn ein Fragment wieder aufgenommen wird, wird es ebenfalls hinzugefügt. Siehe [fragment lifecycle docs] (https://developer.android.com/guide/components/fragments.html#Creating). – yuval

1

Überprüfen, ob es sichtbar ist, bevor Dimissing diese Nullzeigerausnahme vermeiden könnte

if (mDialog != null && mDialog.isVisible) { 
     mDialog.dismiss(); 
     mDialog = null; 
    } 
10

Die einfachste Lösung ist, "getFragmentManager()" auf "null" zu überprüfen, bevor die Methode "dispatch()" aufgerufen wird. Sie können auch „DialogFragment“ Klasse und überschreiben Methode „entlassen()“ erweitern es dort zu überprüfen:

@Override 
public void dismiss() 
{ 
    if (getFragmentManager() != null) super.dismiss(); 
} 
6

Ich weiß, diese Nachricht ist alt, aber ich lief in einem ähnlichen Fall, die ich brauchte, ohne Refactoring solvew oder ändern viel Code. Hoffe, dass es für jemanden

package com.example.playback; 

import android.os.Bundle; 
import android.support.v4.app.DialogFragment; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 

public class SaferDialogFragment extends DialogFragment { 

    private boolean allowStateLoss = false; 
    private boolean shouldDismiss = false; 

    public SaferDialogFragment() { 
    } 

    @Override 
    public void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setRetainInstance(true); 
    } 

    @Override 
    public void onStart() { 
     super.onStart(); 
     //check if we should dismiss the dialog after rotation 
     if (shouldDismiss) { 
      if (allowStateLoss) 
       dismissAllowingStateLoss(); 
      else 
       dismiss(); 
     } 
    } 

    @Override 
    public void dismiss() { 
     if (getActivity() != null) { // it's "safer" to dismiss 
      shouldDismiss = false; 
      super.dismiss(); 
     } else { 
      shouldDismiss = true; 
      allowStateLoss = false; 
     } 
    } 

    @Override 
    public void dismissAllowingStateLoss() { 
     if (getActivity() != null) { // it's "safer" to dismiss 
      shouldDismiss = false; 
      super.dismissAllowingStateLoss(); 
     } else 
      allowStateLoss = shouldDismiss = true; 
    } 

    //keeping dialog after rotation 
    @Override 
    public void onDestroyView() { 
     if (getDialog() != null && getRetainInstance()) 
      getDialog().setDismissMessage(null); 
     super.onDestroyView(); 
    } 



    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) { 
     /** omitted code **/ 
     return super.onCreateView(inflater, container, savedInstanceState); 
    } 
} 
1

Der Rückruf nützlich ist, die auf der Aktivität wahrscheinlich aufgerufen wird, die ist oder sollte (nach Orientierung ändern) zerstört werden, auch der Fortschrittsdialog könnte mit der gleichen Aktivität instanziiert wurde. Dies könnte die NPE verursachen. Rückrufe auf Aktivitäten sollten nicht von Hintergrundaufgaben aufgerufen werden, um diese Art von Problemen zu vermeiden. Entkoppeln Sie die Hintergrundaufgabe von der Aktivität, z. B. mit otto, oder verhindern Sie, dass die Hintergrundaufgabe die (zu vernichtende) Aktivität aufruft.

Dies ist ein Code von mir:

statische innere Klasse der Tätigkeit:

public static class ProgressDialogFragment extends DialogFragment { 
    ProgressDialog dialog; 

    public ProgressDialogFragment() { 
    } 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     dialog = new ProgressDialog(getActivity(), getTheme()); 
     dialog.setTitle(getString(R.string.please_wait)); 
     dialog.setMessage(getString(R.string.uploading_picture)); 
     dialog.setIndeterminate(true); 
     dialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); 
     return dialog; 
    } 

} 

Otto Abonnement in Tätigkeit:

@Subscribe 
public void onUploadEvent(UploadAvatarEvent uploadAvatarEvent) { 
    switch (uploadAvatarEvent.state) { 
     case UploadAvatarEvent.STATE_UPLOADING: 
      if (!mProgressDialog.isAdded()) { 
       mProgressDialog.show(getFragmentManager(), TAG_PROGRESS_DIALOG); 
      } 
      break; 
     case UploadAvatarEvent.STATE_UPLOAD_SUCCES: 
      mProgressDialog.dismiss(); 
      break; 
     case UploadAvatarEvent.STATE_UPLOAD_ERROR: 
      mProgressDialog.dismiss(); 
      break; 
    } 
} 

onCreate() in der Aktivität:

 mProgressDialog = (ProgressDialogFragment) getFragmentManager().findFragmentByTag(TAG_PROGRESS_DIALOG); 
    if (mProgressDialog == null) { 
     mProgressDialog = new ProgressDialogFragment(); 
    } 
Verwandte Themen