Android架构模式-MVC

2020-04-16  本文已影响0人  烧伤的火柴

介绍

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层,那么一些耗时的操作就要在这里边处理,耗时任务处理不当就会造成内存泄漏,最终导致内存
溢出。

上一篇下一篇

猜你喜欢

热点阅读