写在前面的话
嗯 聊聊架构。 这段时间一直在维护旧项目。 包括自己之前写的新项目 越来越发现 一个架构清晰的项目往往让人赏心悦目。不至于在一个bug丢过来之后手足无措。包括以后别人接收自己的项目 能很快上手 何乐而不为呢?
曾经的"MVC"
先从我们曾经的 "MVC" 说起, "MVC" 全名 "Model View Controller".即 视图层(View), 控制层(Controller),模型层(Model).他们之间的关系 如图:
MVC
视图层(View) 一般由 xml布局文件担任 控制层(Controller) 一般由 activity和fragment 担任 模型层 (Model) 一般指的是 抽离出来的接口扩展。 尝尝可以复用的那一部分。 你只需要把接口 在Controller 抛出。 更新 View即可。其实我们刚开始参与项目开发 技术薄弱的时候 都会想到把复用的部分抽离出来 但是activity类写的很臃肿 至少不够优雅这其实就是MVC
写到这里 我们知道android中 Controller 控制层一般有Activity(主要)和fragment 担任。但是activity类中 常常包含一些ui 初始化 或者 网络 第三方sdk 的初始化又或者是业务逻辑。那么 这样写起代码层就会变得臃肿。而且不够优雅。那么MVP就从MVC中眼花诞生了。
MVP
MVP中的model层继续沿用MVC中的model
大家看这个图 也许不是很清楚或者说不是太理解。
在这里 "View" 由presenter中的接口担任 实现向View实现类通信,你也可以在Android组件中实现它。有时最好直接使用Activity,Fragment或自定义View。>The view that receives commands from a presenter in MVP, is referred to as "view"
先说Model: 它是一个负责管理数据的接口。模型的职责包括使用API、缓存数据、管理数据库等。该模型也可以是一个接口,与其他模块负责这些职责的沟通。例如,如果您使用的是存储库模式,则该模型可能是一个存储库。如果您使用的是干净的建筑,相反,该模型可以关联。(翻译过来略显生硬 简而言之 Model层是数据访问层,如数据库API或远程服务器API)
Presenter:大家可以看到Model和View是完全没有交互的。那么 Presenter则担当Model与View之间的中间人。你所有的业务逻辑都属于它。Presenter负责查询模型和更新视图,响应用户交互更新模型。
View:The view can be implemented by Activities, Fragments, any Android widget or anything that can do operations like showing a ProgressBar, updating a TextView, populating a RecyclerView and so on.(这个翻译起来有点尴尬 感觉大家应该都能看懂)
对照MVC图来看 我们发现 View 中出现了<Interface> 而Presenter中也出现了<Interface> .什么意思呢? 我们看Presenter中的代码实例:
View中的TicTacToeView示例
关于
- 怎么写Model
- 如何理解View
- 如何写Presenter
参考 MVP详解
关于MVP文字性的描述就这么多。其实网上大部分讲解都是套三者之间的关系交互图来讲的。只有理解了尝试敲几遍就差不多会了。另外 符合项目需要的才是最好的。下面我尝试着动手仿照 ONE 一个app将MVP模式套入其中练手(项目会不断完善的 架构 分包开始)
先截个图 看一下目前大致的结构。
aaa.png没用到Presenter之前切换Fragment MainActivity切换标签页:
<code>
FragmentUtils fragmentUtil = new FragmentUtils(this, R.id.view_container);
fragmentUtil.switchTo(OneFragment.class);
mBinding.radio.setOnCheckedChangeListener((group, checkedId) -> {
switch (checkedId) {
case R.id.home_one:
fragmentUtil.switchTo(OneFragment.class);
break;
case R.id.home_reading:
fragmentUtil.switchTo(ReadingFragment.class);
break;
case R.id.home_music:
fragmentUtil.switchTo(MusicFragment.class);
break;
case R.id.home_movie:
fragmentUtil.switchTo(MovieFragment.class);
break;
default:
break;
}
});
</code>
用到Presenter之后:
<code>
if(presenter==null)
presenter = new MainTabPresenter(this, mBinding.radio);
presenter.switchTo();
</code>
其中 HomeActivity 类:
<code>
public class HomeActivity extends BaseActivity<HomeActBinding> {
private MainTabPresenter presenter;
@Override
protected int getLayoutId() {
return R.layout.home_act;
}
@Override
protected void setupView() {
if(presenter==null)
presenter = new MainTabPresenter(this, mBinding.radio);
presenter.switchTo();
}
}
</code>
BaseActivity类:
<code>
public abstract class BaseActivity<T extends ViewDataBinding> extends AppCompatActivity{
protected T mBinding;
protected ApiInteractor api;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initLayout();
}
private void initLayout() {
AppComponent appComponent = ((App) getApplication()).getAppComponent();
api = appComponent.getApiInteractor();
ViewDataBinding inflate = DataBindingUtil.setContentView(this, getLayoutId());
mBinding = DataBindingUtil.bind(inflate.getRoot());
if (mBinding == null) {
if (getLayoutId() == 0) {
throw new InflateException("activity no source_ID");
} else {
throw new NullPointerException("mBinding==NULL");
}
}
setupView();
}
@LayoutRes
protected abstract int getLayoutId();
protected abstract void setupView();
}
</code>
Presenter类:
<code>
public class MainTabPresenter implements MainTabContract.Presenter {
private RadioGroup radioGroup;
private final FragmentActivity mContext;
public MainTabPresenter(@Nullable FragmentActivity mActivity, @Nullable RadioGroup group){
mContext = checkNotNull(mActivity, "FragmentActivity cannot be null!");
radioGroup = checkNotNull(group, "RadioGroup cannot be null!");
}
@Override
public void switchTo() {
FragmentUtils fragmentUtil = new FragmentUtils(mContext, R.id.view_container);
fragmentUtil.switchTo(OneFragment.class);
radioGroup.setOnCheckedChangeListener((group1, checkedId) -> {
switch (checkedId) {
case R.id.home_one:
fragmentUtil.switchTo(OneFragment.class);
break;
case R.id.home_reading:
fragmentUtil.switchTo(ReadingFragment.class);
break;
case R.id.home_music:
fragmentUtil.switchTo(MusicFragment.class);
break;
case R.id.home_movie:
fragmentUtil.switchTo(MovieFragment.class);
break;
default:
break;
}
});
}
@Override
public void start() {//有些地方 RxJava MediaPlayer Handler 等生命周期用得上
}
}
</code>
简单的逻辑代码交由Presenter 和Model协作实现。可能表述 和理解的不是太好 欢迎指正。Demo架构Dagger2+RxJava+Retrofit 雏形已经大致出现 后面会陆续补充一些使用的小技巧 和 第三方的插入 譬如 微信热修复 等等吧。谢谢大家~
参考:
MVC vs. MVP vs. MVVM on Android
Android App的设计架构:MVC,MVP,MVVM与架构经验谈
官方MVP Dagger2
等等 分享几个免费的api(侵删 谢谢)
干货Api
和风天气
one 抓包接口
欢迎加群:
QQ 群 521039620
原文博客地址
网友评论