片段内部类应该是静态的

我有一个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。

非静态的内部类不包含对其父类的引用。 使片段内部类非静态的问题是,你总是持有对活动的引用。 垃圾收集器无法收集您的活动 。 所以,如果方向改变,你可以“泄漏” 活动 。 因为片段可能仍然存在并被插入到新的活动中

编辑:

由于有些人问我一些例子,我开始写一个,而这样做时,我发现使用非静态片段时更多的问题:

  • 它们不能在xml文件中使用,因为它们没有空的构造函数(它们可以有一个空构造函数,但是通常通过执行myActivityInstance.new Fragment()来实例化非静态嵌套类,这与仅调用一个空的构造函数是不同的)
  • 它们不能被重用 – 因为FragmentManager有时也会调用这个空构造函数。 如果你在一些事务中添加了Fragment

所以为了使我的例子工作,我不得不添加

 wrongFragment.setRetainInstance(true); 

行,以防止方向变化的应用程序崩溃。

如果你执行这个代码,你将有一个文本浏览和2个button的活动 – button增加一些计数器。 碎片显示他们认为他们的活动有方向。 在开始一切工作正常。 但是在改变屏幕方向之后,只有第一个片段正确地工作 – 第二个片段仍然在以前的活动中调用东西。

我的活动类:

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

请注意,如果你想在不同的活动中使用你的Fragment ,你可能不应该在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%的情况。
从我的angular度来看,我不希望我的DialogFragment(你的NetworkConnectionError)是静态的,因为我想能够从它调用包含类(Activity)的variables或方法。
所以它不会是静态的。 但是我不想产生内存泄漏。
那么解决scheme是什么?
简单的说,当你进入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"); } 

这样你就避免了内存泄漏(因为它是坏的),你可以确保你没有一个不能访问你的活动的字段和方法的静态片段。 从我的angular度来看,这是处理这个问题的好方法。

如果你在android工作室开发它,那么没有问题,如果你不把它作为static.The项目将运行没有任何错误,在apk的生成时间,你会得到错误:这个片段的内部类应该是静态的[ValidFragment]

这就是lint错误,你可能用gradle构build,以禁止错误中止,添加:

 lintOptions { abortOnError false } 

build立。 `

如果你想访问的外部类(活动)的成员,仍然不想让成员静态在Activity(因为片段应该是公共静态),你可以做覆盖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”)