Inyección de dependencia asistida en ViewModel con Dagger-Hilt en Android

Si usa la inyección de dependencia Dagger-Hilt en su proyecto de Android, debe haber encontrado un caso en el que desea proporcionar manualmente algunos parámetros para construir un objeto con el fin de inyectarlo. En general, cuando inyectamos una dependencia/objeto en una clase, el marco Dagger-Hilt lo construye detrás de escena al pasarle automáticamente parámetros específicos, pero ¿qué pasa cuando Dagger-hilt no sabe acerca de algunas dependencias que cómo inyectarlas y queremos? para suministrar manualmente esas dependencias en tiempo de ejecución por nuestra cuenta. 

Ejemplo: cuando creamos un objeto ViewModel, en el constructor de viewmodel especificamos las dependencias requeridas (podría requerir que su objeto de repositorio de red y su base de datos local/objeto DAO, etc.) sean inyectados por dagger-hilt para construirlo. Ahora supongamos que mientras dagger-hilt está inyectando las dependencias especificadas requeridas en el modelo de vista, también desea pasar manualmente algún objeto/dependencia en tiempo de ejecución en el modelo de vista para construirlo… Entonces, ¿cómo haría eso?

Por lo tanto, en este artículo, veremos cómo podemos ayudar manualmente algunas dependencias en tiempo de ejecución junto con las dependencias proporcionadas por la empuñadura de daga. Si desea ver cómo configurar/usar la inyección de dependencia de Dagger-Hilt, primero consulte el artículo de Dagger-Hilt etiquetado anteriormente.

Comprenda cómo usar la inyección asistida en el caso de ViewModel con un ejemplo

Supongamos que tenemos ArticlesFeedViewModel que se inyectará en ArticlesFeedFragment. Ahora suponga que este fragmento aparecerá en la pantalla, necesita obtener la lista de todos los artículos publicados por usted y tiene que mostrarlos en la interfaz de usuario y para hacer esa llamada a la API, estamos llamando a la función getPublishedArticles() presente en ArticlesFeedViewModel’s init{ } bloque para que se llame a la API tan pronto como se inicialice este modelo de vista. Ahora, getPublishedArticles() de ArticlesFeedViewModel llamará a una función presente en ArticlesRepository para comenzar a realizar una llamada de red, por lo que también necesitamos un objeto ArticlesRepository en este modelo de vista y estamos inyectando ArticlesRepository en el constructor de viewmodel con la ayuda de Dagger-Hilt.

Pero, ¿ha notado que también tenemos un objeto/dependencia más llamado ID de usuario anotado con @Assisted? En realidad, necesitamos pasar ese ID de usuario en tiempo de ejecución en la función getPublishedArticles() para recuperar los artículos publicados por usted y estamos obteniendo este ID de usuario en ArticleViewModel de ArticlesFeedFragment tan pronto como se crea una instancia de viewmodel en ese fragmento. Pero cómo ? Veremos esto dentro de un tiempo, primero comprendamos mejor el código dentro de viewmodel.

Entendiendo el código de ViewModel

En comparación con la inyección normal, necesitamos hacer algunos cambios en el caso de la inyección asistida:

  • Usamos @AssistedInject en lugar de @Inject para la inyección del constructor.
  • Los argumentos que deben proporcionarse en tiempo de ejecución se anotan con @Assisted.

Ahora, después de hacer los cambios anteriores, aún no podemos inyectar directamente nuestro modelo de vista en fragmento/actividad, primero debemos crear una fábrica para él porque usaremos esa fábrica para crear una instancia de la clase real, es decir, modelo de vista en nuestro caso. (tenga en cuenta que creamos una fábrica para nuestra clase/modelo de vista como interfaz)

Entonces, hablemos ahora de la fábrica:

  • Anotamos nuestra fábrica con @AssistedFactory. Esta anotación le dice a Dagger-Hilt que esta interfaz se usa para crear una instancia de una clase/modelo de vista que requiere inyección asistida.
  • Dentro de esta fábrica, creamos una función llamada «crear» que será responsable de devolver una instancia de nuestra clase, es decir, ArticlesViewModel y acepta solo aquellos argumentos que debemos proporcionar/asistir en tiempo de ejecución (es decir, ID de usuario), lo que significa argumentos anotados como @Asistido.

Ahora crearemos una función de proveedor de fábrica llamada provideFactory() que proporcionará ViewModelProvider.Factory y tendremos una clase anónima dentro que nuevamente habrá anulado la creación de una función que hemos implementado para crear realmente una instancia de nuestro modelo de vista. Tenga en cuenta que la función ProvidesFactory() requerirá que AssistedFactory y todos los argumentos asistidos se suministren en tiempo de ejecución.

Kotlin

// We are using @AssistedInject instead
// of normal @Inject as we want
// to assist one dependency by our own
// at runtime and rest by dagger-hilt
class ArticlesFeedViewModel @AssistedInject constructor(
    private val articlesRepo: ArticlesRepository,
    // dependency which is to be assisted by
    // us should be annotated with @Assisted
    @Assisted
    private val userId: String
) : ViewModel {
 
    // It's a factory of this viewmodel, we need
    // to annotate this factory interface
    // with @AssistedFactory in order to
    // let Dagger-Hilt know about it
    @AssistedFactory
    interface ArticlesFeedViewModelFactory {
        fun create(userId: String): ArticlesFeedViewModel
    }
 
    // Suppressing unchecked cast warning
    @Suppress("UNCHECKED_CAST")
    companion object {
       
        // putting this function inside
        // companion object so that we can 
        // access it from outside i.e. from fragment/activity
        fun providesFactory(
            assistedFactory: ArticlesFeedViewModelFactory,
            userId: String
        ): ViewModelProvider.Factory = object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                 
                // using our ArticlesFeedViewModelFactory's create function
                // to actually create our viewmodel instance
                return assistedFactory.create(userId) as T
            }
        }
    }
 
    init {
       // Calling getPublishedArticles() function
       // in viewmodel's init block
       // because init block just gets executed
       // after primary constructor
       // So, actually this api will get called as soon
       // as viewmodel gets instantiated
       getPublishedArticles(userId)
    }
 
    private fun getPublishedArticles(userId: String) {
        // Just for sack of example we are just
        // simply storing response in a variable
        val response = articlesRepo.getPublishedArticles(userId).cachedIn(viewModelScope)
    }

Inyectar Viewmodel’s Factory y viewmodel en sí

Estamos utilizando el componente de navegación Jetpack en este ejemplo para la navegación de fragmentos. Ahora supongamos que estamos navegando a ArticlesFeedFragment desde, digamos, HomeFragment y estamos enviando el ID de usuario desde HomeFragment a ArticlesFeedFragment usando Safe Args .

Entonces, primero hemos inyectado nuestra fábrica de ArticlesViewModel. Ahora crearemos una instancia de nuestro modelo de vista con la ayuda de la propiedad de delegado de kotlin «por» y luego llamaremos a ArticlesFeedViewModel.providesFactory() divertido dentro de él que requerirá una fábrica asistida, es decir, nuestro ArticlesFeedViewModelFactory inyectado anteriormente y el ID de usuario asistido manualmente que estamos obteniendo ayuda de argumentos seguros.

Kotlin

@AndroidEntryPoint
class ArticlesFeedFragment : Fragment() {
 
    // using viewbinding
    private var _binding: FragmentArticlesFeedBinding? = null
    private val binding get() = _binding!!
     
    // Using navargs for getting argument
    // supplied from previous fragment
    private val args: ArticlesFeedFragmentArgs by navArgs()
 
    // First injecting our
    // viewmodel's factory interface
    @Inject
    lateinit var articlesFeedViewModelFactory: ArticlesFeedViewModel.ArticlesFeedViewModelFactory
 
    // Creating viewmodel here with
    // the help of kotlin delegate property "by"
    private val viewModel: ArticlesFeedViewModel by viewModels {
        ArticlesFeedViewModel.providesFactory(
            assistedFactory = articlesFeedViewModelFactory,
            userId = args.userId
        )
    }
 
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        _binding = FragmentArticlesFeedBinding.inflate(inflater, container, false)
        return binding.root
    }
     
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
         
        // Do something here
        // You can observe/handle your api response
       // here coming through ArticlesViewModel
    }

Entonces, de esta manera, inyectaremos nuestro ViewModel con el ID de usuario de dependencia asistida proporcionado en tiempo de ejecución. Así es como usamos la Inyección de Dependencia Asistida con Dagger-Hilt que nos permite pasar argumentos en tiempo de ejecución para construir un objeto/dependencia.

Publicación traducida automáticamente

Artículo escrito por haiderfaizan732 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *