En la documentación de Android, tenemos un ejemplo de enlace de datos sin lateinit
:
private var _binding: ResultProfileBinding? = null // This property is only valid between onCreateView and // onDestroyView. private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = ResultProfileBinding.inflate(inflater, container, false) val view = binding.root return view } override fun onDestroyView() { super.onDestroyView() _binding = null }
Por qué no usamos lateinit
, como lo usamos en actividad:
private lateinit var binding: ResultProfileBinding? = null override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { binding = ResultProfileBinding.inflate(inflater, container, false) return binding.root }
Sospecho que tiene algo con un problema de pérdida de memoria. ¿Puedes explicarlo?
Encontré una buena explicación aquí .
Fragmento de la explicación:
¿Cómo ocurren las fugas en los fragmentos? Primero, debemos comenzar revisando el importante matiz de los fragmentos. Tienen dos ciclos de vida diferentes:
- Su propio ciclo de vida (
onCreate
yonDestroy
)- Es el ciclo de vida de la vista (
onCreateView
yonDestroyView
)Tener dos ciclos de vida para una sola pantalla puede ser problemático. Se crean y destruyen en diferentes momentos, por ejemplo, al colocar un fragmento en la pila posterior. Específicamente, mantener las vistas después de llamar a
onDestroyView
se filtrará. Esto sucede cuando un fragmento está en la pila posterior y, aunque su vista está destruida, el fragmento en sí no lo está. El recolector de elementos no utilizados no puede borrar la referencia a esas vistas.
Y un fragmento de esta respuesta de Stack Overflow:
Debe anular sus referencias a las vistas en
onDestroyView
, ya que esa es la señal de que el sistema Fragment ya no usa la vista y se puede recolectar basura de manera segura si no fuera por su referencia continua aView
.
// here a firestore database uses callback to be executed when the document recieved db.collection("cities").document("SF").get() .addOnSuccessListener { document -> if (document != null) { binding.textView.text = document.data.toString() } else { Log.d(TAG, "No such document") } }
si el usuario abrió el fragmento y lo cerró antes de recibir el documento (eso significa que el fragmento ya no se usará y el recolector de basura debería borrar todas sus variables si la variable es nula o ya no se usa)
lateinit
private lateinit var binding: ResultProfileBinding
el colector de garaje no borrará el binding
ya que todavía se usa en la devolución de llamada y el fragmento permanecerá en la memoria, lo que provocará una fuga de memoria, luego, sin embargo, la devolución de llamada se ejecutó y estableció el texto que el usuario no sabrá porque dejó el fragmento
¡Imagínese si el usuario hiciera este escenario varias veces!
private var _binding: ResultProfileBinding? = null private val binding get() = _binding!!
lo configura en nulo en onDestroyView
para que el binding
y el fragmento se puedan recolectar como basura (sin fugas de memoria)
PERO, ¿qué sucederá cuando se ejecute la devolución de llamada?
obtendrá una NullPointerException
, así que tenga en cuenta eso
y cualquiera que sea la cantidad de veces que el usuario abra el fragmento y lo cierre, se recolectará como basura
pruébelo usted mismo con este código, puede usar Android Studio Profiler para ver la memoria del dispositivo y / o leakCanary para recibir notificaciones sobre las fugas de memoria de la aplicación
binding
de fragmentos podría provocar fugas de memoria, si no están configurados como nulos en onDestroyView
. Es por eso que configuramos _binding
en null
en onDestroyView
Mientras usamos lateinit
, no podemos asignar una propiedad lateinit
a null
. Por lo tanto, si usamos lateinit binding
, no podremos configurarlo como nulo en onDestroyView
y provocaría una pérdida de memory leak
. Es por eso que la documentación de Android recomienda usar una variable nullable
.
En el caso de activities
, no necesitamos asignar el binding
a null
, ya que no provocan pérdidas de memoria, y podemos usar una propiedad lateinit
para el binding
.