【Android Studio】图书管理系统——功能实现原理
使用框架
采用了MVVM框架,即Model View ViewModel模式
Model:接收后端发送的实体数据
ViewModel:处理Model的数据并发送给View
View:展示处理好的数据

项目结构

具体实例
逻辑层
在ReaderClientApplication.kt文件中定义token以及一个全局context
class ReaderClientApplication: Application() {
var TOKEN = ""
companion object{
@SuppressLint("StaticFieldLeak")
lateinit var context: Context
}
override fun onCreate() {
super.onCreate()
context = applicationContext
TOKEN = TOKEN
}
}
同时还需要在AndroidManifest.xml中注册
<application
android:name=".ReaderClientApplication"
android:allowBackup="true"
android:icon="@drawable/logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
.......
</application>
根据返回的json格式,定义数据模型
model中的UserResponse.kt
data class UserResponse (val code: String, val data: UserData, val msg: String) data class UserData(val admin: Int, val borrowCount: Int, val major: String, val password: String, val sno: String, val username: String) data class User(val sno: String, val password: String)
定义访问后端提供的API的接口
network中的BookService
interface BookService {
@POST("user/login")
fun loginValidate(@Body user: User ): Call<UserResponse>
......//其它接口
}
创建Retrofit构建器
network中的ServiceCreator
package com.example.readerclient.logic.network
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
object ServiceCreator {
//后端服务基础地址
private const val BASE_URL = "http://172.27.183.4:8080/aissm_war_exploded/"
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)
}
定义统一网络数据源 访问入口
object ReaderClientNetwork {
private val BookService = ServiceCreator.create(BookService::class.java)
suspend fun loginValidate(sno: String, password: String) = BookService.loginValidate(User(sno, password)).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)
}
})
}
}
}
创建仓库层的统一封装入口
Repository单例类
object Repository {
fun loginValidate(sno: String, password: String) = liveData(Dispatchers.IO){
//var userResponse = UserResponse("", UserData(0, 0 ,"", "", "", ""), "")
val result = try{
val userResponse = ReaderClientNetwork.loginValidate(sno, password)
if(userResponse.code == "0") {
Result.success(userResponse)
}else{
Result.failure(RuntimeException("Response status is ${userResponse.code}, msg: ${userResponse.msg}"))
}
}catch(e: Exception){
Result.failure<List<String>>(e)
}
//Log.d("userResponse code is:", result.toString())
//Log.d("userResponse code is:", userResponse.toString())
emit(result)
}
......//其它方法
}
定义ViewModel层
ui/login中的UserViewModel
class UserViewModel: ViewModel() {
private val loginLiveData = MutableLiveData<User>()
val userLiveData = Transformations.switchMap(loginLiveData){
user -> Repository.loginValidate(user.sno, user.password)
}
fun loginValidate(sno: String, password: String){
loginLiveData.value = User(sno, password)
}
}
UI层
创建activity_login.xml布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.login.LoginActivity">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:paddingTop="50dp"
android:paddingBottom="50dp"
app:srcCompat="@drawable/logo" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/account"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="账号" />
<EditText
android:id="@+id/accountEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:inputType="textPersonName" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码" />
<EditText
android:id="@+id/passwordEdit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:ems="10"
android:inputType="textPassword" />
</LinearLayout>
<Button
android:id="@+id/loginBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录" />
</LinearLayout>
创建LoginActivity文件
class LoginActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
val viewModel by lazy{ ViewModelProviders.of(this).get(UserViewModel::class.java)}
val prefs = getSharedPreferences("data", Context.MODE_PRIVATE)
var today = Calendar.getInstance().get(Calendar.DAY_OF_YEAR);
var lastday = prefs.getInt("lastday", today)
var attempt = prefs.getInt("attempt", 0)
accountEdit.setText(prefs.getString("account", ""))
//Toast.makeText(this, "${today} ${lastday}", Toast.LENGTH_SHORT).show()
//判断今日密码输入次数
if(today == lastday)
{
if( attempt >= 100) {
Toast.makeText(this, "已达今天最大尝试次数,请再重试", Toast.LENGTH_SHORT).show()
this.finish()
}
}else{
attempt = 0
lastday = today
}
var flag = 1
//登录按钮
loginBtn.setOnClickListener(){
val account = accountEdit.text.toString()
val password = passwordEdit.text.toString()
val editor = prefs.edit()
val intent = Intent(this, MainActivity::class.java)
var userResponse : UserResponse
editor.putInt("lastday", lastday)
viewModel.loginValidate(account, password)
viewModel.userLiveData.observe(this, Observer{
result ->
if(result.getOrNull() != null){
userResponse = result.getOrNull() as UserResponse
Log.d("testing",result.toString())
Log.d("testing",userResponse.toString())
editor.putString("account", account)
if(userResponse.code.toInt() == 0){
editor.putString("token", userResponse.msg)
val user = userResponse.data
if(password == user.password) {
editor.putString("name", user.username)
editor.putString("sno", user.sno)
editor.putString("major", user.major)
editor.apply()
this.finish()
startActivity(intent)
}
}
else{
Toast.makeText(this, userResponse.msg, Toast.LENGTH_SHORT).show()
}
}
})
}
}
}
运行结果


浙公网安备 33010602011771号