我有一个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。
答案 0 :(得分:93)
非静态内部类确实包含对其父类的引用。使Fragment内部类非静态的问题是您始终保持对 Activity 的引用。 GarbageCollector 无法收集您的活动。因此,如果方向发生变化,您可以“泄漏”活动。因为 Fragment 可能仍然存在并插入到新的 Activity 中。
修改强>
由于有些人问我一些例子,我开始写一个,而这样做时我发现使用非静态片段时会遇到更多问题:
myActivityInstance.new Fragment()
来实例化非静态嵌套类,这与仅调用一个空是不同的构造函数)FragmentManager
有时也会调用这个空构造函数。如果您在某些交易中添加了 Fragment 。 因此,为了使我的示例工作,我必须添加
wrongFragment.setRetainInstance(true);
排除不会导致应用在方向更改时崩溃。
如果您执行此代码,您将有一个带有一些textview和2个按钮的活动 - 按钮会增加一些计数器。碎片显示了他们认为自己活动的方向。一开始一切正常。但是在更改屏幕方向后,只有第一个片段正确地工作 - 第二个片段仍在调用其旧活动中的内容。
我的活动课程:
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>
答案 1 :(得分:17)
我不会谈论内部碎片,更具体地说是关于活动中定义的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]静态片段无法访问你的活动的字段和方法。 从我的角度来看,这是处理这个问题的好方法。
答案 2 :(得分:5)
如果你在android studio中开发它,那么没有问题,如果你不把它作为静态。项目将运行没有任何错误,在生成apk时你将得到错误:这个片段内部类应该是静态的[ValidFragment ]
这就是lint错误,你可能正在使用gradle构建,要禁用错误中止,请添加:
lintOptions {
abortOnError false
}
to build.gradle。`
答案 3 :(得分:4)
如果要访问外部类(Activity)的成员并且仍然不想在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
...
}
答案 4 :(得分:-2)
在内部类
之前添加注释@SuppressLint( “validFragment”)