一.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,定义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,内部持有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解耦,易于单元测试。
- 可维护性:清晰的组件职责划分,减少耦合。
通过以上方式,MVI架构可以帮助开发者构建响应式、可维护的Android应用。