一.MVC架构模式
View:对应的xml布局文件
Model:实体模型(JavaBean)
Controllor:对应于Activity业务逻辑,数据处理和UI处理
该模式的特点,就是当某个界面非常复杂的时候,activity代码量会上千行的增加,非常冗余。
二.MVP架构模式
View:对应的xml布局文件
Model:实体模型(JavaBean)
Presenter:责完成View于Model间的交互和业务逻辑
搭建MVP的一个栗子
(1).BaseView接口
interface BaseView {
void showLoading();
void hideLoading();
}
(2).创建一个持有BaseView接口的泛型基类BasePersenter
- 定义泛型对象引用 T mView ,方便HomePresenter()构造方法赋值
- compositeDisposable 管理订阅请求
- detachView 解除订阅,释放mView
public class BasePrenster<T extends BaseView> {
T mView; //方便构造方法赋值
CompositeDisposable compositeDisposable=new CompositeDisposable(); //管理订阅请求
public void detachView(){
compositeDisposable.dispose(); //compositeDisposable.clear();//取消订阅
mView=null;
}
}
(3).新建一个HomePresenter继承BasePrenster<HomeView>,定义getHomeData方法,请求数据
class HomePresenter extends BasePrenster<HomeView> {
public HomePresenter(HomeView homeView){
this.mView=homeView;
}
public void getHomeData() {
mView.showLoading();
compositeDisposable.add(HttpRetrofit.INSTANCE.getApiService().getTopArticles()
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new OConsumer<HttpResult<ArrayList<HomeData.DatasBean>>>(new
SuccessCallBack<HttpResult<ArrayList<HomeData.DatasBean>>>() {
@Override
public void onSuccess(HttpResult<ArrayList<HomeData.DatasBean>> httpResult) {
mView.onHomeDataSuccess(httpResult);
mView.hideLoading();
}
}), new Consumer<Throwable>() {
@Override
public void accept(Throwable throwable) throws Exception {
mView.hideLoading();
mView.onHomeDataFailed(throwable.getMessage());
}
}));
}
}
(4).新建一个持有presenter的泛型抽象基类BaseMvpActivity
public abstract class BaseMvpActivity<P extends BasePrenster> extends AppCompatActivity {
protected P mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPresenter=createPresenter();
initData();
}
protected abstract P createPresenter();
protected abstract void initData();
@Override
protected void onDestroy() {
super.onDestroy();
if (mPresenter!=null){
mPresenter.detachView();
}
}
}
(5).新建一个HomeActivity继承自BaseMvpActivity的,实现抽象方法createPersenter(),initData
- createPresenter() 直接 new HomePresenter(this),将HomeView作为参数传递.
- initData() 通过mPresenter.getHomeData请求Home页面数据.
- showLoading 显示Loading
- hideLoading 隐藏Loading
- onHomeDataSuccess 请求成功回调
- onHomeDataFailed 请求失败回调
class HomeActivity extends BaseMvpActivity<HomePresenter> implements HomeView{
@Override
protected HomePresenter createPresenter() {
return new HomePresenter(this);
}
@Override
protected void initData() {
mPresenter.getHomeData();
}
@Override
public void showLoading() {
}
@Override
public void hideLoading() {
}
@Override
public void onHomeDataSuccess(HttpResult<ArrayList<HomeData.DatasBean>> httpResult) {
}
@Override
public void onHomeDataFailed(String errorMsg) {
}
}
核心就是Activity实现View的接口,并持有Presenter对象,Presenter中持有Model和View对象,负责完成View于Model间的交互和业务逻辑,本文中的Prsenter并没有Model,而是直接新建了一些类似model中的请求方法,这样可以省掉Model接口和ModelIml接口实现类,简化代码。
三.MVVM架构
-View:对应的xml布局文件
-Model:实体模型(JavaBean)
-ViewModel:责完成View于Model间的交互和业务逻辑(依赖于LiveData)
1.新建一个BaseViewModel 继承 ViewModel.
open class BaseViewModel: ViewModel() {
// private val _state = MutableStateFlow("")
val showLoadingLiveData by lazy { UnPeekLiveData<String>() }
val dismissDialogLiveData by lazy { UnPeekLiveData<String>() }
fun <T:Any> request(
block: suspend () -> HttpResult<T>,
success: (HttpResult<T>) -> Unit,
error: (Throwable) -> Unit={},
isShowDialog: Boolean = false,
loadingMessage: String = "loading..."
): Job {
//如果需要弹窗 通知Activity/fragment弹窗
return viewModelScope.launch {
runCatching {
if(isShowDialog){
showLoadingLiveData.value=loadingMessage
}
block()
}.onSuccess {
dismissDialogLiveData.value="close"
success(it)
}.onFailure {
dismissDialogLiveData.value="close"
error(it)
}
}
}
}
2.新建一个泛型基类BaseVMActivity<VM :BaseViewModel>,内部持有ViewModel对象
abstract class BaseVMActivity<VM : BaseViewModel>:AppCompatActivity() {
lateinit var mViewModel:VM
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mViewModel=createViewModel()
initData();
}
abstract fun createViewModel(): VM
abstract fun initData()
}
3.新建一个HomeViewModel继承 BaseViewModel
class HomeViewModel : BaseViewModel() {
val homeLiveData= MutableLiveData<ListDataUiState<ArrayList<HomeData.DatasBean>>>()
fun getHomeList() {
request(
block = { HttpRetrofit.apiService.getHomeList() },
success = {
val listDataUiState=ListDataUiState<ArrayList<HomeData.DatasBean>>(
dataBean =it
)
homeLiveData.value=listDataUiState
},
error = {
val listDataUiState=ListDataUiState<ArrayList<HomeData.DatasBean>>(
isException = true,
error = it
)
homeLiveData.value=listDataUiState
},
isShowDialog = true,
loadingMessage = "加载中..."
)
}
}
4.新建一个HomeActivity继承BaseVMActivity
class HomeActivity:BaseVMActivity<HomeViewModel>() {
lateinit var mDataBind:ActivityHomeBinding
override fun createViewModel(): HomeViewModel {
return ViewModelProvider(this).get(HomeViewModel::class.java);
}
override fun initData() {
mDataBind = DataBindingUtil.setContentView(this, R.layout.activity_home) //实例化mDataBind
mViewModel.getHomeList() //请求数据
//添加观察者,处理数据
mViewModel.homeLiveData.observe(this) { //处理数据
mDataBind.homeData=it;//数据双向绑定
}
}
}
四. MVI模式**
在Android开发中,MVI(Model-View-Intent)架构是一种基于响应式编程思想的架构模式,它将应用分为Model(模型)、View(视图)和Intent(意图)三部分,并通过单向数据流来管理应用的状态和交互。以下是MVI架构的详细实现方式:
4.1. 核心组件设计
(1)Model(模型)
- 职责:管理应用的状态和业务逻辑。
-
实现方式:
- 使用不可变数据类(如Kotlin的
data class
)表示应用状态。 - 状态通常是一个
State
类,包含所有可能的UI状态。 - 示例:
data class WeatherState( val isLoading: Boolean = false, val error: String? = null, val weatherData: WeatherData? = null )
- 使用不可变数据类(如Kotlin的
(2)View(视图)
- 职责:展示数据并响应用户交互。
-
实现方式:
- View是被动观察者,订阅状态变化并更新UI。
- 通常使用
LiveData
、StateFlow
或RxJava
的Observable
来观察状态。 - 示例:
class WeatherActivity : AppCompatActivity() { private val viewModel: WeatherViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_weather) viewModel.state.observe(this) { state -> when { state.isLoading -> showLoading() state.error != null -> showError(state.error) state.weatherData != null -> showWeather(state.weatherData) } } findViewById<Button>(R.id.refreshButton).setOnClickListener { viewModel.dispatchIntent(WeatherIntent.RefreshWeather) } } private fun showLoading() { /* 显示加载动画 */ } private fun showError(error: String) { /* 显示错误信息 */ } private fun showWeather(data: WeatherData) { /* 更新UI */ } }
(3)Intent(意图)
- 职责:表示用户的操作或系统事件。
-
实现方式:
- 使用密封类(
sealed class
)定义所有可能的用户操作。 - Intent是View与Model交互的唯一方式。
- 示例:
sealed class WeatherIntent { //穷举所有可能的用户操作 object RefreshWeather : WeatherIntent() object LoadMoreWeather : WeatherIntent() data class Search(val keyword: String) : WeatherIntent() // 搜索意图 }
- 使用密封类(
4.2. 数据流管理
-
单向数据流:
-
View捕获用户操作并生成
Intent
。 -
ViewModel接收
Intent
并更新State
。 -
View观察
State
变化并更新UI。
-
View捕获用户操作并生成
-
状态管理:
- 使用
StateFlow
或LiveData
来存储和分发状态。 - 示例(使用
StateFlow
):class WeatherViewModel : ViewModel() { private val stateFlow= MutableStateFlow(WeatherState()) fun dispatchIntent(intent: WeatherIntent) { when (intent) { is WeatherIntent.RefreshWeather -> fetchWeather() } } private fun fetchWeather() { viewModelScope.launch { stateFlow.value = stateFlow.value.copy(isLoading = true) try { val weatherData = weatherRepository.getWeather() stateFlow.value = stateFlow.value.copy(isLoading = false, weatherData = weatherData) } catch (e: Exception) { stateFlow.value = stateFlow.value.copy(isLoading = false, error = e.message) } } } }
- 使用
4.3. 关键技术点
(1)不可变数据
- 使用
data class
和copy
方法确保状态不可变。 - 示例:
val newState = oldState.copy(isLoading = false, weatherData = data)
(2)协程与异步操作
- 使用Kotlin协程处理异步任务(如网络请求)。
- 在
ViewModel
中使用viewModelScope
确保协程在生命周期内自动取消。
(3)依赖注入
- 使用Dagger Hilt或Koin注入依赖(如Repository)。
- 示例(Hilt):
@HiltViewModel class WeatherViewModel @Inject constructor( private val weatherRepository: WeatherRepository ) : ViewModel()
(4)测试
-
单元测试:
- 测试
Reducer
(状态更新逻辑)。 - 测试
Intent
处理逻辑。
- 测试
-
集成测试:
- 测试
ViewModel
与Repository
的交互。
- 测试
4.4. 示例:天气应用完整流程
-
用户点击刷新按钮 → View生成
WeatherIntent.RefreshWeather
。 -
ViewModel接收Intent → 调用
fetchWeather()
。 -
Repository发起网络请求 → 返回
WeatherData
。 -
ViewModel更新State → 设置
isLoading = false
并填充weatherData
。 - View观察State变化 → 更新UI显示天气信息。
4.5. 优势
- 可预测性:单向数据流使状态变化可追踪。
- 可测试性:状态更新逻辑与UI解耦,易于单元测试。
- 可维护性:清晰的组件职责划分,减少耦合。
6. 注意事项
- 状态膨胀:复杂页面可能导致状态类过于庞大,需合理拆分。
- 内存开销:频繁状态更新可能增加内存使用,需优化状态更新逻辑。
通过以上方式,MVI架构可以帮助开发者构建响应式、可维护的Android应用。