- 原文作者 : MuratCanBur
- 译文出自 : 掘金翻译计划
- 译者 : foolishgao
- 校对者 : kassadin、Sausure
- 状态 : 完成
迄今为止,我阅读了很多有关Android软件开发中结构设计的文章。以我对他们的认识,比较好的方法是实现**MVP(Model View Presenter)**模式,这对Android开发者也是非常重要的。
我在其他开发者的技术博客和项目中学到了一些有用的东西,现在我决定开发一个基本的项目架构来用于实现我们的客户端软件mobiwise. 我选择了MVP模式作为项目架构,让我们开始了解一下。
你能在网上找到很多MVP相关解释和定义,让我来说一下我对MVP的理解。MVP是一种分离展示层和业务逻辑层的模式,使两者独立存在的模式。我相信分离这些部分的代码的过程属实令人厌烦。
为了这个实践,我们应该在项目中提供出各个抽象层。
为了使项目易于理解,我们首先做的是抽象出各个层面。这对开发测试和维护代码都非常重要。在任何Android项目中为了开发需要都会抽象出很多层,这里我说下重点!
项目中特有的业务逻辑部分,这里称之为Domain layer, 数据模型、网络相关、数据库操作部分,这里称之为Model layer,只有Android特有的部分,称之为Presentation or App Layer。 最后一个,也很重要,用于第三方library或者项目中共用的、基础工具类等,称之 Common Layer.
我觉得,抽象出这么多层,在开始阶段,似乎难以理解和实现。
这一层是完全独立的因为它指定了特定项目的业务逻辑。就我在网上查阅过的资料,这一层有个差不多的实现方式。根据项目的命名规则,定义出项目业务逻辑的用例接口,在创建出用例控制实现类来实现这个接口做相对应的工作。
让我们试想一个新闻应用程序,并试着定义个基本的业务用例场景。我定义了一个基本的业务用例接口,一个很简单的场景用例接口。
public interface GetPopularTitlesUsecase extends Usecase {
void getPopularTitles();
void onPopularTitlesReceived(ArrayList<Title> title);
void sendToPresenter();
}
定义好接口,开始写class来实现GetPopularTitlesUsecase。下面是个基本的实现类。
public class GetPopularTitlesUsecaseController implements GetPopularTitlesUsecase {
private List<Title> titleList;
public GetPopularTitlesUsecaseController() {
BusUtils.getRestBusInstance().register(this);
}
@Override
public void getPopularTitles() {
SyncService.start();
}
@Subscribe
@Override
public void onPopularTitlesReceived(ArrayList<Title> titleList) {
this.titleList = titleList;
sendToPresenter();
}
@Override
public void sendToPresenter() {
BusUtils.getUIBusInstance().post(titleList);
BusUtils.getRestBusInstance().unregister(this);
}
@Override
public void execute() {
getPopularTitles();
}
}
开发者都知道的,在项目中必须有一个Model Layer来处理网络请求和数据库存取相关的工作。我一般把这些部分的代码分成三个包,分别叫entity, rest和database。对于大部分项目分成这样已经足够。也许你需要创建有别于数据层的业务相关层。比如,你想展示用户的全称,就不应该通过在数据层中获取用户的姓和用户的名再通过指定的adapter类或者view类等方式做一个拼接处理,这很笨拙,此时应该定义业务层来实现这个操作。定义两个不同的数据层很笨拙。但是仍然重要。
这是最基本和熟知的抽象层,指代了Android程序开发中特有组件的部分。
##View
View在MVP中代表UI组件部分。
public interface PopularTitlesView extends MVPView {
void showTitles(List<Title> titleList);
void showLoading();
void hideLoading();
}
Presenter在MVP中类似于连接view和model的桥梁。常用的实现方式,我们需要创建model接口来处理特定的场景。
public interface RadioListPresenter extends Presenter {
void loadRadioList();
void onRadioListLoaded(RadioWrapper radioWrapper);
}
创建完简单的RadioListPresenter接口,我们来实现这个接口。
public class RadioListPresenterImp implements RadioListPresenter {
RadioListView radioListView;
GetRadioListUsecase getRadioListUsecase;
Bus uiBus;
@Inject
public RadioListPresenterImp(GetRadioListUsecase getRadioListUsecase, Bus uiBus) {
this.getRadioListUsecase = getRadioListUsecase;
this.uiBus = uiBus;
}
@Override
public void loadRadioList() {
radioListView.showLoading();
getRadioListUsecase.execute();
}
@Subscribe
@Override
public void onRadioListLoaded(RadioWrapper radioWrapper) {
radioListView.onListLoaded(radioWrapper);
radioListView.dismissLoading();
}
@Override
public void start() {
uiBus.register(this);
}
@Override
public void stop() {
uiBus.unregister(this);
}
@Override
public void attachView(MVPView mvpView) {
radioListView = (RadioListView) mvpView;
}
}
所有以上这些来自我们代码中的例子,只是一个简单的实现。这些都不绝对完全也不是最好的,不同的项目需要不同的方式来实践,要视情况而定。
每一个Activity,Fragment要根据逻辑功能来实现一个View接口,以我项目中的例子来说,RadioListFragment应该实现RadioListView接口,覆写相关的方法并让相关的presentar的方法来处理对应逻辑。
public class RadioListFragment extends Fragment implements RadioListView, SwipeRefreshLayout.OnRefreshListener {
@Inject
RadioListPresenter radioListPresenter;
public RadioListFragment() {
}
public static RadioListFragment newInstance() {
RadioListFragment fragment = new RadioListFragment();
return fragment;
}
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
BusUtil.BUS.register(this);
initializeInjector();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
radioListPresenter.loadRadioList();
}
private void initializeInjector() {
RadyolandApp app = (RadyolandApp) getActivity().getApplication();
DaggerGetRadioListComponent.builder()
.appComponent(app.getAppComponent())
.getRadioListModule(new GetRadioListModule())
.build()
.inject(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_radio_list, container, false);
ButterKnife.bind(this, view);
BusUtil.BUS.post(new TitleEvent(R.string.radio_list));
radioListPresenter.start();
radioListPresenter.attachView(this);
return view;
}
@Override
public void showLoading() {
swipeRefresh.setRefreshing(true);
}
@Override
public void dismissLoading() {
swipeRefresh.setRefreshing(false);
}
@Override
public void onListLoaded(RadioWrapper radioWrapper) {
radioListAdapter.setRadioList(radioWrapper.radioList);
radioListAdapter.notifyDataSetChanged();
DatabaseUtil.saveRadioList(radioWrapper.radioList);
}
@Subscribe
public void RefreshRadioListEvent(RefreshRadioListEvent radioListEvent) {
radioListPresenter.loadRadioList();
}
@Override
public void onDestroy() {
super.onDestroy();
BusUtil.BUS.unregister(this);
}
@Override
public void onRefresh() {
radioListPresenter.loadRadioList();
}
当我第一次在网上搜索这方面的内容时,我发现很多开发者给每一层都分别创建了不同的modules。这个方法似乎适用于很多开发者,但是我不喜欢。是为什么我没有这么做的原因。我给每个module或者层创建了不同的包,相信这不适用于所有人,只是我的方式,我感觉这样很舒服。
最后,我写了Android-base-project项目
我写了一个适用于很多开发项目的基础项目(不是library,只是一个指导的project)包括基础Fragment,Activity和Retrofit网络层相关类,工具类和常见Gradle文件结构。是时候让更多的类基于MVP模式来开发。
我并不是想推荐给你在Android项目开发中使用的那些libraries,诸如Dagger 2, RxJava等。我只希望一切简单就好,把重点放在项目的结构设计上。
我相信MVP有很多的不同的实现方式,我经常会去学习其他开发者的方式,找出我认为最好的来实践。
重要的一点是我们应该开发一个可以独立于其他libraries、UI层、数据库和网络层的项目。如果你开发的项目基本满足以上,这个项目一定是易于开发,测试和维护的。
Resources: