Nous avons déjà parcouru beaucoup de terrain dans notre série de composants d’architecture Android. Nous avons commencé par parler de l'idée de la nouvelle architecture et des composants clés présentés dans Google I / O. Dans le second article, nous avons commencé notre exploration approfondie des principaux composants du paquet, en examinant de plus près les Cycle de la vie
et LiveModel
Composants. Dans cet article, nous allons continuer à explorer les composants d’architecture, en analysant cette fois l’impressionnant Données en direct
composant.
Je suppose que vous connaissez les concepts et les composants abordés dans les derniers tutoriels, comme Cycle de la vie
, Cycle de vie
, et LifecycleObserver
. Si vous ne l'êtes pas, jetez un coup d'œil au premier message de cette série, où je discute de l'idée générale de la nouvelle architecture Android et de ses composants..
Nous allons continuer à construire sur l'exemple d'application que nous avons démarré dans la dernière partie de cette série. Vous pouvez le trouver dans le tutoriel GitHub repo.
Données en direct
est un détenteur de données. Il est capable d'être observé, il peut contenir n'importe quel type de données et, en plus, sensible au cycle de vie. En termes pratiques, Données en direct
peut être configuré pour envoyer des mises à jour de données uniquement lorsque son observateur est actif. Grâce à sa conscience du cycle de vie, quand observé par un Cycle de vie
la Données en direct
composant enverra des mises à jour uniquement lorsque l'observateur Cycle de la vie
est toujours actif, et il va supprimer la relation observée une fois que l'observateur Cycle de la vie
est détruit.
le Données en direct
Le composant présente de nombreuses caractéristiques intéressantes:
Cycle de la vie
Données en direct
UNE Données en direct
composant envoie des mises à jour de données uniquement lorsque son observateur est "actif". Observé par un Cycle de vie
, la Données en direct
composante considère que l’observateur n’est actif que lorsque Cycle de la vie
est sur les états COMMENCÉ
ou A REPRIS
, sinon, l'observateur sera considéré comme inactif. Pendant l'état inactif de l'observateur, Données en direct
arrêtera le flux de mise à jour des données jusqu'à ce que son observateur redevienne actif. Si l'observateur est détruit, Données en direct
va supprimer sa référence à l'observateur.
Pour obtenir ce comportement, Données en direct
crée une relation étroite avec l'observateur Cycle de la vie
quand observé par un Cycle de vie
. Cette capacité permet d'éviter plus facilement les fuites de mémoire lors de l'observation Données en direct
. Cependant, si le processus d’observation est appelé sans Cycle de vie
, la Données en direct
le composant ne réagira pas Cycle de la vie
état, et le statut de l'observateur doit être traité manuellement.
Observer un Données en direct
, appel observer (LifecycleOwner, Observer
ou observerForever (observateur
.
observer (LifecycleOwner, Observer)
: C’est le moyen standard d’observer un Données en direct
. Il relie l'observateur à un Cycle de la vie
, en changeant Données en direct
Etat actif et inactif selon le Cycle de vie
état actuel.observerForever (observateur)
: Cette méthode n'utilise pas de Cycle de vie
, alors le Données en direct
ne sera pas en mesure de répondre Cycle de la vie
événements. Lorsque vous utilisez cette méthode, il est très important d’appeler removeObserver (Observateur)
, sinon l'observateur ne peut pas être ramassé, ce qui provoque une fuite de mémoire.// appelé depuis un LifecycleOwner location.observe (// LifecycleOwner this, // création d'un observateur Observateur emplacement -> info ("emplacement: $ emplacement !!. latitude, $ emplacement.longitude") // Observer sans LifecycleOwner val observer = Observateur emplacement -> info ("emplacement: $ emplacement !!. Latitude, $ emplacement.longitude")) location.observeForever (observateur) // lorsque observateur sans LivecyleOwner // il est nécessaire de supprimer les observateurs à un moment donné location.removeObserver (observateur)
Données en direct
Le type générique dans le Données en direct
La classe définit le type de données qui seront conservées. Par exemple, Données en direct
retient Emplacement
Les données. Ou Données en direct
détient un Chaîne
.
Deux méthodes principales doivent être prises en compte dans la mise en œuvre du composant: onActive ()
et onInactive ()
. Les deux méthodes réagissent à l'état de l'observateur.
Dans notre exemple de projet, nous avons utilisé beaucoup de Données en direct
objets, mais nous n'en avons implémenté qu'un: LocationLiveData
. La classe traite du GPS Emplacement
, passer la position actuelle uniquement pour un observateur actif. Notez que la classe met à jour sa valeur sur le onLocationChanged
méthode, en transmettant à l’observateur actif actuel le rapport actualisé Emplacement
Les données.
Classe du constructeur LocationLiveData @Inject (contexte: contexte): LiveData(), LocationListener, AnkoLogger private value locationManager: LocationManager = context.getSystemService (Context.LOCATION_SERVICE) comme LocationManager @SuppressLint ("MissingPermission") annule le plaisir onInactive () info ("onInactive") locationManager.removeUpdates (this)) SuppressLint ("MissingPermission") fun refreshLocation () info ("refreshLocation") locationManager.requestSingleUpdate (LocationManager.GPS_PROVIDER, this, null)
MutableLiveData
Classe d'assistancele MutableLiveData
est une classe d'assistance qui s'étend Données en direct
, et expose postValue
et setValue
méthodes. Autre que cela, il se comporte exactement comme son parent. Pour l'utiliser, définissez le type de données qu'il contient, comme MutableLiveData
tenir un Chaîne
, et créer une nouvelle instance.
val myData: MutableLiveData= MutableLiveData ()
Pour envoyer des mises à jour à un observateur, appelez postValue
ou setValue
. Le comportement de ces méthodes est assez similaire; toutefois, setValue
définira directement une nouvelle valeur et ne peut être appelée qu'à partir du thread principal, tandis que postValue
crée une nouvelle tâche sur le thread principal pour définir la nouvelle valeur et peut être appelée à partir d'un thread d'arrière-plan.
fun updateData () // doit être appelé à partir du fil principal myData.value = api.getUpdate fun updateDataFromBG () // peut être appelé à partir du fil bg myData.postValue (api.getUpdate)
Il est important de considérer que, puisque le postValue
méthode crée un nouveau Tâche
et les messages sur le fil principal, il sera plus lent que les appels directs à setValue
.
Données en direct
Finalement, vous devrez changer un Données en direct
et propager sa nouvelle valeur à son observateur. Ou peut-être devez-vous créer une réaction en chaîne entre deux Données en direct
objets, en faisant réagir les changements sur un autre. Pour faire face aux deux situations, vous pouvez utiliser le Transformations
classe.
Transformations.map
applique une fonction sur un Données en direct
instance et envoie le résultat à ses observateurs, ce qui vous permet de manipuler la valeur des données..
Il est très facile à mettre en œuvre Transformations.map
. Tout ce que vous avez à faire est de fournir un Données en direct
à observer et un Une fonction
être appelé quand l'observé Données en direct
change, en se rappelant que le Une fonction
doit renvoyer la nouvelle valeur du transformé Données en direct
.
Supposons que vous ayez un Données en direct
qui doit appeler une API quand un Chaîne
la valeur, comme un champ de recherche, change.
// LiveData qui appelle api // quand 'searchLive' change de valeur val apiLive: LiveData= Transformations.map (searchLive, requête -> return @ map api.call (requête)) // Chaque fois que "searchLive" a // sa valeur mise à jour, il appellera // "apiLive" Transformation.map fun updateSearch (query: String) searchLive.postValue (query)
le Transformations.switchMap
est assez similaire à Transformations.map
, mais il faut retourner un Données en direct
objet à la suite. C'est un peu plus difficile à utiliser, mais cela vous permet de construire de puissantes réactions en chaîne.
Dans notre projet, nous avons utilisé Transformations.switchMap
créer une réaction entre LocationLiveData
et ApiResponse
.
Transformation.switchMap
observe LocationLiveData
changements.LocationLiveData
la valeur mise à jour est utilisée pour appeler le MainRepository
pour obtenir la météo pour l'emplacement spécifié.OpenWeatherService
qui produit un Données en direct>
Par conséquent.Données en direct
est observé par un MediatorLiveData
, responsable de la modification de la valeur reçue et de la mise à jour de la météo présentée dans la couche de visualisation.classe constructeur MainViewModel @Inject (référentiel de valeurs privé: MainRepository): ViewModel (), AnkoLogger // Emplacement emplacement de la valeur privée: LocationLiveData = repository.locationLiveDa () variable privée weatherByLocationResponse: LiveData> = Transformations.switchMap (emplacement, l -> info ("weatherByLocation: \ nlocation: $ l") return @ switchMap repository.getWeatherByLocation (l))
Méfiez-vous des opérations fastidieuses dans votre Données en direct
transformations, cependant. Dans le code ci-dessus, les deux Transformations
les méthodes s'exécutent sur le thread principal.
MediatorLiveData
MediatorLiveData
est un type plus avancé de Données en direct
. Ses capacités sont très similaires à celles du Transformations
classe: il est capable de réagir à d'autres Données en direct
objets, appelant un Une fonction
lorsque les données observées changent. Cependant, il présente de nombreux avantages par rapport à Transformations
, car il n'a pas besoin de fonctionner sur le fil principal et peut observer plusieurs Données en direct
s à la fois.
Observer un Données en direct
, appel addSource (LiveData
, faire réagir l'observateur à la , Observateur)onChanged
méthode de la donnée Données en direct
. Pour arrêter l'observation, appelez removeSource (LiveData
.)
val mediatorData: MediatorLiveData= MediatorLiveData () mediatorData.addSource (dataA, valeur -> // réagit à la valeur info ("ma valeur $ valeur")) mediatorData.addSource (dataB, valeur -> // réagit à la valeur info ("ma valeur $ value ") // on peut supprimer le source une fois utilisé mediatorData.removeSource (dataB))
Dans notre projet, les données observées par la couche de vue contenant la météo à afficher sont un MediatorLiveData
. Le composant observe deux autres Données en direct
objets: weatherByLocationResponse
, qui reçoit les mises à jour météorologiques par emplacement, et weatherByCityResponse
, qui reçoit les mises à jour météorologiques par nom de ville. Chaque fois que ces objets sont mis à jour, weatherByCityResponse
mettra à jour la couche de vue avec la météo actuelle demandée.
dans le MainViewModel
, on observe le Données en direct
et fournir le Météo
objet à voir.
Classe constructeur MainViewModel @Inject (référentiel de valeurs privé: MainRepository): ViewModel (), AnkoLogger //… // Valeur observée par View. // Il transforme une réponse météo en un WeatherMain. météo privé: MediatorLiveData> = MediatorLiveData () // récupère la météo LiveData amusante getWeather (): LiveData > info ("getWeather") météo retour fun privé addWeatherSources () info ("addWeatherSources") weather.addSource (weatherByCityResponse, w -> info ("addWeatherSources: \ nweather: $ w !!.. data !!) !! ") updateWeather (w.data !!)) weather.addSource (weatherByLocationResponse, w -> info (" addWeatherSources: weatherByLocationResponse: \ n $ w !!. données !! ") updateWeather (w.data! !)) private fun updateWeather (w: WeatherResponse) info ("updateWeather") // Obtenir la météo à partir de aujourd'hui val weatherMain = WeatherMain.factory (w) // Enregistrer sur les préférences partagées repository.saveWeatherMainOnPrefs (weatherMain) // update valeur météo weather.postValue (ApiResponse (data = weatherMain)) init //… addWeatherSources ()
dans le Activité principale
, la météo est observée et son résultat est montré à l'utilisateur.
private fun initModel () // Obtenez ViewModel viewModel = ViewModelProviders.of (this, viewModelFactory) .get (MainViewModel :: class.java) if (viewModel! = null) // observez la météo viewModel !!. getWeather (). observer (ceci @ MainActivity, Observateur r -> if (r! = null) info ("Météo reçue sur MainActivity: \ n $ r") if (! r.hasError ()) // N'en a pas errors info ("météo: $ r.data") if (r.data! = null) setUI (r.data) else // erreur ("erreur: $ r.error") isLoading ( false) if (r.error !!. statusCode! = 0) if (r.error !!. message! = null) toast (r.error.message !!) else toast ("Une erreur s'est produite") )
le MediatorLiveData
a également été utilisé comme base d’un objet gérant les appels à l’API OpenWeatherMap. Jetez un coup d'œil à cette implémentation; c'est plus avancé que ceux ci-dessus, et ça vaut vraiment la peine d'étudier. Si vous êtes intéressé, jetez un oeil à OpenWeatherService
, en accordant une attention particulière à la Médiateur
classe.
Nous sommes presque à la fin de notre exploration des composants d'architecture d'Android. À présent, vous devriez comprendre suffisamment pour créer des applications puissantes. Dans le prochain post, nous allons explorer Pièce
, un ORM qui enveloppe SQLite
et peut produire Données en direct
résultats. le Pièce
composant s'inscrit parfaitement dans cette architecture, et il est la dernière pièce du puzzle.
À bientôt! Et en attendant, découvrez quelques-uns de nos autres articles sur le développement d'applications Android.!