viewModel观察retrofit数据

apache设置了一个简单的字段, {"name":"this is new name"}
viewModel观察retrofit获取的数据,更新给textView

img


MainActivity.kt

class MainActivity : AppCompatActivity() {

    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        viewModel = ViewModelProvider(this)[MainViewModel::class.java]

        val textView: TextView = findViewById(R.id.textView)
        viewModel.counter.observe(this) { current ->
            textView.text = current
        }

        val btn: Button = findViewById(R.id.btn)
        btn.setOnClickListener {
            val intent = Intent(this, SecondActivity::class.java)
            startActivity(intent)
        }
}

MainViewModel.kt

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.switchMap
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response

class MainViewModel : ViewModel() {
    val counter: LiveData<String>
        get() = _counter

    private val _counter = MutableLiveData<String>()
   
    init {
        val appService = ServiceCreator.create<AppService>()
        appService.getAppData().enqueue(object : Callback<App> {
            override fun onResponse(call: Call<App>, response: Response<App>) {
                val ponSe = response.body()
                if (ponSe != null) {
                    _counter.value = ponSe.name
                }
            }

            override fun onFailure(call: Call<App>, t: Throwable) {
                t.printStackTrace()
            }
        })
    }
}

问题①,使用协程简化回调,按书本方法不对,该如何正确使用.

class MainViewModel : ViewModel() {
    val counter: LiveData<String>
        get() = _counter

    private val _counter = MutableLiveData<String>()
    
    private suspend fun <T> Call<T>.await(): T {
        return suspendCoroutine { continuation ->
            enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    val body = response.body()
                    if (body != null) continuation.resume(body)
                    else continuation.resumeWithException(
                        RuntimeException("response body is null")
                    )
                }

                override fun onFailure(call: Call<T>, t: Throwable) {
                    continuation.resumeWithException(t)
                }
            })
        }
    }
}

img


方法灰色

img


直接引用也不行

img


问题②,按书本的MVVM架构继续搭建,到了后两步按书大致意思修改但没写对,

需要详细更改代码参考学习,

定义数据模型 App.kt

class App (val name: String)

访问接口 AppService.kt

import retrofit2.Call
import retrofit2.http.GET

interface AppService {
    @GET("get_data.json")
    fun getAppData(): Call<App>
}

retrofit构建器 ServiceCreator.kt

import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory

object ServiceCreator {
    private const val BASE_URL = "http://10.0.2.2"

    private val retrofit = Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    fun <T> create(serviceClass: Class<T>): T = retrofit.create(serviceClass)
    inline fun <reified T> create(): T = create(T::class.java)
}

定义统一的数据源访问入口,AppNetwork.kt

import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import retrofit2.await
import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine

object AppNetwork {
    private val appService = ServiceCreator.create<AppService>()

    suspend fun searchApp() = appService.getAppData().await()

    private suspend fun <T> Call<T>.await(): T {
        return suspendCoroutine { continuation ->
            enqueue(object : Callback<T> {
                override fun onResponse(call: Call<T>, response: Response<T>) {
                    val body = response.body()
                    if (body != null) continuation.resume(body)
                    else continuation.resumeWithException(
                        RuntimeException("response body is null")
                    )
                }

                override fun onFailure(call: Call<T>, t: Throwable) {
                    continuation.resumeWithException(t)
                }
            })
        }
    }
}

定义仓库层的统一封装,我的数据只有一个参数,书本的比较多,这里不知道改的对不对,

img


Repository.kt

import androidx.lifecycle.liveData
import kotlinx.coroutines.Dispatchers

object Repository {
    fun searchApp(query: @JvmSuppressWildcards String) = liveData(Dispatchers.IO) {
        val result = try {
            val searchResponse = AppNetwork.searchApp()
            Result.success(searchResponse)
        } catch (e: Exception) {
            Result.failure<App>(e)
        }
        emit(result)
    }
}

书本viewModel的逻辑

img


MainViewModel.kt,这里的逻辑没写对

img


MativityAtivity也没正常引用

img

书上定义的suspend 修饰的挂起函数的调用需要在协程作用域中才可以调用。具体原因是kotlin 协程,suspend 修饰的函数,反编译之后会在函数最后一位增加一个continue 参数。普通函数无法调用suspend修饰的挂起函数

1.可以添加ViewModleScope 生命周期感知组件依赖。这样的话就可以

  fun initData() {
        viewModelScope.launch {
           
            _xxxx.value =  getAppData()
        }
    }

将 Kotlin 协程与生命周期感知型组件一起使用  |  Android 开发者  |  Android Developers

Kotlin的协程能够大大简化传统回调的写法。需要使用suspendCoroutine函数。但是要注意的是suspendCoroutine函数必须在协程作用域或者挂起函数中才能够调用。

在ViewModel中定义一个可观察的字段text,用于保存从网络请求获取到的数据。当数据改变时,通知观察者更新TextView

可以使用suspendCoroutine函数来实现

问题1、简单更改了下方式,小小代码示例:
#在这个示例代码中,await()函数使用了suspendCoroutine()函数来创建一个挂起函数,并将请求加入到线程池中执行。在enqueue()函数中,创建了一个Callback对象来处理请求的响应或失败情况,并通过continuation.resume()或continuation.resumeWithException()函数将结果返回给调用方。

class MainViewModel : ViewModel() {  
    val counter: LiveData<String>  
        get() = _counter  
   
    private val _counter = MutableLiveData<String>()  
      
    private fun <T> Call<T>.await(): T {  
        return suspendCoroutine { continuation ->  
            enqueue(object : Callback<T> {  
                override fun onResponse(call: Call<T>, response: Response<T>) {  
                    val body = response.body()  
                    if (body != null) continuation.resume(body)  
                    else continuation.resumeWithException(  
                        RuntimeException("response body is null")  
                    )  
                }  
   
                override fun onFailure(call: Call<T>, t: Throwable) {  
                    continuation.resumeWithException(t)  
                }  
            })  
        }  
    }  
}