Escribí un programa muy simple con una actividad que contiene un fragmento. Hice que el constructor Fragment sea privado y estoy usando el método estático newInstance() para devolver el fragmento. El problema comienza cuando estoy girando el teléfono. Estoy recibiendo una excepción que dice:
No se puede iniciar la actividad ComponentInfo{com.example.todeleteimmediatley/com.example.MainActivity}: androidx.fragment.app.Fragment$InstantiationException: No se puede crear una instancia del fragmento com.example.DatesFragment: no se pudo encontrar el constructor Fragment
Depuré el programa y vi que la excepción está en la primera línea en el método onCreate (al llamar a super.onCreate() ). Alguien puede explicarme por qué el Fragmento debe tener un constructor y por qué ocurre la excepción en la fase super.onCreate() .
Cuando rote su dispositivo, su Activity será destruida y recreada. Parte de esto es destruir y recrear cualquier Fragment que esté alojando tu Actividad.
Durante el paso de recreación , el marco de Android necesita crear una nueva instancia de su Fragment . De forma predeterminada, lo hace invocando el constructor sin argumentos de su Fragmento. Esto significa que este constructor debe (a) existir y (b) ser public .
Esta recreación de Fragmentos es activada por el super.onCreate() de su Actividad.
La recomendación general es crear un método de fábrica newInstance() (como lo ha hecho), pero dejar solo el constructor predeterminado (es decir, no hacerlo private ). Sí, esto significa que todavía es posible que alguien llame directamente al constructor, lo que no desea, pero es obligatorio si no desea involucrarse con FragmentFactory .
¿Significa que en onCreate debo verificar si el contenedor ya contiene un Fragmento antes de agregarlo y confirmarlo? Porque, según tengo entendido, onCreate (). super restaurará el Fragmento anterior.
Mi recomendación aquí es solo confirmar la transacción Fragmento una vez, la primera vez que se inicia su Actividad. Por lo general, esto se logra verificando que el paquete de estado de savedInstanceState sea nulo antes de confirmar la transacción:
if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .replace(R.id.foo, FooFragment.newInstance(...)) .commit(); } Debido a que sus Fragmentos son parte del estado de instancia de su Actividad, cualquier recreación (siempre que el estado de instancia savedInstanceState no sea null ) se manejará automáticamente.
¿Por qué es mejor en el método newInstance agregar la información que el Fragmento necesita al Paquete y no almacenarlos como miembros (atributos) del Fragmento?
Todo vuelve al hecho de que el marco de trabajo de Android necesita crear una nueva instancia de su Fragmento. Si simplemente tiene campos de miembros en su Fragmento que configuró, el marco de trabajo de Android no los reconocerá y no tendrá forma de guardarlos o restaurarlos.
Sin embargo, el paquete de arguments es algo que Android sí conoce. Los argumentos se consideran parte del estado de la instancia del Fragmento (y, por lo tanto, parte del estado de la instancia de la Actividad contenedora) y se guardarán y restaurarán automáticamente. Es por eso que solo puede poner ciertos tipos de datos en el paquete de arguments ; Android solo sabe cómo "escribir" y "leer" ciertos tipos de datos durante este proceso de recreación.
en Java
Si una clase no define ningún constructor, entonces, en tiempo de compilación, el compilador generará un constructor que no tiene argumentos (generalmente lo llamamos constructor predeterminado, constructor sin argumentos o constructor sin argumentos).
Si una clase define cualquier constructor que tenga argumentos, en tiempo de compilación, el compilador no generará un constructor predeterminado.
¿Alguien puede explicarme por qué el Fragmento debe tener un constructor?
No necesita definir un constructor si su fragmento no recibe ningún parámetro.
¿Por qué ocurre la excepción en la fase super.onCreate()?
En Android, hay varios escenarios en los que el sistema necesita volver a crear una actividad, como.
Cuando cambia la configuración, como cuando los usuarios giran la orientación de la pantalla o cambian el idioma.
Cuando el sistema tiene poca memoria
Al volver a crear una actividad, el sistema creará una nueva instancia de la actividad, luego llamará al ciclo de vida de la actividad, el primero será la devolución de llamada onCreate() . En su actividad (por ejemplo, MainActivity), debe llamar a super.onCreate() de su padre. Esta declaración restaurará todos los fragmentos (como DatesFragment en su caso) que administró la actividad.
Debido a que esta es una nueva instancia de MainActivity, también debe crear una nueva instancia de todos los fragmentos administrados (incluido DatesFragment). Para hacerlo, invocarán el constructor de argumentos predeterminado.
Pero en DatesFragment hizo que el constructor predeterminado sea privado, lo que significa que solo se accede dentro de esa clase, no hay forma de que el sistema pueda invocar al constructor de la clase DatesFragment, por lo que lanzan InstatiationException.
Solución:
Si su fragmento no recibe ningún parámetro, no defina ningún constructor
Si necesita pasar parámetros a su fragmento, use el constructor predeterminado junto con setArguments (Bundle)