La classe interne de fragment doit être statique

J’ai une classe FragmentActivity avec une classe interne qui doit afficher Dialog . Mais je suis obligé de le rendre static . Eclipse me propose de supprimer les erreurs avec @SuppressLint("ValidFragment") . Est-ce mauvais style si je le fais et quelles sont les conséquences possibles?

 public class CarActivity extends FragmentActivity { //Code @SuppressLint("ValidFragment") public class NetworkConnectionError extends DialogFragment { private Ssortingng message; private AsyncTask task; private Ssortingng taskMessage; @Override public void setArguments(Bundle args) { super.setArguments(args); message = args.getSsortingng("message"); } public void setTask(CarActivity.CarInfo task, Ssortingng msg) { this.task = task; this.taskMessage = msg; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { // Use the Builder class for convenient dialog construction AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage(message).setPositiveButton("Go back", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { Intent i = new Intent(getActivity().getBaseContext(), MainScreen.class); startActivity(i); } }); builder.setNegativeButton("Retry", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { startDownload(); } }); // Create the AlertDialog object and return it return builder.create(); } } 

startDownload() démarre Asynctask.

Les classes internes non statiques contiennent une référence à leurs classes parentes. Le problème avec la création d’une classe interne de fragment non statique est que vous avez toujours une référence à l’ activité . Le GarbageCollector ne peut pas collecter votre activité . Vous pouvez donc “fuir” l’ activité si, par exemple, l’orientation change. Parce que le fragment peut encore vivre et est inséré dans une nouvelle activité .

MODIFIER:

Comme certaines personnes me demandaient un exemple, j’ai commencé à en écrire un, mais en même temps, j’ai trouvé d’autres problèmes lors de l’utilisation de fragments non statiques:

  • Ils ne peuvent pas être utilisés dans un fichier XML car ils ne possèdent pas de constructeur vide (ils peuvent avoir un constructeur vide, mais vous instanciez généralement des classes nestedes non myActivityInstance.new Fragment() en myActivityInstance.new Fragment() , ce qui diffère de l’appel à un constructeur vide)
  • Ils ne peuvent pas être réutilisés du tout – puisque FragmentManager appelle parfois ce constructeur vide. Si vous avez ajouté le fragment dans une transaction.

Donc, pour que mon exemple fonctionne, je devais append le

 wrongFragment.setRetainInstance(true); 

Ligne pour ne pas faire tomber l’application sur le changement d’orientation.

Si vous exécutez ce code, vous aurez une activité avec des vues de texte et 2 boutons – les boutons augmentent certains compteurs. Et les fragments montrent l’orientation qu’ils pensent avoir de leur activité. Au début, tout fonctionne correctement. Mais après avoir modifié l’orientation de l’écran, seul le premier Fragment fonctionne correctement – le second appelle toujours des choses à son ancienne activité.

Mon cours d’activité:

 package com.example.fragmenttest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.Fragment; import android.app.FragmentTransaction; import android.content.res.Configuration; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class WrongFragmentUsageActivity extends Activity { private Ssortingng mActivityOrientation=""; private int mButtonClicks=0; private TextView mClickTextView; private static final Ssortingng WRONG_FRAGMENT_TAG = "WrongFragment" ; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); int orientation = getResources().getConfiguration().orientation; if (orientation == Configuration.ORIENTATION_LANDSCAPE) { mActivityOrientation = "Landscape"; } else if (orientation == Configuration.ORIENTATION_PORTRAIT) { mActivityOrientation = "Portrait"; } setContentView(R.layout.activity_wrong_fragement_usage); mClickTextView = (TextView) findViewById(R.id.clicksText); updateClickTextView(); TextView orientationtextView = (TextView) findViewById(R.id.orientationText); orientationtextView.setText("Activity orientation is: " + mActivityOrientation); Fragment wrongFragment = (WrongFragment) getFragmentManager().findFragmentByTag(WRONG_FRAGMENT_TAG); if (wrongFragment == null) { wrongFragment = new WrongFragment(); FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.add(R.id.mainView, wrongFragment, WRONG_FRAGMENT_TAG); ft.commit(); wrongFragment.setRetainInstance(true); // <-- this is important - otherwise the fragment manager will crash when readding the fragment } } private void updateClickTextView() { mClickTextView.setText("The buttons have been pressed " + mButtonClicks + " times"); } private String getActivityOrientationString() { return mActivityOrientation; } @SuppressLint("ValidFragment") public class WrongFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { LinearLayout result = new LinearLayout(WrongFragmentUsageActivity.this); result.setOrientation(LinearLayout.VERTICAL); Button b = new Button(WrongFragmentUsageActivity.this); b.setText("WrongFragmentButton"); result.addView(b); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { buttonPressed(); } }); TextView orientationText = new TextView(WrongFragmentUsageActivity.this); orientationText.setText("WrongFragment Activities Orientation: " + getActivityOrientationString()); result.addView(orientationText); return result; } } public static class CorrectFragment extends Fragment { private WrongFragmentUsageActivity mActivity; @Override public void onAttach(Activity activity) { if (activity instanceof WrongFragmentUsageActivity) { mActivity = (WrongFragmentUsageActivity) activity; } super.onAttach(activity); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { LinearLayout result = new LinearLayout(mActivity); result.setOrientation(LinearLayout.VERTICAL); Button b = new Button(mActivity); b.setText("CorrectFragmentButton"); result.addView(b); b.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { mActivity.buttonPressed(); } }); TextView orientationText = new TextView(mActivity); orientationText.setText("CorrectFragment Activities Orientation: " + mActivity.getActivityOrientationString()); result.addView(orientationText); return result; } } public void buttonPressed() { mButtonClicks++; updateClickTextView(); } } 

Notez que vous ne devriez probablement pas lancer l'activité dans onAttach si vous souhaitez utiliser votre fragment dans différentes activités - mais pour cela, il fonctionne dans l'exemple.

Le fichier activity_wrong_fragement_usage.xml:

      

Je ne parlerai pas de fragments internes, mais plus particulièrement d’un DialogFragment défini dans une activité, car 99% de la question est posée.
De mon sharepoint vue, je ne veux pas que mon DialogFragment (votre NetworkConnectionError) soit statique parce que je veux pouvoir appeler des variables ou des méthodes de ma classe (Activity) contenant.
Ce ne sera pas statique, mais je ne veux pas non plus générer de memoryLeaks.
Quelle est la solution?
Simple. Lorsque vous allez dans onStop, assurez-vous de tuer votre DialogFragment. C’est aussi simple que ça. Le code ressemble à quelque chose comme ça:

 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"); } 

De cette façon, vous évitez les memory leaks (car c’est mauvais) et vous vous assurez de ne pas avoir un fragment statique [explétif] qui ne peut pas accéder aux champs et méthodes de votre activité. C’est le bon moyen de gérer ce problème, de mon sharepoint vue.

Si vous le développez dans Android Studio alors pas de problème si vous ne le donnez pas comme statique.Le projet s’exécutera sans aucune erreur et au moment de la génération de l’apk vous obtiendrez Erreur: Cette classe interne de fragment devrait être statique [ValidFragment]

C’est peut-être une erreur de peluches, vous construisez probablement avec gradle, pour désactiver l’abandon des erreurs, ajoutez:

 lintOptions { abortOnError false } 

pour build.gradle. `

Si vous voulez accéder aux membres de la classe externe (Activity) et que vous ne voulez toujours pas rendre les membres statiques dans Activity (puisque fragment doit être statique), vous pouvez effectuer le remplacement sur ActiveCreated

 public static class MyFragment extends ListFragment { private OuterActivityName activity; // outer Activity @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); activity = (OuterActivityName) getActivity(); ... activity.member // accessing the members of activity ... } 

append une annotation avant la classe interne

@SuppressLint (“validFragment”)