android - from - dialogfragment kotlin
La clase interna del fragmento debe ser estática (5)
Tengo una clase FragmentActivity
con clase interna que debería mostrar Dialog
. Pero estoy obligado a hacerlo static
. Eclipse me ofrece suprimir el error con @SuppressLint("ValidFragment")
. ¿Es 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()
inicia Asynctask.
No hablaré sobre fragmentos internos, sino más específicamente sobre un DialogFragment definido dentro de una actividad porque es el 99% del caso para esta pregunta.
Desde mi punto de vista, no quiero que mi DialogFragment (tu NetworkConnectionError) sea estático porque quiero poder invocar variables o métodos de mi clase contenedora (Activity).
No será estático, pero tampoco quiero generar memoryLeaks.
¿Cuál es la solución?
Sencillo. Cuando ingrese en Stop, asegúrese de eliminar su DialogFragment. Es tan simple como eso. El código parece algo así:
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 forma evitará fugas de memoria (porque es malo) y se asegurará de que no tenga un fragmento estático [impropio] que no pueda acceder a los campos y métodos de su actividad. Esta es la buena manera de manejar ese problema, desde mi punto de vista.
Si desea acceder a los miembros de la clase externa (Actividad) y aún así no desea que los miembros permanezcan estáticos en Actividad (ya que el fragmento debe ser público estático), puede hacer la anulación en ActividadCreada
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
...
}
Si lo desarrollas en un estudio de Android, entonces no hay problema si no lo pones como estático. El proyecto se ejecutará sin ningún error y en el momento de la generación de la aplicación obtendrás un error: esta clase interna de fragmento debería ser estática [Fragmento_valido]
Eso es un error de pelusa, probablemente estés compilando con gradle, para deshabilitar el aborto en los errores, agrega:
lintOptions {
abortOnError false
}
a build.gradle `
agregar anotación antes de la clase interna
@SuppressLint ("validFragment")
Las clases internas no estáticas tienen una referencia a sus clases principales. El problema de hacer que una clase interna Fragmento no sea estática es que siempre mantienes una referencia a la Actividad . GarbageCollector no puede recopilar tu actividad . Entonces puede ''filtrar'' la actividad si, por ejemplo, cambia la orientación. Porque el Fragmento aún podría vivir y se inserta en una nueva Actividad .
EDITAR:
Como algunas personas me pidieron un ejemplo, comencé a escribir una, mientras hacía esto, encontré algunos problemas más al usar Fragmentos no estáticos:
- No se pueden usar en un archivo xml ya que no tienen un constructor vacío (Pueden tener un constructor vacío, pero normalmente se crean instancias de clases anidadas no
myActivityInstance.new Fragment()
haciendomyActivityInstance.new Fragment()
y esto es diferente a llamar solo a un constructor vacío) - No se pueden reutilizar en absoluto, ya que
FragmentManager
veces llama también a este constructor vacío. Si agregó el Fragmento en alguna Transacción.
Entonces, para hacer que mi ejemplo funcione, tuve que agregar
wrongFragment.setRetainInstance(true);
Línea para no hacer que la aplicación falle en el cambio de orientación.
Si ejecuta este código, tendrá una actividad con algunas textviews y 2 botones: los botones aumentan el contador. Y los Fragmentos muestran la orientación que creen que tiene su actividad. Al principio todo funciona correctamente. Pero después de cambiar la orientación de la pantalla, solo el primer Fragmento funciona correctamente: el segundo sigue llamando cosas en su actividad anterior.
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 probablemente no debería convertir la actividad en onAttach
si desea usar su Fragmento en diferentes Actividades, pero aquí está trabajando para el ejemplo.
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>