Android 框架

article/2025/9/24 11:10:07

背景

我们有一个需求:我们需要查询用户账号信息,用户输入账号,点击按钮可进行查询账号信息,如果查询数据成功,则将数据展示在界面上;如果查询数据失败,则在界面上提升获取数据失败。
假如说我们不去使用框架来实现这个需求会是什么样子的呢?
无框架
布局文件

<?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="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/userName"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:hint="请输入用户名"android:textSize="14sp"/><Buttonandroid:id="@+id/submit"android:layout_margin="30dp"android:text="提交"android:layout_width="match_parent"android:layout_height="wrap_content" /><TextViewandroid:id="@+id/result"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:textSize="14sp"/>
</LinearLayout>

用户信息实体类
这里我们就直接使用了 Kotlin 中的数据类来进行实现

package com.xjh.queryuser.beandata class Account(var name:String,var level:Int)

回调接口
对于数据请求的成功和失败提供相关接口

package com.xjh.queryuser.callbackimport com.xjh.queryuser.bean.Accountinterface MCallback {fun onSuccess(account:Account)fun onFailed()
}

NormalActivity

package com.xjh.queryuserimport android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.xjh.queryuser.bean.Account
import com.xjh.queryuser.callback.MCallback
import kotlinx.android.synthetic.main.activity_normal.*
import java.util.*class NormalActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal)initEvent()}private fun initEvent() {submit.setOnClickListener {val userInput = getUserInput()getAccountData(userInput, object : MCallback{override fun onSuccess(account: Account) {showSuccessPage(account)}override fun onFailed() {showFailedPage()}})}}// 获取用户输入信息private fun getUserInput(): String {return userName.text.toString()}// 展示获取数据成功的界面private fun showSuccessPage(account: Account) {result.text = "用户账号:" + account.name + " | " + "用户等级:" + account.level}// 展示获取数据失败的界面private fun showFailedPage() {result.text = "获取数据失败"}// 模拟查询账号数据private fun getAccountData(accountName: String, callback: MCallback) {val random = Random()val isSuccess = random.nextBoolean()if (isSuccess) {val account = Account(accountName, 100)callback.onSuccess(account)} else {callback.onFailed()}}
}

这时候我们就会发现所有的功能都是堆积在一个Activity中的,导致Activity中代码的可复用性降低,Activity过于累赘。
而Android的框架就是来解决这些问题的,Android中的框架主要是有传统的MVC模式,以及Android中的MVP以及MVVM模式,那他们分别是什么呢?

MVC

简介

MVC的全名是Model View Controller,即模型(model)- 视图(view)- 控制器(controller)。
MVC
在 Android 中 Controller 就是 Activity、Fragment 等,而 View 则是 layout, view 等控件,最后 Model 则对应的就是一些数据处理的逻辑,比如:网络请求,数据库,文件查询等
主要是这个过程,我们对 View 进行点击会传递到 Controller 中,也就是 Activity 上,然后呢,Controller 就会通知给 Model,Model 对数据进行处理以后将结构返回给 View 进行展现。
比如我们将数据从 Controller 传递到 Model 上,可以让 Controller 持有 Model的引用,而 Model 要向 View 传递数据的话我们一般不会让 Model 持有 View 的引用,而是类似 CallBack 的注册监听的方式进行数据的传递。
然后我们怎么来使用 MVC 模式优化我们之前的代码呢?
我们可以将上面的五条按照 MVC 的规则进行分层,其实 MVC 主要是把数据处理这块的逻辑给放到 Model 层中进行处理。
MVC优化

代码实现

然后我们需要怎么做呢?我们需要将数据的获取与界面的展示分离,以及解决 MVC 各层之间的通信问题。对于前一个问题我们只需要将查询账号数据的功能从 Activity 中分离到 Model 中就可以了,而后者就需要想办法让 Activity 通知 Model 获取数据和 Model 通知 Activity 更新界面。
MVCModel

package com.xjh.queryuser.mvcimport com.xjh.queryuser.bean.Account
import com.xjh.queryuser.callback.MCallback
import java.util.*class MVCModel {// 模拟查询账号数据fun getAccountData(accountName: String, callback: MCallback) {val random = Random()val isSuccess = random.nextBoolean()if (isSuccess) {val account = Account(accountName, 100)callback.onSuccess(account)} else {callback.onFailed()}}
}

然后我们在 Activity 中持有 Model 的引用就可以了
MVCActivity

package com.xjh.queryuser.mvcimport android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.xjh.queryuser.R
import com.xjh.queryuser.bean.Account
import com.xjh.queryuser.callback.MCallback
import kotlinx.android.synthetic.main.activity_normal.*class MVCActivity : AppCompatActivity() {private lateinit var mMVCModel: MVCModeloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal)mMVCModel = MVCModel()initEvent()}private fun initEvent() {submit.setOnClickListener {mMVCModel.getAccountData(getUserInput(), object : MCallback {override fun onSuccess(account: Account) {showSuccessPage(account)}override fun onFailed() {showFailedPage()}})}}// 获取用户输入信息private fun getUserInput(): String {return userName.text.toString()}// 展示获取数据成功的界面private fun showSuccessPage(account: Account) {result.text = "用户账号:" + account.name + " | " + "用户等级:" + account.level}// 展示获取数据失败的界面private fun showFailedPage() {result.text = "获取数据失败"}
}

这样我们就将数据请求的部分代码抽离了出去,通过引用进行调用,让代码更加灵活、干净。

优缺点

优点

一定程度上的实现了 Model 与 View 的分离,降低了代码的耦合性。

缺点

Controller 与 View 难以完全解耦,并且随着项目复杂度的提升,Controller 将会越来越臃肿,Activity 承担了控制器的功能,又要承担部分视图层的工作。
其实 MVC 模式的实现也可以这样进行表示:
MVC
这样就体现出来了 View 和 Controller 的耦合关系。

MVP

简介

MVP 的全称为 Model - View - Presenter 模型,他是将 Model 和 View 隔离开来,两者之间不相互作用,而是通过 Presenter 作为一个中间件进行通信。
MVP
Model 和 MVC 模式中的 Model 的功能是相同的,主要是做一些数据处理的功能,而 View 就是直接对应了 Activity,Fragment 以及 layout 和 view 等控件,Presenter 就是他们俩之间沟通的桥梁。
这样 Activity 的功能就被简化了,不再充当控制器,主要就是负责 View 层面的工作。
这样我们就又可以将业务逻辑处理部分代码从 MVC 中的 Controller 中取出,放入 Presenter 中。
在这里插入图片描述

代码实现

这时候我们就要定义相关的接口,让 Presenter 可以调用 Activity 中实现的方法。
接口定义

package com.xjh.queryuser.mvpimport com.xjh.queryuser.bean.Accountinterface IMVPView {fun getUserInput(): Stringfun showSuccessPage(account: Account)fun showFailedPage()
}

我们的 Activity 实现这个接口
MVPActivity

package com.xjh.queryuser.mvpimport android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.xjh.queryuser.R
import com.xjh.queryuser.bean.Account
import kotlinx.android.synthetic.main.activity_normal.*class MVPActivity : AppCompatActivity(), IMVPView {private lateinit var mMVPPresenter: MVPPresenteroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_normal)mMVPPresenter = MVPPresenter(this)initEvent()}private fun initEvent() {submit.setOnClickListener {mMVPPresenter.getData()}}// 获取用户输入信息override fun getUserInput(): String {return userName.text.toString()}// 展示获取数据成功的界面override fun showSuccessPage(account: Account) {result.text = "用户账号:" + account.name + " | " + "用户等级:" + account.level}// 展示获取数据失败的界面override fun showFailedPage() {result.text = "获取数据失败"}
}

Model 与 MVC 中的 Model 的作用相同,所以代码可以直接复用过来
MVPModel

package com.xjh.queryuser.mvpimport com.xjh.queryuser.bean.Account
import com.xjh.queryuser.callback.MCallback
import java.util.*class MVPModel {// 模拟查询账号数据fun getAccountData(accountName: String, callback: MCallback) {val random = Random()val isSuccess = random.nextBoolean()if (isSuccess) {val account = Account(accountName, 100)callback.onSuccess(account)} else {callback.onFailed()}}
}

Presenter 持有 View 和 Model 的引用,通过 Presenter 我们向 Model 请求相关数据,并根据判断将结果返回 View 上面显示出来
MVPPresenter

package com.xjh.queryuser.mvpimport com.xjh.queryuser.bean.Account
import com.xjh.queryuser.callback.MCallbackclass MVPPresenter {private val mView: IMVPViewprivate val mModel: MVPModelconstructor(mView: IMVPView) {this.mView = mViewmModel = MVPModel()}fun getData() {mModel.getAccountData(mView.getUserInput(), object : MCallback {override fun onSuccess(account: Account) {mView.showSuccessPage(account)}override fun onFailed() {mView.showFailedPage()}})}
}

优缺点

优点

解决了 MVC 中 Controller 与 View 过度耦合的缺点,职责划分明显,更加易于维护。

缺点

接口数量过多,项目复杂的升高。随着项目的复杂度升高, Presenter 层将会越来越臃肿
结合上面所说的优缺点,有几条关于 MVP 的使用建议:

  1. 接口规范化(封装父类接口以减少接口的使用量)
  2. 使用第三方插件自动生成 MVP 代码
  3. 对于一些简单的页面,可以选择不使用框架
  4. 根据项目的复杂程度,部分模块可以选择不使用接口

其实这些操作都是为了一个目的:减少接口的数量

MVVM

简介

MVVM 的全称是 Model - View - ViewModel 模型,他的模型结构和 MVP 很相像,但是在代码逻辑上 MVVM 会显得更加简洁,其实他就是将 Presenter 换为了 ViewModel。
MVVM
MVVM 在 MVP 的基础上实现了数据视图的绑定(DataBinding),这样的话就不用使用接口进行传递了,而是当数据变化的时候,视图会自动更新;反之,当视图发生改变的时候,数据也会进行自动更新。这样做有什么好处呢?

  1. 减少了接口数量
  2. 不用使用 findViewById 去操控 View

DataBinding

DataBinding 是谷歌官方发布的一个实现数据绑定的框架(实现数据与视图的双向绑定),DataBinding 可以帮助我们在 Android 中更好的实现 MVVM 模式。那我们如何使用 DataBinding 呢?

  1. 在代码中启用 DataBinding
  2. 修改布局文件为 DataBinding 布局
  3. 数据绑定

详细的 DataBinding 用法可以看我另一篇博客:传送门

代码实现

其实这里的流程图和 MVP 的形式是一样的,只不过通讯手段从接口的形式变为了 DataBinding 的形式
MVVM
首先我们需要在 build.gradle 中声明使用 DataBinding
build.gradle( app )
在 android 中添加下面代码

android {dataBinding {enabled = true}
}

如果使用的是 Kotlin 的话还需要添加下面代码

apply plugin: 'kotlin-kapt'kapt {generateStubs = true
}

然后我们的 Model 和 MVP 中的是一样的
MVVMModel

package com.xjh.queryuser.mvvmimport com.xjh.queryuser.bean.Account
import com.xjh.queryuser.callback.MCallback
import java.util.*class MVVMModel {// 模拟查询账号数据fun getAccountData(accountName: String, callback: MCallback) {val random = Random()val isSuccess = random.nextBoolean()if (isSuccess) {val account = Account(accountName, 100)callback.onSuccess(account)} else {callback.onFailed()}}
}

之后我们就要分析在 ViewModel 中我们需要哪些变量以及方法,比如:getData(),我们需要调用 Model 中的 getAccountData 方法去获取数据,因为我们是使用 DataBinding 方法,所以在 ViewModel 中还需要记录对应的返回值用于展现,因为我们希望和数据变更进行绑定,防止每次都要更新所有的数据,所以我们的类需要继承 BaseObservable。然后在元素的 get 和 set 方法做出修改,get 方法需要添加注解 @Bindable 而 set 方法中需要加入更新该元素显示的代码 notifyPropertyChanged(BR.XXX); ,BR 是什么呢,其实就是相当于我们的 R 文件,用来确定是那个控件需要更新,只不过这里可以直接用变量名。
MVVMViewModel

package com.xjh.queryuser.mvvmimport android.databinding.BaseObservable
import android.databinding.Bindable
import android.view.Viewimport com.xjh.queryuser.bean.Account
import com.xjh.queryuser.callback.MCallbackimport com.xjh.queryuser.BRclass MVVMViewModel : BaseObservable() {private val mvvmModel: MVVMModel = MVVMModel()@get:Bindablevar userInput: String? = nullset(userInput) {field = userInputnotifyPropertyChanged(BR.userInput)}@get:Bindablevar result: String? = nullset(result) {field = resultnotifyPropertyChanged(BR.result)}fun getData(view: View) {this.userInput?.let {mvvmModel.getAccountData(it, object : MCallback {override fun onSuccess(account: Account) {result = "用户账号:" + account.name + " | " + "用户等级:" + account.level}override fun onFailed() {result = "获取数据失败"}})}}
}

然后我们就要进入使用 DataBinding 的第二步:修改布局文件,我们需要将 ViewModel 作为布局文件的参数,然后在布局文件中进行调用。
布局文件

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data><variablename="viewModel"type="com.xjh.queryuser.mvvm.MVVMViewModel" /></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"><EditTextandroid:id="@+id/userName"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:hint="请输入用户名"android:text="@={viewModel.userInput}"android:textSize="14sp" /><Buttonandroid:id="@+id/submit"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="30dp"android:onClick="@{viewModel.getData}"android:text="提交" /><TextViewandroid:id="@+id/result"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:text="@{viewModel.result}"android:textSize="14sp" /></LinearLayout>
</layout>

这样的话我们就可以直接在布局文件中调用 ViewModel 的方法了。
最后我们就要进行使用 DataBinding 的最后一步操作了,绑定数据,这时候我们就要修改原有的 setContentView 方法,改为使用 DataBindingUtil.setContentView 方法进行绑定,然后我们将我们需要的 ViewModel 传入进去。
DataBinding 有单向绑定和双向绑定的区别,如果是单向绑定的话就是直接用 @{XXX} 的形式,如果是需要双向绑定的话就是需要改为 @={XXX} 的形式。
双向绑定的好处就是不仅在数据变化的时候进行刷新 View,也会在 View 主动修改数据的时候对数据进行更新,多用于编辑框等控件。
MVVMActivity

package com.xjh.queryuser.mvvmimport android.databinding.DataBindingUtil
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import com.xjh.queryuser.R
import com.xjh.queryuser.databinding.ActivityMvvmBindingclass MVVMActivity : AppCompatActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)var binding = DataBindingUtil.setContentView<ActivityMvvmBinding>(this, R.layout.activity_mvvm)var mvvmViewModel = MVVMViewModel()binding.viewModel = mvvmViewModel}
}

优缺点

优点

实现了数据和视图的双向绑定,极大的简化代码

缺点

Bug 难以调试,学习成本较大。

总结

框架名总结
MVC学习简单,但是解耦不够彻底
MVP解耦更加彻底,学习起来较为简单,但是代码相对较为繁琐
MVVM代码逻辑简洁欸,但是学习成本较大

项目GitHub地址:传送门


http://chatgpt.dhexx.cn/article/Yk3QAX6p.shtml

相关文章

Android - 框架使用

目录 1 Gson解析JSON 1.1 引入依赖 1.2 使用 1.3 安装插件 1.4 Demo 2 下滑刷新&#xff0c;上滑加载新数据 2.1 背景 2.2 引入依赖 2.3 代码实现 3 加载网络图片 3.1 背景 3.2 引入依赖 3.3 代码实现 4 轮播图 4.1 引入依赖 4.2 代码实现 5 网络数据请求 Re…

Android 快速开发框架 集成框架

由于自己经常写项目&#xff0c;没有一个方便开发的一套框架怎么行&#xff0c;所以在日常开发总自己整理了一套&#xff0c;请各位过目&#xff0c;不喜勿喷。一个新项目搭建需要具备的环境:先导入我制作的jar包。添加必要的权限。需要一个Application初始化数据,下面开始调用…

Android应用-开发框架设计

目录 1. &#x1f4c2; 简介 1.1 背景 1.2 专业术语 2. &#x1f531; 总体设计思想 2.1 分层&#xff1a;组件化设计框架 2.2 分类&#xff1a;应用开发架构图 3. ⚛️ 框架详细设计 3.1 组件化框架外形 3.2 业务模块化 3.3 代码编程框架 4. &#x1f4a0; 框架其他…

Android常用框架

1.缓存框架 1.1DiskLruCache&#xff1a;Java实现基于LRU的磁盘缓存&#xff0c;DiskLruCache不是google官方所写&#xff0c;但是得到了官方推荐&#xff0c;DiskLruCache没有编写到SDK中去&#xff0c;如需使用可直接copy这个类到项目中去。使用场景&#xff1a;如“清除…

Android开发框架大全

包括各种快速开发框架、测试框架、系统框架、插件补丁框架、设计模式框架、主题切换框架。 android-tips-tricks&#xff1a; https://github.com/nisrulz/android-tips-tricks Android 开发的一些 Tips 集合 Android-Code-Style&#xff1a; https://github.com/LoranWong/And…

Android开发常用开源框架

Android开源框架系列 Android开源项目 Android开发常用开源框架2 Android开发常用开源框架3 GitHub上最火的Android开源项目,所有开源项目都有详细资料和配套视频 2017年伊始&#xff0c;你需要尝试的25个Android第三方库 Android开发常用第三方平台 免费的计算机编程类中…

Android开发框架模式(MVC、MVP、MVVM)实例解析

Android项目中&#xff0c;尤其是比较大型的项目开发中&#xff0c;模块内部的高聚合和模块间的低耦合性就显得尤为重要了。所以我们一般情况下需要为项目设计一种框架模式&#xff0c;通常情况下我们一般用到的三种MVC、MVP、MVVM。 通过框架模式设计的项目能够极大的提高开发…

LDA主题模型评估方法–Perplexity

在LDA主题模型之后,需要对模型的好坏进行评估,以此依据,判断改进的参数或者算法的建模能力。 Blei先生在论文《Latent Dirichlet Allocation》实验中用的是Perplexity值作为评判标准。 一、Perplexity定义 源于wiki:http://en.wikipedia.org/wiki/Perplexity perplexity是一…

LDA困惑度perplexity的一些个人理解

纠结这个问题很久了&#xff0c;期间主要去了gensim的google论坛&#xff0c;以及在StackOverflow、StackexChange用关键词topic number perplexity搜了下&#xff0c;得到这些很模糊的认识&#xff1a; 1. gensim的log_perplexity()解读&#xff1a; 根据gensim3.8.3的源码&…

NLP基础知识点:困惑度(Perplexity)

本篇内容翻译自Speech and Language Processing. Daniel Jurafsky & James H. Martin. 链接&#xff1a;https://web.stanford.edu/~jurafsky/slp3/ 不愧是自然语言处理领域的圣经&#xff0c;读起来流畅自然&#xff0c;以后还是要多读经典。 困惑度&#xff08;Perplexit…

Python LDA gensim 计算 perplexity

转载自 https://blog.csdn.net/qq_23926575/article/details/79472742 1.LDA主题模型困惑度 这部分参照&#xff1a;LDA主题模型评估方法–Perplexity&#xff0c;不过后面发现这篇文章Perplexity(困惑度)感觉写的更好一点&#xff0c;两篇都是翻译的维基百科。 perplexity是一…

困惑度 (perplexity)

困惑度 (perplexity) 在自然语言处理中,对于一个语言模型,一般用困惑度来衡量它的好坏,困惑度越低,说明语言模型面对一句话感到困惑的程度越低,语言模型就越好。 对于LDA模型,最常用的两个评价方法困惑度(Perplexity)、相似度(Corre)。 其中困惑度可以理解为对于一篇…

Metric评价指标-Perplexity语言模型

欢迎关注知乎&#xff1a; 世界是我改变的 知乎上的原文链接 一. 原理介绍 在研究生实习时候就做过语言模型的任务&#xff0c;当时让求PPL值&#xff0c;当时只是调包&#xff0c;不求甚解&#xff0c;哈哈哈&#xff0c;当时也没想到现在会开发这个评价指标&#xff0c;那现…

perplexity和预训练时用的loss的区别

Perplexity和预训练时用的loss都是用来评估语言模型的性能的指标&#xff0c;但是它们的计算方式和意义有所不同。 Perplexity是一种用来衡量语言模型对一个测试集的预测能力的指标。它的计算方式是将测试集中的所有句子输入到语言模型中&#xff0c;计算每个句子的困惑度&…

技术干货 | 基于MindSpore详解Perplexity语言模型评价指标

01 原理介绍 在研究生实习时候就做过语言模型的任务&#xff0c;当时让求PPL值&#xff0c;当时只是调包&#xff0c;不求甚解&#xff0c;哈哈哈&#xff0c;当时也没想到现在会开发这个评价指标&#xff0c;那现在我来讲一下我对这个指标的了解&#xff0c;望各位大佬多多指…

Perplexity定义

Refer from http://blog.csdn.net/pipisorry/article/details/42460023 http://blog.csdn.net/pipisorry/article/details/42460023 熵/信息熵 Perplexity定义 perplexity是一种信息理论的测量方法&#xff0c;b的perplexity值定义为基于b的熵的能量&#xff08;b可以是一个概…

gensim---LDA---perplexity

以下内容来源于https://blog.csdn.net/qq_25073545/article/details/79773807 使用gensim实现lda&#xff0c;并计算perplexity&#xff08; gensim Perplexity Estimates in LDA Model&#xff09; Neither. The values coming out of bound() depend on the number of topi…

世界上第一个会话搜索引擎——Perplexity AI使用测评

引言 比起传统的列表式搜索&#xff0c;Perplexity AI把艳惊四座的ChatGPT和必应搜索结合起来&#xff0c;既有ChatGPT式的问答&#xff0c;又像普通搜索引擎那样列出链接&#xff0c;就连马斯克也亲自称赞&#xff1a;它不仅总结出了推文的由来&#xff0c;还将推文的内容解释…

主题模型TopicModel:LDA主题模型的评估

http://blog.csdn.net/pipisorry/article/details/42460023 基础知识&#xff1a;熵 [熵与互信息] 皮皮blog Perplexity定义 perplexity是一种信息理论的测量方法&#xff0c;b的perplexity值定义为基于b的熵的能量&#xff08;b可以是一个概率分布&#xff0c;或者概率模型…

语言模型常用评价方法:perplexity、bleu

目录 1. perplexity&#xff08;困惑度、复杂度&#xff09; 2. BLEU 代码实现 1. perplexity&#xff08;困惑度、复杂度&#xff09; 更多详细&#xff0c;参考&#xff1a;详解语言模型NGram及困惑度Perplexity 语言模型&#xff1a;语言模型可以表示为一个计算 的模型&a…