介绍协程+Retrofit+ViewModel+LiveData+DataBinding的框架搭建,实现从网络请求api接口功能,相关demo
一、框架搭建
整体项目结构如下
1.1、环境、依赖库配置
新建项目,在app的build.gradle中添加依赖
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.0-RC-native-mt'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.0-RC-native-mt'
implementation "androidx.activity:activity-ktx:1.1.0"
因为涉及到http网络请求,配置manifest权限
<uses-permission android:name="android.permission.INTERNET" />
application节点下的http权限
android:networkSecurityConfig="@xml/network_security_config"
xml -> network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
app的build.gradle中配置databinding
android {
...
dataBinding {
enabled = true
}
}
gradle.properties中配置debug包运行
android.injected.testOnly=false
1.2、创建api网络请求
新建UserApi.kt,这里简单实现数据解析的User实体类,服务器返回json
{
"name": "jason",
"address": "California"
}
data class User(val name: String, val address: String)
val userServiceApi: UserServiceApi by lazy {
val retrofit = retrofit2.Retrofit.Builder()
.client(OkHttpClient.Builder().addInterceptor {
it.proceed(it.request()).apply {
Log.d("jason", "request:${code()}")
}
}.build())
.baseUrl("http://81.68.175.49:8080/kotlinstudyserver/")
.addConverterFactory(MoshiConverterFactory.create())
.build()
retrofit.create(UserServiceApi::class.java)
}
interface UserServiceApi {
@GET("user")
fun loadUser(@Query("name") name: String) : Call<User>
@GET("user")
suspend fun getUser(@Query("name") name: String) : User
}
1.3、创建viewmodel
创建MainViewModel继承自ViewModel()
class MainViewModel: ViewModel() {
}
根据google官方指南,我们是在ViewModel中通过repository去请求网络、数据库的数据
因此这里还需要新建UserRepository
class UserRepository {
suspend fun getUser(name: String): User {
return userServiceApi.getUser(name)
}
}
因为这里继承自ViewModel,所以这里带了viewModelScope
MainViewModel部分代码:
class MainViewModel: ViewModel() {
val userLiveData = MutableLiveData<User>()
private val userRepository = UserRepository()
fun getUser(name: String) {
viewModelScope.launch {
userLiveData.value = userRepository.getUser(name)
}
}
}
1.4、加载数据
activity_main.xml中绘制mvvm绑定视图
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="com.example.retrofitviewmodellivedatadatabinding.viewmodel.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/submitButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="104dp"
android:text="提交"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/guideline" />
<TextView
android:id="@+id/nameTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.userLiveData.name}"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@+id/guideline"
app:layout_constraintHorizontal_bias="0.484"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.681" />
<androidx.constraintlayout.widget.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintGuide_percent="0.5" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
activity中加载数据set到布局上
class MainActivity : AppCompatActivity() {
private val mainViewModel: MainViewModel by viewModels()
@SuppressLint("StaticFieldLeak")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding =
DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
binding.viewModel = mainViewModel
binding.lifecycleOwner = this
binding.submitButton.setOnClickListener {
mainViewModel.getUser("xxx")
}
}
}
二、设计好在哪里
2.1、viewModel解决Activity很可能会被销毁重建,页面的状态将会丢失
如下图所示页面旋转时,CoroutineDemo数据销毁了,而使用了ViewModel的数据仍然存在
2.2、协程解决了回调地狱的问题
在网络请求中,协程将异步任务同步化,优化了代码可读性
|