2013-03-14 15 views
9

Si tratta di un frammento di codice dal mio progetto che sto usando per imparare Android:Una classe DialogFragment interna può essere statica o no?

private void enableLocationSettings() { 
    Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); 
    startActivity(settingsIntent); 
} 

@SuppressLint("ValidFragment") 
public class EnableGpsDialogFragment extends DialogFragment { 

    @Override 
    public Dialog onCreateDialog(Bundle savedInstanceState) { 
     return new AlertDialog.Builder(getActivity()) 
      .setTitle("Tytuł") 
      .setMessage("wiadomosc") 
      .setPositiveButton("odpal", new DialogInterface.OnClickListener() { 

       @Override 
       public void onClick(DialogInterface dialog, int which) { 
        enableLocationSettings(); 
       } 

      }) 
      .create(); 
    } 
} 

Come potete vedere Devo aggiungere @SuppressLint a fare il mio lavoro, ma app sul guide questa annotazione non era necessario.

Cosa sto sbagliando?

qui sono i miei importazioni:

import android.annotation.SuppressLint; 
import android.app.Activity; 
import android.app.AlertDialog; 
import android.app.Dialog; 
import android.content.Context; 
import android.content.DialogInterface; 
import android.content.Intent; 
import android.location.LocationManager; 
import android.os.Bundle; 
import android.os.Handler; 
import android.os.Message; 
import android.provider.Settings; 
import android.support.v4.app.FragmentActivity; 
import android.support.v4.app.FragmentManager; 
import android.support.v4.app.DialogFragment; 
import android.view.Menu; 
import android.view.View; 
import android.widget.TextView; 
import android.widget.ToggleButton; 

risposta

20

L'esempio non ha tali annotazioni perché la classe è in un file. Ciò significa che è indipendente dell'attività che utilizza il frammento.

Nel tuo caso, il tuo frammento si trova all'interno di un'attività e non utilizza il modificatore statico. Ciò significa che è legato all'istanza Activity.

Avere il frammento dipendente dall'istanza di attività è una cattiva idea, entrambe queste classi hanno stili di vita complessi (soprattutto perché le attività vengono distrutte e ricreate abbastanza spesso) e dovrebbero essere indipendenti l'una dall'altra.

È necessario modificare il modificatore EnableGpsDialogFragmentstatic.

public static class EnableGpsDialogFragment extends DialogFragment { 

Un static classe non dipende l'istanza della classe che racchiude, quindi l'avviso andrà via.

Per ulteriori informazioni, consultare il tutorial Java su nested classes.

Modifica in risposta alla tua modifica: Ora che le classi non dipendono vicenda esempio, si dovrà uscire da un'istanza di YourActivity in modo da poter chiamare enabledLocationSettings() un modo è lanciando e funzionerà solo se EnableGpsDialogFragment è usato solo da YourActivity:

@Override 
public void onClick(DialogInterface dialog, int which) { 
    enableLocationSettings(); 
} 

a

@Override 
public void onClick(DialogInterface dialog, int which) { 
    ((YourActivity)getActivity()).enableLocationSettings(); 
} 

Se questo frammento sarà utilizzato da molteplici attività, si dovrebbe create an interface to be implemented by each Activity instead .

+0

Grazie, ma l'aggiunta di modificatore static per EnableGpsDialogFragment mi sta costringendo per aggiungere modificatore static per enableLocationSettings private void(). E se aggiungo appare un nuovo errore: Non può fare un riferimento statico al metodo startActivity non statico (Intent) dal tipo di attività – szpic

+0

ben @szpic che è perché il vostro ** ** di modifica alla tua domanda ora include una chiamata a ' enableLocationSettings(); ' –

+0

Per aggirare questo problema, è possibile utilizzare un riferimento debole a qualsiasi cosa sia necessaria dall'attività. Quindi, prima dell'uso, verificare che sia ancora valido. Questo dovrebbe essere impostato ogni volta che l'attività viene distrutta/creata per assicurarsi che sia valida. Per aggiungere informazioni sui frammenti statici: possono perdere memoria in questo contesto se non vengono creati staticamente, poiché è possibile che mantengano un riferimento e un'attività obsoleta che non è più accessibile e che dovrebbe essere stata raccolta. –

1

Non dovrebbe essere!

Dal mio punto di vista, non voglio che il mio DialogFragment (your NetworkConnectionError) sia statico perché voglio essere in grado di chiamare variabili o metodi della mia classe contenente (Activity) da esso.
Quindi non sarà statico. Ma non voglio nemmeno generare memoryLeaks.
Quindi qual è la soluzione?
Semplice, quando vai su onStop, assicurati di uccidere il tuo DialogFragment, è così semplice. Quindi il codice sembra qualcosa di simile:

public class CarActivity extends AppCompatActivity{ 

/** 
* The DialogFragment networkConnectionErrorDialog 
*/ 
private NetworkConnectionError networkConnectionErrorDialog ; 
//... your code ...// 
@Override 
protected void onStop() { 
    super.onStop(); 
    //invalidate the DialogFragment to avoid stupid memory leak 
    if (networkConnectionErrorDialog != null) { 
     if (networkConnectionErrorDialog .isVisible()) { 
      networkConnectionErrorDialog .dismiss(); 
     } 
     networkConnectionErrorDialog = null; 
    } 
} 
/** 
* The method called to display your dialogFragment 
*/ 
private void onDeleteCurrentCity(){ 
    FragmentManager fm = getSupportFragmentManager(); 
    networkConnectionErrorDialog =(DeleteAlert)fm.findFragmentByTag("networkError"); 
    if(networkConnectionErrorDialog ==null){ 
     networkConnectionErrorDialog =new DeleteAlert(); 
    } 
    networkConnectionErrorDialog .show(getSupportFragmentManager(), "networkError"); 
} 

E in questo modo si evita perdite di memoria (perché è male) e si assicura non si dispone di un frammento statica cazzo che non possono accedere ai campi e metodi della vostra attività. Questo è il modo migliore per gestire questo problema, dal mio punto di vista.

+0

DialogFragments * dovrebbe * essere classi statiche pubbliche con un costruttore no-op pubblico, semplice come quello. In caso contrario, il sistema non sarà in grado di ricreare il frammento se necessario in una situazione di memoria insufficiente, ecc. Inoltre, a partire dalla libreria di supporto v25, l'app si arresterà in modo anomalo se si tenta di mostrare un DialogFragment che non soddisfa i suddetti vincoli: 'java.lang.IllegalStateException: Fragment TestActivity $ TestDialogFrament deve essere una classe statica pubblica da ricreare correttamente dallo stato dell'istanza. – JHH

+0

oops, typo: no-op -> no-arg – JHH

+0

No. Non dovrebbero . Posso ricrearli da solo. –

3

Tutti DialogFragments dovrebbero essere pubblici e - se una classe interna - static. Dovrebbero anche avere un costruttore no-arg pubblico e affidarsi solo a setArguments() per il passaggio dei parametri.

caso di mancata ottemperanza a questo ha per qualche tempo ha prodotto un avvertimento Lint, che si potrebbe sopprimere se si voleva davvero, ma a partire dal Android v25 libreria di supporto e, sarà effettivamente ottenere un'eccezione se cercando di mostrare un DialogFragment che non è conforme a queste regole:

java.lang.IllegalStateException: Fragment TestActivity$TestDialogFrament must be a public static class to be properly recreated from instance state.

la ragione è, come detto, che il sistema operativo deve essere in grado di ricreare tutti i frammenti in caso qualcosa di simile a una situazione di poca memoria costringe a distruggere frammenti quando un'app viene messa in secondo piano. Quando l'applicazione è messo nuovamente in primo piano, i frammenti dovrebbero essere possibile ricreare dallo stato applicazione serializzato, e che non è possibile per classi interne non statici non da un'istanza della classe esterna che racchiude, e la ri -la creatività non viene eseguita da quel contesto.

Purtroppo questo rende le cose più complesse per le finestre di dialogo, dal momento che di solito è molto conveniente per creare solo un anonimo sottoclasse che sostituisce onCreateDialog. Tuttavia, una tale finestra di dialogo non sarebbe in grado di ricreare del tutto.

+0

Le situazioni a bassa memoria sono una vera minaccia dato che abbiamo almeno 2GB di RAM in smartphone economici ... :) Non ho mai capito perché Google voglia ricreare i miei frammenti di dialogo se dovessero distruggerli - Posso farlo da solo e già farlo (io uso v23 e non aggiornerò). –

+0

Beh, in parte perché così tante applicazioni si comportano male e sono sostanzialmente in esecuzione tutto il tempo, o sono costantemente riavviati a causa di frequenti intenti broadcast. Questo problema viene affrontato severamente in Android N e O. Se 50 app vogliono essere eseguite ininterrottamente, ci sarà un sacco di swapping in e out app dalla memoria. Inoltre, ricorda che su uno schermo 1080p, una singola bitmap è di 8 MB. E non sono abbastanza sicuro di capire perché vorresti ricreare lo stato te stesso completamente manualmente, ma tutti noi abbiamo i nostri dolcetti ... – JHH

Problemi correlati