Внутренний класс фрагмента должен быть статическим

У меня есть класс FragmentActivity с внутренним классом, который должен отображать Dialog . Но я должен сделать его static . Eclipse предлагает мне подавить ошибку с помощью @SuppressLint("ValidFragment") . Это плохой стиль, если я это делаю и каковы возможные последствия?

 public class CarActivity extends FragmentActivity { //Code @SuppressLint("ValidFragment") public class NetworkConnectionError extends DialogFragment { private String message; private AsyncTask task; private String taskMessage; @Override public void setArguments(Bundle args) { super.setArguments(args); message = args.getString("message"); } public void setTask(CarActivity.CarInfo task, String 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() запускает Asynctask.

Solutions Collecting From Web of "Внутренний класс фрагмента должен быть статическим"

Нестационарные внутренние классы содержат ссылку на родительские классы. Проблема с созданием внутреннего класса Fragment non-static заключается в том, что вы всегда держите ссылку на Activity . GarbageCollector не может собирать вашу деятельность . Таким образом, вы можете «пропустить» действие, если, например, изменения ориентации. Поскольку фрагмент все еще может жить и вставляется в новое действие.

РЕДАКТИРОВАТЬ:

Поскольку некоторые люди спрашивали меня о каком-то примере, я начал писать один, делая это, я нашел еще несколько проблем при использовании нестатических фрагментов:

  • Они не могут использоваться в XML-файле, поскольку у них нет пустого конструктора (у них может быть пустой конструктор, но вы обычно создаете нестатические вложенные классы, выполняя myActivityInstance.new Fragment() и это отличается от вызова только пустого конструктора)
  • Они не могут быть повторно использованы вообще, поскольку FragmentManager иногда вызывает этот пустой конструктор. Если вы добавили фрагмент в какую-либо транзакцию.

Поэтому, чтобы сделать мой пример работы, мне пришлось добавить

 wrongFragment.setRetainInstance(true); 

Линия, чтобы не привести к сбою приложения при изменении ориентации.

Если вы выполните этот код, у вас будет активность с некоторыми текстовыми изображениями и двумя кнопками – кнопки увеличивают некоторый счетчик. И Фрагменты показывают, какую ориентацию они считают своей деятельностью. В начале все работает правильно. Но после изменения ориентации экрана только первый фрагмент работает корректно – второй по-прежнему вызывает материал при его старой активности.

Мой класс активности:

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

Обратите внимание, что вы, вероятно, не должны onAttach активность в onAttach если хотите использовать свой фрагмент в разных действиях, но для этого он работает для примера.

Activity_wrong_fragement_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> 

Я не буду говорить о внутреннем фрагменте, но более конкретно о DialogFragment, определенном в рамках действия, потому что это 99% случае для этого вопроса.
С моей точки зрения, я не хочу, чтобы мой DialogFragment (ваш NetworkConnectionError) был статичным, потому что я хочу иметь возможность вызывать переменные или методы моего содержащего класса (Activity).
Так что это не будет статично. Но я не хочу генерировать memoryLeaks.
Так в чем же решение?
Просто, когда вы заходите в onStop, забудьте, что вы убили свой DialogFragment, это так просто. Таким образом, код выглядит примерно так:

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

И таким образом вы избегаете утечек памяти (потому что это плохо), и вы гарантируете, что у вас нет [expletive] статического фрагмента, который не может получить доступ к полям и методам вашей деятельности. Это хороший способ справиться с этой проблемой, с моей точки зрения.

Если вы разрабатываете его в андроид-студии, тогда нет проблем, если вы не дадите его как static.The проект будет работать без каких-либо ошибок, и во время генерации apk вы получите ошибку: внутренний фрагмент этого фрагмента должен быть статическим [ValidFragment]

Thats lint error, вы, вероятно, строите с помощью gradle, чтобы отключить прерывание ошибок, добавьте:

 lintOptions { abortOnError false } 

Для сборки. `

Если вы хотите получить доступ к элементам внешнего класса (Activity) и по-прежнему не хотите ставить элементы static в Activity (поскольку фрагмент должен быть public static), вы можете сделать переопределение onActivityCreated

 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 ... } 

Добавить аннотацию перед внутренним классом

@SuppressLint ( "validFragment")