通过一堆ViewModel + LiveData,Retrofit + Coroutines在Android应用程序中组织简单架构

无需过多介绍,我将告诉您如何快速轻松地组织应用程序的便捷体系结构。 对于不熟悉mvvm模式和Kotlin协程的人,该材料将很有用。

因此,我们有一个简单的任务:接收和处理网络请求,并在视图中显示结果。

我们的动作:从活动(片段)中调用所需的方法ViewModel-> ViewModel访问改造句柄,通过协程执行请求->将响应作为事件发送到实时数据->在活动中,接收将数据传输到视图的事件。

项目设置


依存关系


//Retrofit implementation 'com.squareup.retrofit2:retrofit:2.6.2' implementation 'com.squareup.retrofit2:converter-gson:2.6.2' implementation 'com.squareup.okhttp3:logging-interceptor:4.2.1' //Coroutines implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0' //ViewModel lifecycle implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0' implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0-rc01" 

清单


 <manifest ...> <uses-permission android:name="android.permission.INTERNET" /> </manifest> 

改造设置


创建一个Kotlinovsky NetworkService对象。 这将是我们的网络客户端-单身人士
UPD单例用于易于理解。 评论指出使用控制反转更合适,但这是一个单独的主题。

 object NetworkService { private const val BASE_URL = " http://www.mocky.io/v2/" // HttpLoggingInterceptor       private val loggingInterceptor = run { val httpLoggingInterceptor = HttpLoggingInterceptor() httpLoggingInterceptor.apply { httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY } } private val baseInterceptor: Interceptor = invoke { chain -> val newUrl = chain .request() .url .newBuilder() .build() val request = chain .request() .newBuilder() .url(newUrl) .build() return@invoke chain.proceed(request) } private val client: OkHttpClient = OkHttpClient .Builder() .addInterceptor(loggingInterceptor) .addInterceptor(baseInterceptor) .build() fun retrofitService(): Api { return Retrofit.Builder() .baseUrl(BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .client(client) .build() .create(Api::class.java) } } 

api界面


我们对假服务使用锁定的请求。

暂停乐趣,从这里开始corutin的魔力。

我们用关键字suspend fun ...标记我们的功能。

从2.6.0版开始,学习到了与Kotlin暂停功能一起使用的改造,现在,它可以直接执行网络请求并返回带有数据的对象:

 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是用于我们的网络请求的简单包装器类:

 class ResponseWrapper<T> : Serializable { @SerializedName("response") val data: T? = null @SerializedName("error") val error: Error? = null } 

日期类用户

 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? ) } 

视图模型


我们创建一个抽象BaseViewModel类,所有ViewModel都将从该类继承。 在这里我们更详细地介绍:

 abstract class BaseViewModel : ViewModel() { var api: Api = NetworkService.retrofitService() //       requestWithLiveData  // requestWithCallback,       //           // .       suspend , //             //    .      fun <T> requestWithLiveData( liveData: MutableLiveData<Event<T>>, request: suspend () -> ResponseWrapper<T>) { //        liveData.postValue(Event.loading()) //     ViewModel,  viewModelScope. //        //    . //   IO     this.viewModelScope.launch(Dispatchers.IO) { try { val response = request.invoke() if (response.data != null) { //     postValue  IO  liveData.postValue(Event.success(response.data)) } else if (response.error != null) { liveData.postValue(Event.error(response.error)) } } catch (e: Exception) { e.printStackTrace() liveData.postValue(Event.error(null)) } } } fun <T> requestWithCallback( request: suspend () -> ResponseWrapper<T>, response: (Event<T>) -> Unit) { response(Event.loading()) this.viewModelScope.launch(Dispatchers.IO) { try { val res = request.invoke() //   ,    //       ,  //    //    //  context  launch(Dispatchers.Main) { if (res.data != null) { response(Event.success(res.data)) } else if (res.error != null) { response(Event.error(res.error)) } } } catch (e: Exception) { e.printStackTrace() // UPD (  )   catch     Main  launch(Dispatchers.Main) { response(Event.error(null)) } } } } } 

大事记


Google的一个很酷的解决方案是将日期类包装在Event wrapper类中,在其中可以有几种状态,通常是LOADING,SUCCESS和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 } 

运作方式如下。 在网络请求期间,我们创建一个状态为LOADING的事件。 我们正在等待服务器的响应,然后将数据包装到事件中,并以指定的状态进一步发送。 在视图中,我们检查事件的类型,并根据状态为视图设置不同的状态。 架构模式MVI基于相同的哲学。

ActivityViewModel


 class ActivityViewModel : BaseViewModel() { //       val simpleLiveData = MutableLiveData<Event<Users>>() //  .    requestWithLiveData //  BaseViewModel     , //          //     api.getUsers //         //    fun getUsers(page: Int) { requestWithLiveData(simpleLiveData) { api.getUsers( page = page ) } } //  ,       // UPD           fun getUsersError(page: Int, callback: (data: Event<Users>) -> Unit) { requestWithCallback({ api.getUsersError( page = page ) }) { callback(it) } } } 

最后

主要活动


 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() } //     //         private fun observeGetPosts() { activityViewModel.simpleLiveData.observe(this, Observer { when (it.status) { Status.LOADING -> viewOneLoading() Status.SUCCESS -> viewOneSuccess(it.data) Status.ERROR -> viewOneError(it.error) } }) } private fun buttonOneClickListener() { btn_test_one.setOnClickListener { activityViewModel.getUsers(page = 1) } } //      ,   private fun buttonTwoClickListener() { btn_test_two.setOnClickListener { activityViewModel.getUsersError(page = 2) { when (it.status) { Status.LOADING -> viewTwoLoading() Status.SUCCESS -> viewTwoSuccess(it.data) Status.ERROR -> viewTwoError(it.error) } } } } private fun viewOneLoading() { //  ,    } private fun viewOneSuccess(data: Users?) { val usersList: MutableList<Users.Item>? = data?.items as MutableList<Users.Item>? usersList?.shuffle() usersList?.let { Toast.makeText(applicationContext, "${it}", Toast.LENGTH_SHORT).show() } } private fun viewOneError(error: Error?) { //   } private fun viewTwoLoading() {} private fun viewTwoSuccess(data: Users?) {} private fun viewTwoError(error: Error?) { error?.let { Toast.makeText(applicationContext, error.errorMsg, Toast.LENGTH_SHORT).show() } } } 

源代码

Source: https://habr.com/ru/post/zh-CN475598/


All Articles