片段内部类应该是静态的

use*_*737 69 android fragment android-dialogfragment

我有一个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();
    }
  }
Run Code Online (Sandbox Code Playgroud)

startDownload() 启动Asynctask.

Sim*_*yer 93

非静态内部类确实包含对其父类的引用.使Fragment内部类非静态的问题是您始终保持对Activity的引用.该GarbageCollector无法收集您的活动.因此,如果方向发生变化,您可以"泄漏" 活动.因为片段可能仍然存在并插入到新的Activity中.

编辑:

因为有些人问我一些例子我开始写一个,而这样做时我发现使用非静态片段时会遇到更多问题:

  • 它们不能在xml文件中使用,因为它们没有空构造函数(它们可以有一个空的构造函数,但是你通常通过实例实例化非静态嵌套类myActivityInstance.new Fragment(),这与仅调用空构造函数不同)
  • 它们根本无法重用 - 因为FragmentManager有时也会调用这个空构造函数.如果您在某些交易中添加了片段.

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

wrongFragment.setRetainInstance(true);
Run Code Online (Sandbox Code Playgroud)

排除方向更改时不使应用程序崩溃.

如果您执行此代码,您将有一个带有一些textview和2个按钮的活动 - 按钮会增加一些计数器.碎片显示了他们认为自己活动的方向.一开始一切正常.但是在更改屏幕方向后,只有第一个片段正确地工作 - 第二个片段仍在调用其旧活动中的内容.

我的Activity类:

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

}
Run Code Online (Sandbox Code Playgroud)

请注意,onAttach如果要在不同的活动中使用Fragment,则可能不应该将活动强制转换 - 但是在这里它可以用于示例.

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>
Run Code Online (Sandbox Code Playgroud)

  • @Egis也许这可以让你对嵌套静态内部类有所了解:http://docs.oracle.com/javase/tutorial/java/javaOO/nested.html. (3认同)

Mat*_*2ee 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");
}
Run Code Online (Sandbox Code Playgroud)

这样你就可以避免内存泄漏(因为它很糟糕)并确保你没有一个无法访问你活动的字段和方法的[expletive]静态片段.从我的角度来看,这是处理这个问题的好方法.


cha*_*ddy 5

如果你在android studio中开发它然后没有问题,如果你不给它作为静态.项目将运行没有任何错误,并在生成apk时你将得到错误:这个片段内部类应该是静态的[ValidFragment]

这是lint错误,您可能正在使用gradle构建,要禁用中止错误,请添加:

lintOptions {
    abortOnError false
}
Run Code Online (Sandbox Code Playgroud)

build.gradle.`

  • 你是对的,它将通过建设过程,但这样使用它是正确的吗?因为有一个泄漏的内存问题,这就是android studio警告我们的原因. (8认同)

Dee*_*epu 5

如果要访问外部类(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
        ...
     }
Run Code Online (Sandbox Code Playgroud)