第一步:添加retrofit依赖,版本要求2.6.0+,支持协程
//添加retrofit依赖,版本要求2.6.0+,支持协程
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
第二步:添加 lifecycle viewmodel 依赖
//添加 lifecycle viewmodel 依赖
def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
第三步:创建ApiService 与实体类
/**
*com.example.kt_cf_project.api
*Created by LuZhangHui on2021/9/27 16:18
*describe:
*18
*/
interface ApiService {
companion object{
val BASE_URL = "http://192.168.31.67:8080/ssm/ws/commRest/"
}
//suspend关键字修饰方法,表示这是一个挂起函数,可以在协程中使用,然后返回可以直接返回我们想要的实体类
这个功能只能在Retrofit 2.6以后的版本中使用
@GET("login/{login_id}/{password}")
suspend fun doLogin(@Path("login_id") login_id:String, @Path("password") password : String = "123456") : ResultData<User>
}
data class ResultData<T>(
val success : Boolean,
val result : Int,
val message : String,
val user: T) {
companion object{
const val CODE_SUCCESS = 0
}
fun apiData() : T{
if (result == CODE_SUCCESS) {
return user
}else{
throw (ApiException(result,message))
}
}
}
/**
*com.example.kt_cf_project.mode
*Created by LuZhangHui on2021/9/27 15:52
*describe:
*52
*/
data class User(
val dept_code: String,
val depts: List<Dept>,
val login_id: String,
val name: String,
val passwd: String,
val user_code: String,
val wards: List<Any>
)
data class Dept(
val dept_code: String,
val dept_name: String
)
第四步: 创建Retrofit ,以及提供service服务
/**
*com.example.kt_cf_project.api
*Created by LuZhangHui on2021/9/27 16:35
*describe: 创建Retrofit ,以及提供service服务 devices
*35
*/
object RetrofitClient {
val okHttpClient = OkHttpClient.Builder()
.callTimeout(30,TimeUnit.SECONDS)
.build()
val retrofit = retrofit2.Retrofit.Builder()
.baseUrl(ApiService.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClient)
.build()
val apiService = retrofit.create(ApiService::class.java)
}
第五步:创建viewModel,执行网络请求
package com.example.kt_cf_project.viewMode
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.kt_cf_project.api.RetrofitClient
import com.example.kt_cf_project.mode.User
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import retrofit2.Response
/**
*com.example.kt_cf_project.viewMode
*Created by LuZhangHui on2021/9/27 16:09
*describe:
*09
*/
class UserViewMode : ViewModel() {
val apiData =MutableLiveData<User>()
val doLoginRepostory by lazy {
DoLoginRepostory()
}
fun dologin(account:String, password:String){
// 2021/9/27 进行网络请求,登录操作
//GlobalScope是一个顶级的协程,作用域是全局的,无法提早取消。使用的时候最好使用ViewModel,LiveData,和ViewModel的扩展viewModelScope来完成网络请求
/*GlobalScope.launch {
val result = withContext(Dispatchers.IO) { RetrofitClient.apiService.doLogin(account, password).apiData() }
}*/
/**ViewModel的扩展viewModelScope
* viewModelScope 是官方提供的ViewModel的扩展,继承CoroutineScope,CoroutineScope字面意思协程作用域,
* 它会跟踪所有它所创建的协程, 当当前的ViewModel结束的时候,它所执行的异步任务也需要结束,防止内存泄露,
* 之前我们需要在ViewModel的onCleared方法中通过SupervisorJob的cancel方法来销毁,使用viewModelScope可以简化这个操作,
* 它会自动调用ViewModel的onCleared取消当前操作
*/
viewModelScope.launch {
withContext(Dispatchers.IO){
try {
// val doLogin = doLoginRepostory.doLogin(account, password)
apiData.postValue(RetrofitClient.apiService.doLogin(account,password).apiData())
}catch (e:Exception){
e.printStackTrace()
}
}
}
/* val doLogin = doLoginRepostory.doLogin(account, password)
doLogin.enqueue(object : Callback, retrofit2.Callback> {
*/ /**
* Invoked for a received HTTP response.
*
*
* Note: An HTTP response may still indicate an application-level failure such as a 404 or 500.
* Call [Response.isSuccessful] to determine if the response indicates success.
*//*
override fun onResponse(call: Call<ResultData<User>>, response: Response<ResultData<User>>) {
apiData.postValue(response?.body()?.apiData())
}
*//**
* Invoked when a network exception occurred talking to the server or when an unexpected exception
* occurred creating the request or processing the response.
*//*
override fun onFailure(call: Call<ResultData<User>>, t: Throwable) {
}
})*/
}
}
第六步:在activity中调用及获取数据
package com.example.kt_cf_project.ui.activity
import android.content.Intent
import android.os.Bundle
import android.text.Editable
import android.text.TextUtils
import android.text.TextWatcher
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import com.example.kt_cf_project.R
import com.example.kt_cf_project.base.BaseActivity
import com.example.kt_cf_project.databinding.ActivityLoginBinding
import com.example.kt_cf_project.viewMode.UserViewMode
import com.google.gson.Gson
class LoginActivity : BaseActivity<ActivityLoginBinding>() {
val userViewMode by lazy {
ViewModelProvider(this).get(UserViewMode::class.java)
}
var isClickable = false //登录按钮是否可以点击
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initView()
initEvent()
}
private fun initView() {
userViewMode.run {
apiData.observe(this@LoginActivity, Observer {
logd("userinfo-->"+Gson().toJson(it))
if (it != null) {
startActivity(Intent(this@LoginActivity,MainActivity::class.java))
}else{
toast("登录失败,请稍后重试!")
}
})
}
binding.emailSignInButton.setOnClickListener {
userViewMode.dologin(
account = binding.email.text.toString(),
password = binding.password.text.toString()
)
}
binding.email.addTextChangedListener(object : TextWatcher{
override fun afterTextChanged(s: Editable?) {
logd("after-->${s.toString()}")
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
logd("before-->${s.toString()}")
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
logd("onTextChanged-->${s.toString()}")
//判断是否有内容 控制登录btn的可点击状态
isLoginOk(!TextUtils.isEmpty(s) && !TextUtils.isEmpty(binding.password.text))
}
})
binding.password.addTextChangedListener (object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
//判断是否有内容 控制登录btn的可点击状态
isLoginOk(!TextUtils.isEmpty(s) && !TextUtils.isEmpty(binding.email.text))
}
})
}
private fun initEvent() {
}
fun isLoginOk(isOk: Boolean) {
logd("isok$isOk")
if (isClickable == isOk){
return
}
isClickable = isOk
if (isClickable) {
binding.emailSignInButton.isClickable = true
binding.emailSignInButton.setBackgroundResource(R.drawable.selector_btn_blue_bg)
} else {
binding.emailSignInButton.isClickable = false
binding.emailSignInButton.setBackgroundResource(R.drawable.login_btn_unable)
}
}
}