Android MVVM架构Demo(kotlin+databinding+livedata+lifecycle+协程+retrofit)
程序员文章站
2022-03-01 17:34:08
MVVM优势主要解决的问题:解决了生命周期问题导致的内存解决了mvp中的大量接口MVVM的分层View层就是展示数据的,以及接收到用户的操作传递给viewModel层,通过dataBinding实现数据与view的单向绑定或双向绑定Model层最重要的作用就是获取数据。(由于使用了协程所以不需要通过接口回调数据)ViewModel 层通过调用model层获取数据,以及业务逻辑的处理。MVVM中 viewModel 和MVP中的presenter 的作用类似 ,只不过是通过 datab....
MVVM优势
主要解决的问题:
- 解耦
- 解决了生命周期问题导致的内存泄漏
- 解决了mvp中的大量接口
MVVM的分层
- View层就是展示数据的,以及接收到用户的操作传递给viewModel层,通过dataBinding实现数据与view的单向绑定或双向绑定
- Model层最重要的作用就是获取数据。(由于使用了协程所以不需要通过接口回调数据)
- ViewModel 层通过调用model层获取数据,以及业务逻辑的处理。
- MVVM中 viewModel 和MVP中的presenter 的作用类似 ,只不过是通过 databinding 将数据与ui进行了绑定。livedata用来通知数据的更新。
V层(activity+xml布局,主要负责UI的显示和交互)
class Main3Activity : AppCompatActivity() {
companion object {
@JvmStatic
fun start(context: Context) {
val starter = Intent(context, Main3Activity::class.java)
context.startActivity(starter)
}
}
private lateinit var viewModel: Main3ViewModel
private lateinit var binding: ActivityMain3Binding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application)).get(Main3ViewModel::class.java)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main3)
binding.lifecycleOwner = this
binding.viewModel = viewModel
lifecycleScope.launch {
viewModel.searchArticle2()
}
}
fun onClick(view: View) {
when (view) {
binding.tvTest -> {
lifecycleScope.launch { viewModel.searchArticle2() }
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.zhangyu.myjetpack.vm.Main3ViewModel" />
</data>
<TextView
android:id="@+id/tv_test"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:onClick="onClick"
android:text="@{viewModel.liveData.results.get(0).url}" />
</layout>
VM层(从M层获取数据,处理业务逻辑)
import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.zhangyu.myjetpack.bean.Article
import com.zhangyu.myjetpack.data.ArticleRepository
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
private const val TAG = "Main3ViewModel"
class Main3ViewModel : ViewModel() {
private val repository = ArticleRepository()
var liveData: MutableLiveData<Article> = MutableLiveData()
//协程,同步方式
suspend fun searchArticle2() {
liveData.value = repository.searchRandomData2()
Log.d(TAG, "searchArticle2: " + liveData.value?.results)
}
//线程,异步方式,通过接口回调数据
fun searchArticle3() {
repository.searchRandomData3().enqueue(object : Callback<Article> {
override fun onFailure(call: Call<Article>, t: Throwable) {
Log.e(TAG, "onFailure: " + t.message)
}
override fun onResponse(call: Call<Article>, response: Response<Article>) {
liveData.value = response.body()
}
})
}
}
M层(获取数据)
private const val TAG = "ArticleRepository"
class ArticleRepository {
//协程+Retrofit,同步方式
suspend fun searchRandomData2(): Article {
return try {
RetrofitUtil.provide(ArticleService::class.java).getRandomArticle2("福利", "10")
} catch (e: Exception) {
Log.e(TAG, "searchRandomData2: " + e.message)
Article(true, null)//databinding自带空检查所以可以null
}
}
//线程+Retrofit,异步
fun searchRandomData3(): Call<Article> {
return RetrofitUtil.provide(ArticleService::class.java).getRandomArticle3("福利", "10")
}
}
interface ArticleService {
/**
* 数据类型:福利 | Android | iOS | 休息视频 | 拓展资源 | 前端
* 个数: 数字,大于0
*/
//协程+Retrofit,同步方式
@GET("api/random/data/{type}/{size}")
suspend fun getRandomArticle2(@Path("type") type: String?, @Path("size") size: String?): Article
//线程+Retrofit,异步
@GET("api/random/data/{type}/{size}")
fun getRandomArticle3(@Path("type") type: String?, @Path("size") size: String?): Call<Article>
}
package com.zhangyu.myjetpack.bean
data class Article(
val error: Boolean,
val results: List<Result>?
)
data class Result(
val _id: String,
val createdAt: String,
val desc: String,
val publishedAt: String,
val source: String,
val type: String,
val url: String,
val used: Boolean,
val who: String
)
网络库的封装
import android.util.ArrayMap
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object RetrofitUtil {
const val BASE_URL = "https://gank.io/"
private fun getRetrofit(): Retrofit {
val logger = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BASIC }
val client = OkHttpClient.Builder()
.addInterceptor(logger)
.build()
return Retrofit.Builder()
.client(client)
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
private val mApis = ArrayMap<String, Any>()
@Suppress("UNCHECKED_CAST")
fun <T> provide(apiInterfaceClass: Class<T>): T {
val api = mApis[apiInterfaceClass.name] as T ?: getRetrofit().create(apiInterfaceClass)
mApis[apiInterfaceClass.name] = api
return api
}
}
Gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
buildFeatures {
dataBinding = true
// for view binding :
viewBinding = true
}
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
// work-runtime-ktx 2.1.0 and above now requires Java 8
jvmTarget = "1.8"
// Enable Coroutines and Flow APIs
freeCompilerArgs += "-Xopt-in=kotlinx.coroutines.ExperimentalCoroutinesApi"
freeCompilerArgs += "-Xopt-in=kotlinx.coroutines.FlowPreview"
}
}
dependencies {
...
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.7.2'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
}
本文地址:https://blog.csdn.net/yu540135101/article/details/110628935