Sans longues introductions, je vais vous expliquer comment organiser rapidement et facilement une architecture pratique de votre application. Le matériel sera utile à ceux qui ne sont pas très familiers avec le modèle mvvm et les coroutines Kotlin.
Nous avons donc une tâche simple: recevoir et traiter une demande réseau, afficher le résultat dans une vue.
Nos actions: à partir de l'activité (fragment), nous appelons la méthode souhaitée ViewModel -> ViewModel accède à la poignée de retrofit, exécutant la demande via les coroutines -> la réponse est envoyée aux données en direct comme un événement -> dans l'activité recevant l'événement, nous transférons les données à la vue.
Configuration du projet
Dépendances
Manifeste
<manifest ...> <uses-permission android:name="android.permission.INTERNET" /> </manifest>
Configuration de mise à niveau
Créez un objet Kotlinovsky
NetworkService . Ce sera notre client réseau - Singleton
UPD singleton est utilisé pour faciliter la compréhension. Les commentaires indiquent qu'il est plus approprié d'utiliser l'inversion de contrôle, mais il s'agit d'un sujet distinct.
object NetworkService { private const val BASE_URL = " http://www.mocky.io/v2/"
Interface api
Nous utilisons des demandes verrouillées au faux service.
Arrêtez le plaisir, ici commence la magie de la corutine.
Nous marquons nos fonctions avec le mot-clé
suspend fun ...La modification a appris à fonctionner avec les fonctions de suspension de Kotlin à partir de la version 2.6.0, maintenant elle exécute directement une demande réseau et renvoie un objet avec des données:
interface Api { @GET("5dcc12d554000064009c20fc") suspend fun getUsers( @Query("page") page: Int ): ResponseWrapper<Users> @GET("5dcc147154000059009c2104") suspend fun getUsersError( @Query("page") page: Int ): ResponseWrapper<Users> }
ResponseWrapper est une classe wrapper simple pour nos requêtes réseau:
class ResponseWrapper<T> : Serializable { @SerializedName("response") val data: T? = null @SerializedName("error") val error: Error? = null }
Utilisateurs de la classe de date
data class Users( @SerializedName("count") var count: Int?, @SerializedName("items") var items: List<Item?>? ) { data class Item( @SerializedName("first_name") var firstName: String?, @SerializedName("last_name") var lastName: String? ) }
ViewModel
Nous créons une classe
BaseViewModel abstraite dont tous nos ViewModel seront hérités. Ici, nous nous attardons plus en détail:
abstract class BaseViewModel : ViewModel() { var api: Api = NetworkService.retrofitService()
Les événements
Une solution intéressante de Google consiste à encapsuler les classes de date dans une classe wrapper d'événement dans laquelle nous pouvons avoir plusieurs états, généralement LOADING, SUCCESS et ERROR.
data class Event<out T>(val status: Status, val data: T?, val error: Error?) { companion object { fun <T> loading(): Event<T> { return Event(Status.LOADING, null, null) } fun <T> success(data: T?): Event<T> { return Event(Status.SUCCESS, data, null) } fun <T> error(error: Error?): Event<T> { return Event(Status.ERROR, null, error) } } } enum class Status { SUCCESS, ERROR, LOADING }
Voici comment cela fonctionne. Lors d'une demande de réseau, nous créons un événement avec le statut CHARGEMENT. Nous attendons une réponse du serveur, puis encapsulons les données avec l'événement et envoyons-les avec le statut spécifié. Dans la vue, nous vérifions le type d'événement et, selon l'état, définissons différents états pour la vue. Le modèle d'architecture MVI est basé sur la même philosophie.
ActivityViewModel
class ActivityViewModel : BaseViewModel() {
Et enfin
Mainactivité
class MainActivity : AppCompatActivity() { private lateinit var activityViewModel: ActivityViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) activityViewModel = ViewModelProviders.of(this).get(ActivityViewModel::class.java) observeGetPosts() buttonOneClickListener() buttonTwoClickListener() }
Code source