Fragmento de la clase interna debe ser estático

Tengo un FragmentActivity clase con clase interna que debe mostrar Dialog. Pero estoy obligado a hacer static. Eclipse me ofrece para suprimir el error con @SuppressLint("ValidFragment"). Es de un estilo malo si lo hago y cuáles son las posibles consecuencias?

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() comienza Asynctask.

  • puede mostrar el código
  • En general, las malas prácticas ignorar la pelusa. Es una excelente herramienta. Trate de la publicación de su código, para obtener una respuesta sobre cómo usted puede hacer un mejor trabajo.
  • has comprobado esto code.google.com/p/android/issues/detail?id=41800 con el fin de saber que es ValidFragment acerca de? La pelusa dice que: «Cada fragmento debe tener un constructor vacío, por lo que puede ser instanciado’
  • Yo hice. Pero no veo por qué no debo omitir este mensaje de advertencia. ¿Cuáles pueden ser las posibles consecuencias?
InformationsquelleAutor user2176737 | 2013-03-22

5 Kommentare

  1. 93

    No estática de las clases internas hacer contener una referencia a su padre clases. El problema con la fabricación de un Fragmento de una clase interna estática no es que se mantenga siempre una referencia a la Actividad. El GarbageCollector no puede recoger su Actividad. Así que usted puede ‘fuga’ de la Actividad si, por ejemplo, los cambios de orientación. Debido a que el Fragmento posible que todavía viven y se inserta en una nueva Actividad.

    EDICIÓN:

    Ya que algunas personas me pidió un poco de ejemplo empecé a escribir uno, mientras que haciendo esto he encontrado más problemas cuando se utiliza no estática Fragmentos:

    • No se pueden utilizar en un archivo xml, ya que no tienen un constructor vacío (Que puede tener un constructor vacío, pero normalmente instanciar no estáticos clases anidadas haciendo myActivityInstance.new Fragment() y esto es diferente a sólo llamando a un constructor vacío)
    • Que no puede ser reutilizado en todo – desde el FragmentManager a veces llama a este constructor vacío también. Si has añadido el Fragmento en alguna Transacción.

    Por lo que para hacer mi ejemplo de trabajo he tenido que añadir el

    wrongFragment.setRetainInstance(true);

    Línea para no hacer que la aplicación se bloquee en cambio de orientación.

    Si ejecuta este código, se tendrá una actividad con algunos textviews y 2 botones: los botones de aumentar el contador. Y los Fragmentos muestran la orientación que ellos piensan que su actividad tiene. Al principio todo funciona correctamente. Pero después de cambiar la orientación de la pantalla sólo el primer Fragmento obras correcly – el segundo sigue llamando a las cosas por su antigua actividad.

    Mi clase de Actividad:

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

    Tenga en cuenta que usted probablemente no debería elenco de la actividad en onAttach si usted desea utilizar su Fragmento en diferentes Actividades, pero de aquí su trabajo para el ejemplo.

    La 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>
    • muy interesante, y probablemente muy útil respuesta. Estoy tratando con el mismo problema. ¿Te importaría dejar caer algún tipo de fuente de que su respuesta se basa en?
    • tal vez esto podría dar un poco de información acerca de la nested-estática de las clases internas: docs.oracle.com/javase/tutorial/java/javaOO/nested.html.
    • ¿Usted tiene un de referencia de esta respuesta?
    • Mike: he editado mi respuesta con algún ejemplo
    • Creo que el WrongFragment ejemplo es el mantenimiento de su referencia a la Actividad anterior instancia (WrongFragmentActivity.this utilizado en onCreateView()) debido al hecho de que WrongFragment.onCreateView() no es llamado de nuevo, la vista es nunca re-creado debido a setRetainInstance(true) y «mantener» el fragmento a través de findFragmentByTag(), como se opuso a la nueva instancia de CorrectFragment a través del fichero XML de diseño fragment elemento. Punto menor y un subproducto de la arquitectura de Android que usted describe, pero vale la comprensión.
    • onAttach está en desuso

  2. 17

    No voy a hablar de interior de los fragmentos, pero más específicamente acerca de un DialogFragment definido dentro de una actividad, porque el 99% de los casos para esta pregunta.

    Desde mi punto de vista, no quiero que mi DialogFragment (su NetworkConnectionError) para ser estático porque quiero ser capaz de llamar a las variables o métodos de mi clase que las contiene (Actividad) en ella.

    No será estática, pero no quiero generar memoryLeaks bien.

    ¿Cuál es la solución?

    Simple. Cuando usted va en onStop, asegúrese de matar a su DialogFragment. Es tan simple como eso.
    El código se parece a algo como esto:

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

    Y de esa manera evitar pérdidas de memoria (porque es malo) y asegurarse de que usted no tiene un [improperio] estática fragmento que no tiene acceso a la actividad de los campos y métodos.
    Esta es la buena manera de manejar el problema, desde mi punto de vista.

    • se ve bien, pero va a obtener errores en el tiempo de generación de apk como @hakri Reddy se mencionan a continuación
    • Sí, pero no es porque la pelusa no es lo suficientemente inteligente como para las que necesitamos «tan estúpido como él», memort de fugas se han ido utilizando esta técnica (CanaryLeak le mostrará que)… Por cierto, la primera vez que ejecute mi apk con pelusa para detectar otros errores que puedo haber hecho en mi código, de solucionar el problema yo creo que es necesario y, a continuación, ejecute con la abortOnError falso. Y en algún proyecto, me personalizar Pelusa en esta norma específica («Interior de la clase debe ser estático» gota a weakwarning)
    • ohh… pero en mi caso, real apk generación se realiza por diferentes equipo sentado en la oficina de estados unidos(estoy en la India, y yo sólo proporcionan git repositorio de código enlaces) porque no comparten la compañía de certificado de firma de archivos a nadie. Así que definitivamente no se va a escuchar mi razón y no va a cambiar su configuración 🙁
    • Sí, a veces no hay sólo limitaciones técnicas 🙂 y tenemos que aceptar que ellos 🙂
    • este enfoque me da un Fragment must be a public static class crash error
  3. 5

    Si u desarrollar en android studio, entonces no hay problema si usted no da como estática.El proyecto se ejecuta sin errores y en el tiempo de generación de apk Error :Este fragmento interior de la clase debe ser estática [ValidFragment]

    Eso es la pelusa de error, probablemente el edificio con gradle, para deshabilitar abortar en errores, agregar:

    lintOptions {
        abortOnError false
    }

    a construir.gradle.`

    • la derecha, pasará el proceso de construcción, pero es correcto utilizar de esa manera? porque hay una fuga de un problema de memoria y es por eso que android studio nos advierten.
    • A veces pienso que abortOnError a falso, es sólo difícil, prefiero personalizar la pelusa de la regla de «clase Interna debe ser estática» a weakwarning o info.
  4. 4

    Si quieres tener acceso a los miembros de exterior-class (clase de Actividad) y aún no se quieren hacer los miembros de la estática en la Actividad (desde el fragmento debe ser public static), usted puede hacer el reemplazo 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
            ...
         }

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea