【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() } } }) } } }
运行结果