Android LiveData: ¿cómo reutilizar el mismo ViewModel en diferentes actividades?

5 minutos de lectura

avatar de usuario de user1209216
usuario1209216

Ejemplo de modelo de vista:

public class NameViewModel extends ViewModel {
    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<>();
        }
        return mCurrentName;
    }

}

Actividad principal:

mModel = ViewModelProviders.of(this).get(NameViewModel.class);

// Create the observer which updates the UI.
final Observer<String> nameObserver = textView::setText;

// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
mModel.getCurrentName().observe(this, nameObserver);

quiero llamar mModel.getCurrentName().setValue(anotherName); en la segunda actividad y hacer que MainActivity reciba cambios. ¿Es eso posible?

  • La respuesta “correcta” es que “si desea compartir datos entre ellos, no deberían ser actividades diferentes, y en su lugar debería intercambiar fragmentos”.

    – EpicPandaForce

    19 de marzo de 2018 a las 14:34

  • @EpicPandaForce tal vez, pero no es así como funciona la plantilla maestra/detalle de AndroidStudio, ni los planos de arquitectura de Android

    – Marca

    10 de julio de 2018 a las 6:09

  • @Mark que es una falla de los planos de arquitectura de Android y la plantilla, entonces.

    – EpicPandaForce

    25/01/2019 a las 22:00


  • Consulte este stackoverflow.com/questions/56521969/…

    – Levon Petrosián

    10 de junio de 2019 a las 16:12

Avatar de usuario de Saeed Masoumi
saeed masoumi

Cuando usted llama ViewModelProviders.of(this)en realidad creas/retienes un ViewModelStore que está obligado a thispor lo que diferentes actividades tienen diferentes ViewModelStore y cada ViewModelStore crea una instancia diferente de un ViewModel utilizando una fábrica determinada, por lo que no puede tener la misma instancia de un ViewModel en diferentes ViewModelStores.

Pero puede lograr esto pasando una única instancia de una fábrica ViewModel personalizada que actúa como una fábrica única, por lo que siempre pasará la misma instancia de su ViewModel entre diferentes actividades.

Por ejemplo:

public class SingletonNameViewModelFactory extends ViewModelProvider.NewInstanceFactory {


    NameViewModel t;

    public SingletonNameViewModelFactory() {
      //  t = provideNameViewModelSomeHowUsingDependencyInjection
    }

    @Override
    public NameViewModel create(Class<NameViewModel> modelClass) {
        return t;
    }
}

Entonces lo que necesitas es hacer SingletonNameViewModelFactory singleton (por ejemplo, usando Dagger) y úsalo así:

mModel = ViewModelProviders.of(this,myFactory).get(NameViewModel.class);

Nota:

Conservación ViewModels entre diferentes ámbitos es un anti-patrón. Se recomienda encarecidamente conservar los objetos de la capa de datos (p. ej., hacer que su Fuente de datos o Repositorio sea único) y conservar sus datos entre diferentes ámbitos (Actividades).

Leer este artículo para más detalles.

  • Si ya está almacenando datos en caché en una capa de datos singleton, ¿cuál es el punto de ViewModel?

    – EpicPandaForce

    19 de marzo de 2018 a las 14:36


  • @EpicPandaForce, quiero decir, no importa cómo actúe su capa de datos, ViewModel debe notificar sobre sus cambios de datos de alguna manera, por lo que almacenar en caché su capa de datos es una forma de mantener sus datos en diferentes ámbitos.

    – Saeed Masoumi

    19 de marzo de 2018 a las 14:43

  • Bueno, sí, creo que tienes razón, no debería intentar hacer eso. Pero, ¿qué pasa con el fragmento? ¿Es una buena práctica hacer que los fragmentos secundarios observen el mismo modelo de vista? Por ejemplo ViewModelProviders.of(getActivity()).get(NameViewModel.class) fragmento interior.

    – usuario1209216

    20 de marzo de 2018 a las 6:48

  • @user1209216 Sí, ¿por qué no ver este artículo para obtener más detalles? desarrollador.android.com/topic/libraries/architecture/…

    – Saeed Masoumi

    20 de marzo de 2018 a las 9:19

  • ¿Cómo lograr esto si usamos DaggerViewModelFactory?

    – Morteza Rastgoo

    17 de agosto de 2019 a las 11:36

Avatar de usuario de TotoliciCristian
TotoliciCristian

Al obtener el modelo de vista usando los ViewModelProviders que está pasando como propietario del ciclo de vida MainActivity, esto le dará el modelo de vista para esa actividad. En la segunda actividad, obtendrá una instancia diferente de ese ViewModel, esta vez para su segunda actividad. El segundo modelo tendrá un segundo datos en vivo.

Lo que puede hacer es mantener los datos en una capa diferente, como un repositorio, que puede ser un singleton y de esa manera puede usar el mismo modelo de vista.

ingrese la descripción de la imagen aquí

public class NameViewModel extends ViewModel {
    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = DataRepository.getInstance().getCurrentName();
        }
        return mCurrentName;
    }
}

//SingleTon
public class DataRepository     

    private MutableLiveData<String> mCurrentName;

    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<>();
        }
        return mCurrentName;
    }
//Singleton code
...
}

  • esta debería ser una respuesta aceptada … sin violar el patrón de arquitectura de Android

    – suma20156

    29 de diciembre de 2020 a las 13:40

  • Este enfoque también se recomienda aquí en este tutorial oficial: LiveData in repositories: To avoid leaking ViewModels and callback hell, repositories can be observed

    – alierdogan7

    17 de junio de 2021 a las 9:43


Simplemente cree la instancia de su Ver modeloen este caso NombreVerModelo

Tu ViewModel Factory será como

class ViewModelFactory : ViewModelProvider.NewInstanceFactory() {

    override fun <T : ViewModel?> create(modelClass: Class<T>) =
        with(modelClass){
            when {
                isAssignableFrom(NameViewModel::class.java) -> NameViewModel.getInstance()
                else -> throw IllegalArgumentException("Unknown viewModel class $modelClass")
            }
        } as T


    companion object {
        private var instance : ViewModelFactory? = null
        fun getInstance() =
            instance ?: synchronized(ViewModelFactory::class.java){
                instance ?: ViewModelFactory().also { instance = it }
            }
    }
}

Y tu modelo de vista

class NameViewModel : ViewModel() {

    //your liveData objects and many more...

    companion object {
        private var instance : NameViewModel? = null
        fun getInstance() =
            instance ?: synchronized(NameViewModel::class.java){
                instance ?: NameViewModel().also { instance = it }
            }
    }
}

Ahora puedes usar ViewModelProviders para obtener la misma instancia de su ViewModel para usar en cualquier actividad

ViewModelProviders.of(this, ViewModelFactory.getInstance()).get(NameViewModel::class.java)

O

crear una función de extensión para facilitar el acceso

fun <T : ViewModel> AppCompatActivity.getViewModel(viewModelClass: Class<T>) =
    ViewModelProviders.of(this, ViewModelFactory.getInstance()).get(viewModelClass)

  • Probé esta solución pero no funciona. Intentando actualizar un TextView en un fragmento de una segunda actividad.

    – Samuel

    23 de enero de 2020 a las 20:49

  • ¿Puede echar un vistazo a mi presentación: stackoverflow.com/questions/59887014/…

    – Samuel

    23 de enero de 2020 a las 21:06

Avatar de usuario de Darrell Burk
Darrel Burk

El alcance/ciclo de vida de ViewModel está vinculado a una actividad simplemente porque el ViewModelStoreOwner que pasa al constructor de ViewModelProvider resulta ser la actividad.

Dado que puede proporcionar ViewModelStoreOwner, puede proporcionar fácilmente uno que tenga un ciclo de vida más largo, como la aplicación.

Puedes

  1. Proporcione su propia subclase de aplicación y haga que implemente ViewModelStoreOwner (y tenga un ViewModelStore)
  2. En las llamadas al constructor de ViewModelProvider, pase actividad.aplicación en lugar de actividad.

Esto hará que ViewModelProvider interactúe con ViewModelStore a nivel de la aplicación, lo que le permitirá crear instancias de ViewModel que tendrán el alcance de la aplicación.

¿Ha sido útil esta solución?