Android架构模式-MVC
介绍
Android项目中,尤其是在比较大的项目开发中,模块内的高聚合和模块间的低耦合性显得尤为重要。所以我们一般情况需要为项目设计一种框架模式,通常情况下我们一般用到的三种模式是MVC、MVP、MVVM。通过框架模式设计的项目能够极大的提高开发效率,提高项目的可维护性和扩展性,另外在模块测试和定位问题上也提供了较大的便利。
本篇主要介绍MVC
M-Model 业务逻辑层;V-View视图层;C-Controller 控制层
mvc示意图.jpg
三者的关系是:user触发事件的时候,View层发送指令到Controller层,接着Controller层通知Model处理逻辑更新数据,
Model层更新完数据直接展示到View层上。
传统的Android开发中,静态布局文件XXX.xml作为View层,各种的JavaBean和Repository 数据库操作 网络层等等都是
Model层,那么Activity就是Controller层。
实战
本次实现使用Kotlin实现
activity_main.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=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/queryBt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Query" />
<Button
android:id="@+id/insertBt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Insert" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.core.widget.ContentLoadingProgressBar
android:id="@+id/progressBar"
android:layout_width="48dp"
android:layout_height="48dp"
style="?android:attr/progressBarStyleLarge"
android:visibility="gone"
android:layout_gravity="center" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/userRv"
android:visibility="invisible"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
</LinearLayout>
数据模型类Student是一个数据类(data class)
data class Student(val name: String, val age: Int, val score: Int) {
companion object {
fun mockData(): MutableList<Student> {
val list = MutableList<Student>(10){
Student("jawe$it", 30 + it, 60 + it * 2)
}
return list
}
}
}
StudentManager是一个model类, 管理Student的信息
class StudentManager private constructor(context:Context) {
private val key = "key_student"
private val sharePreference:SharedPreferences = context.getSharedPreferences("students",Context.MODE_PRIVATE)
companion object{
private var instance:StudentManager? = null
fun getInstance(context: Context):StudentManager{
if (instance == null) {
instance = StudentManager(context)
}
return instance!!
}
}
fun saveStudents(students:List<Student>){
val gsonData = GsonBuilder().create().toJson(students)
val editor = sharePreference.edit()
editor?.putString(key, gsonData)?.apply()
}
fun fetchStudent():List<Student>{
val gson = Gson()
val data = sharePreference.getString(key,null)
var list:List<Student> = listOf()
data?.let {
val resultType = object:TypeToken<List<Student>>(){}.type
list = gson.fromJson(it, resultType)
}
return list
}
}
为了能够展示Student列表还需要一个Adapter
class StudentAdapter(dataList: MutableList<Student>) :
RecyclerView.Adapter<StudentAdapter.StudentViewHolder>() {
val studentList = dataList
class StudentViewHolder(v: View) : RecyclerView.ViewHolder(v) {
val nameTv = v.findViewById<TextView>(R.id.nameTv)
val ageTv = v.findViewById<TextView>(R.id.ageTv)
val scoreTv = v.findViewById<TextView>(R.id.scoreTv)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StudentViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_student, parent, false)
return StudentViewHolder(view)
}
override fun getItemCount(): Int = studentList.size
override fun onBindViewHolder(holder: StudentViewHolder, position: Int) {
val student = studentList[position]
holder.nameTv.text = student.name
val age = "年龄是:${student.age}"
holder.ageTv.text = age
holder.scoreTv.text = "成绩是:${student.score}"
}
}
item_student的布局文件是
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical">
<TextView
android:id="@+id/nameTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
tools:text="jawe"/>
<TextView
android:id="@+id/ageTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
tools:text="27"/>
<TextView
android:id="@+id/scoreTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
tools:text="66"/>
</LinearLayout>
最重要的Controller的工作都是在MainActivity中完成的
class MainActivity : AppCompatActivity() {
var queryBt: Button? = null
var insertBt: Button? = null
var progressBar: ContentLoadingProgressBar? = null
var userRv: RecyclerView? = null
val studentManager by lazy {
StudentManager.getInstance(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
queryBt = findViewById(R.id.queryBt)
insertBt = findViewById(R.id.insertBt)
progressBar = findViewById(R.id.progressBar)
userRv = findViewById(R.id.userRv)
val layoutManager = LinearLayoutManager(this)
userRv?.layoutManager = layoutManager
initListener()
}
private fun initListener(){
queryBt?.setOnClickListener {
//显示进度条
showLoading()
thread {
var fetchStudents = studentManager.fetchStudent()
if (fetchStudents.isEmpty()) {
fetchStudents = Student.mockData().toList()
}
//模拟耗时操作
SystemClock.sleep(1000)
runOnUiThread {
hideLoading()
val adapter = StudentAdapter(fetchStudents.toMutableList())
userRv?.adapter = adapter
}
}
}
insertBt?.setOnClickListener {
toast(this, "保存中...")
thread {
studentManager.saveStudents(Student.mockData())
//模拟耗时操作
SystemClock.sleep(1000)
runOnUiThread{
toast(this, "保存成功")
}
}
}
}
private fun showLoading(){
progressBar?.show()
progressBar?.visibility = View.VISIBLE
userRv?.visibility = View.GONE
}
private fun hideLoading(){
progressBar?.hide()
progressBar?.visibility = View.GONE
userRv?.visibility = View.VISIBLE
}
}
总结
通过该列子我们会发现,Activity在充当Controller的角色的时候,内部更多的是处理View的逻辑,比如showLoading和hideLoading函数。而且这里的加载数据和保存数据都需要子线程中保存,处理不当会造成内存泄漏(Activity对象无法被回收掉)
所以这种模式的缺点是 :
1.View层的控制能力太弱了,如果要更改图片的颜色,隐藏/显示一个view控件,这些ui视图逻辑都要放到Activity中,这样就
会造成Activity不仅是Controller层,同时还担任了View的任务忙,这违反软件设计的单一职责。
2.View层和Model层是紧耦合的,这样代码的毫无扩展性和复用性,而且单元测试和后期维护成本都比较大
3.Activity充当Controller层,那么一些耗时的操作就要在这里边处理,耗时任务处理不当就会造成内存泄漏,最终导致内存
溢出。