Classes internes non statiques contiennent une référence à leurs classes parentes. Le problème qui se pose lorsqu'on rend non statique une classe interne de Fragment, c'est qu'on conserve toujours une référence à l'élément Activité . Le site Collecteur d'ordures ne peut pas collecter votre Activité . Vous pouvez donc "fuir" le Activité si par exemple l'orientation change. Parce que le Fragment pourrait encore vivre et être insérée dans une nouvelle Activité .
EDITAR:
Comme certaines personnes m'ont demandé un exemple, j'ai commencé à en écrire un. En faisant cela, j'ai découvert d'autres problèmes liés à l'utilisation de fragments non statiques :
- Elles ne peuvent pas être utilisées dans un fichier xml car elles n'ont pas de constructeur vide (elles peuvent avoir un constructeur vide, mais vous instanciez généralement les classes imbriquées non statiques en faisant
myActivityInstance.new Fragment()
et c'est différent d'appeler seulement un constructeur vide)
- Ils ne peuvent pas du tout être réutilisés - puisque l
FragmentManager
appelle aussi parfois ce constructeur vide. Si vous avez ajouté le Fragment dans certaines Transactions.
Pour que mon exemple fonctionne, j'ai donc dû ajouter l'option
wrongFragment.setRetainInstance(true);
Ligne pour ne pas faire planter l'application lors d'un changement d'orientation.
Si vous exécutez ce code, vous aurez une activité avec des vues de texte et 2 boutons - les boutons augmentent un compteur. Et les Fragments montrent l'orientation qu'ils pensent que leur activité a. Au début, tout fonctionne correctement. Mais après avoir changé l'orientation de l'écran, seul le premier fragment fonctionne correctement - le second appelle toujours des choses dans son ancienne activité.
Ma classe 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 String mActivityOrientation="";
private int mButtonClicks=0;
private TextView mClickTextView;
private static final String 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é en onAttach
si vous voulez utiliser votre Fragment dans des activités différentes - mais ici, cela fonctionne pour l'exemple.
L'activité_mauvaise_fragmentation_usage.xml :
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".WrongFragmentUsageActivity"
android:id="@+id/mainView">
<TextView
android:id="@+id/orientationText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
<TextView
android:id="@+id/clicksText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
<fragment class="com.example.fragmenttest.WrongFragmentUsageActivity$CorrectFragment"
android:id="@+id/correctfragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>