¿Debo incluir LifecycleOwner en ViewModel?

4 minutos de lectura

Avatar de usuario de Hiroga Katageri
Hiroga Katageri

Actualmente se necesita LifecycleOwner para poder crear un observador.

Tengo un código que crea un Observador en ViewModel, por lo que adjunto LifecycleOwner cuando recupero ViewModel en mi Fragmento.

Según la documentación de Google.

Precaución: un modelo de vista nunca debe hacer referencia a una vista, un ciclo de vida o cualquier clase que pueda contener una referencia al contexto de la actividad.

¿Rompí esa advertencia y, si lo hice, de qué manera me recomienda mover mi creación de un observador para el retorno de datos?

Solo hice un observador, así que me pregunto si todavía es válido. Ya que también en la documentación de Google también decía.

Los objetos ViewModel pueden contener LifecycleObservers, como objetos LiveData.

Fragmento principal

private lateinit var model: MainViewModel

/**
 * Observer for our ViewModel IpAddress LiveData value.
 * @see Observer.onChanged
 * */
private val ipObserver = Observer<String> {
    textIp.text = it
    hideProgressBar()
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    model = ViewModelProviders.of(this).get(MainViewModel::class.java)
    model.attach(this)
}

override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? =
        inflater?.inflate(R.layout.fragment_main, container, false)

override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    buttonRetrieveIp.setOnClickListener {
        showProgressBar()
        model.fetchMyIp().observe(this, ipObserver) //Here we attach our ipObserver
    }
}

override fun showProgressBar() {

    textIp.visibility = View.GONE
    progressBar.visibility = View.VISIBLE
}

override fun hideProgressBar() {

    progressBar.visibility = View.GONE
    textIp.visibility = View.VISIBLE
}

Modelo de vista principal

private var ipAddress = MutableLiveData<String>()
private lateinit var owner: LifecycleOwner

fun attach(fragment: MainFragment) {
    owner = fragment
}

/**
 * For more information regarding Fuel Request using Fuel Routing and Live Data Response.
 * @see <a href="https://github.com/kittinunf/Fuel#routing-support">Fuel Routing Support</a>
 * @see <a href="https://github.com/kittinunf/Fuel#livedata-support">Fuel LiveData Support</a>
 * */
fun fetchMyIp(): LiveData<String> {

    Fuel.request(IpAddressApi.MyIp())
            .liveDataResponse()
            .observe(owner, Observer {

                if (it?.first?.statusCode == 200) {//If you want you can add a status code checker here.

                    it.second.success {

                        ipAddress.value = Ip.toIp(String(it))?.ip
                    }
                }
            })
    return ipAddress
}

Actualización 1: ViewModel mejorado gracias a la sugerencia de @pskink para usar Transformaciones.

private lateinit var ipAddress:LiveData<String>

/**
 * Improved ViewModel since January 23, 2018 credits to <a href="https://stackoverflow.com/users/2252830/pskink">pskink</a> <a href="
 *
 * For more information regarding Fuel Request using Fuel Routing and Live Data Response.
 * @see <a href="https://github.com/kittinunf/Fuel#routing-support">Fuel Routing Support</a>
 * @see <a href="https://github.com/kittinunf/Fuel#livedata-support">Fuel LiveData Support</a>
 * */
fun fetchMyIp(): LiveData<String> {

    ipAddress = Transformations.map(Fuel.request(IpAddressApi.MyIp()).liveDataResponse(), {

        var ip:String? = ""

            it.second.success {

                ip = Ip.toIp(String(it))?.ip
            }
        ip
    })

    return ipAddress
}

  • intentó MediatorLiveData o Transformations#map / Transformations#switchMap?

    – pskink

    23 de enero de 2018 a las 7:34


  • Todavía no lo he hecho, así que en lugar de devolver una cadena LiveData, devolveré un MediatorLiveData y agregaré ambas fuentes y agregaré un solo observador en mi fragmento, ¿no?

    – Hiroga Katageri

    23 de enero de 2018 a las 7:48

  • primer intento Transformations clase – parece que es la forma más fácil – si no, cuando se trata de MediatorLiveData ¿Qué quieres decir con “ambas fuentes”? hay una fuente que cambia, ¿no?

    – pskink

    23 de enero de 2018 a las 7:49


  • ver Transformar LiveData

    – pskink

    23 de enero de 2018 a las 9:30

  • Lo tengo ahora, pude hacerlo funcionar usando Transformaciones.

    – Hiroga Katageri

    23 de enero de 2018 a las 9:40

Avatar de usuario de Vitaliy A
Vitaliy A

No. Si desea observar cambios de algunos LiveData dentro de tu ViewModel puedes usar observeForever() que no requiere LifecycleOwner.

Recuerde eliminar este observador en ViewModel‘s onCleared() evento:

val observer = new Observer() {
  override public void onChanged(Integer integer) {
    //Do something with "integer"
  }
}

liveData.observeForever(observer);

override fun onCleared() {
    liveData.removeObserver(observer) 
    super.onCleared()
}

Muy buena referencia con ejemplos de observar LiveData.

  • Esta parece ser una respuesta útil. Desafortunadamente, el enlace para observar LiveData está roto.

    –Steve Gelman

    9 de julio a las 23:17

Suposiciones:

  1. Fuel se refiere a tu ViewModel
  2. Fuel.request(IpAddressApi.MyIp()) es un método en su ViewModel
  3. IpAddressApi.MyIp() no tiene una referencia a su LifecycleOwner,

Si todo es cierto, entonces no lo estás violando. Siempre y cuando no esté pasando un LifecycleOwner referencia a la ViewModel ¡estás seguro!

Propietario del ciclo de vidase relaciona con una Actividad o Fragmento, ya que posee varios ciclos de vida de Android, por ejemplo, onCreate, onPause, onDestroy, etc.

  • He actualizado mi pregunta con información adicional. Fuel es mi HttpClient, Fuel.request es un método en Fuel que convierte una interfaz FuelRouting en una solicitud, que es donde creo un observador para el retorno de mi método GET.

    – Hiroga Katageri

    23 de enero de 2018 a las 7:40


en kotlin esto puede ser algo como:

val mObserver = Observer<List<QueueTabData>> { myString->
// do something with myString
}

¿Ha sido útil esta solución?