Ohne lange Einführungen erkläre ich Ihnen, wie Sie eine praktische Architektur Ihrer Anwendung schnell und einfach organisieren können. Das Material wird für diejenigen nützlich sein, die mit dem MVVM-Muster und den Kotlin-Coroutinen nicht sehr vertraut sind.
Wir haben also eine einfache Aufgabe: Eine Netzwerkanfrage zu empfangen und zu verarbeiten, das Ergebnis in einer Ansicht anzuzeigen.
Unsere Aktionen: Ausgehend von der Aktivität (Fragment) rufen wir die gewünschte Methode ViewModel auf -> ViewModel greift auf das Retrofit-Handle zu, führt die Anforderung über die Coroutinen aus -> die Antwort wird als Ereignis an die Live-Daten gesendet -> in der Aktivität, die das Ereignis empfängt, übertragen wir die Daten an die Ansicht.
Projekteinrichtung
Abhängigkeiten
Manifest
<manifest ...> <uses-permission android:name="android.permission.INTERNET" /> </manifest>
Nachrüstsetup
Erstellen Sie ein Kotlinovsky
NetworkService- Objekt. Dies wird unser Netzwerk-Client sein - Singleton
Der Einfachheit halber wird UPD-Singleton verwendet. In den Kommentaren wurde darauf hingewiesen, dass die Verwendung der Steuerelementumkehrung besser geeignet ist, dies ist jedoch ein separates Thema.
object NetworkService { private const val BASE_URL = " http://www.mocky.io/v2/"
API-Schnittstelle
Wir verwenden gesperrte Anfragen an den Fake-Service.
Unterbrich den Spaß, hier beginnt die Magie von Corutin.
Wir kennzeichnen unsere Funktionen mit dem Schlüsselwort
suspend fun ....Das Retrofit hat gelernt, mit den Kotlin-Suspend-Funktionen ab Version 2.6.0 zu arbeiten. Jetzt führt es direkt eine Netzwerkanforderung aus und gibt ein Objekt mit Daten zurück:
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 ist eine einfache Wrapper-Klasse für unsere Netzwerkanforderungen:
class ResponseWrapper<T> : Serializable { @SerializedName("response") val data: T? = null @SerializedName("error") val error: Error? = null }
Datumsklasse
Benutzer 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
Wir erstellen eine abstrakte
BaseViewModel- Klasse, von der unser gesamtes ViewModel geerbt wird. Hier verweilen wir im Detail:
abstract class BaseViewModel : ViewModel() { var api: Api = NetworkService.retrofitService()
Ereignis
Eine coole Lösung von Google besteht darin, Datumsklassen in eine Event-Wrapper-Klasse zu packen, in der wir mehrere Status haben können, normalerweise LOADING, SUCCESS und 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 }
So funktioniert es Während einer Netzwerkanfrage erstellen wir ein Ereignis mit dem Status LOADING. Wir warten auf eine Antwort vom Server und verpacken dann die Daten mit dem Ereignis und senden es mit dem angegebenen Status weiter. In der Ansicht überprüfen wir den Ereignistyp und legen je nach Status unterschiedliche Status für die Ansicht fest. Das Architekturmuster MVI basiert auf derselben Philosophie.
ActivityViewModel
class ActivityViewModel : BaseViewModel() {
Und endlich
Hauptaktivität
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() }
Quellcode