http://www.jianshu.com/p/f60ce3cae7b5
http://saulmm.github.io/2015/02/02/A%20useful%20stack%20on%20android%20%231,%20architecture/
https://github.com/saulmm/Material-Movies
原文标题:A useful stack on android #1, architecture
本文是对该文章的翻译,本人翻译水平有限,但是会尽可能保证不会出现明显的逻辑错误,英语阅读能力较强的朋友可以直接去看原文。
该文章的项目主要采用了MVP的模式,并使用了Square的Retrofit和Otto这两个库,这篇文章属于这一系列的第一篇。
最后再补一句,如果有啥翻译错的,请各位朋友务必指出,不甚感激QAQ,如果有啥疑惑欢迎提出,共同讨论。
译文如下:
这是本系列的第一篇文章,本系列将介绍如何配置环境去开发一个具有可扩展性、可维护性和可测试性的项目,在本系列中,我将涵盖一些模式和以及类库的使用使安卓开发者在日常中不会发疯。
作为例子,我将用到下面的项目,这是一个简单的电影目录,能够被标记为视图或者挂起。
有关电影的信息是调用这个公共接口 themoivedb,你能在Apiary这一章找到合适的文档
这个项目是基于Model View Presenter(MVP)模式,也实现了一些Material Design的设计,比如转场、结构、动画、配色等等。
所有的代码能在github上面下载,这里也有一个视频展示这个app的内容
这个架构的设计是基于MVP模式,MVP模式是MVC模式的一种演变
这种模式尝试去抽象表现层的业务逻辑,在Android中这是十分重要的,因为我们的框架降低了它与数据层的耦合度,一个清楚的例子就是Adaters或者CursorLoaders。
这种架构促使,视图层的变化不需要修改业务逻辑层和数据层。这样就很简单的复用代码或者变化各种各样的数据源,比如数据库或者REST API
译者注:以上只是对MVP模式的简单介绍
这种架构会分成三层:
模型负责提供信息,这一层并不知道domain层和表现层,它能实现对数据库的连接和接口,使用REST API或者其它方式
domain层是完全独立于表现层,它将位于你的应用的业务逻辑。
domain层和模型层被分为两个module,表现层在主app的module,还有一个叫common的module,用来放一些类库和工具类。
domain module 有着usecases和它的实现类,这是作为应用的业务逻辑
这个模块是完全独立于Android框架
它是依赖于 model module 和 common module。
usecase 能够获得各种电影的评级,看看哪一类是最受欢迎的。usecase 会需要获取电影的信息并计算,将这些信息提供给model。
dependencies {
compile project (':common')
compile project (':model')
}
Model module 是负责对信息的管理,增删改查等等,在第一个版本我只有对电影api的信息进行管理。
它也实现了实体,比如TvMovie这个类表示为一个电影。
它目前只依赖 common module,这个类库被用来管理API的请求,这里我使用了Square的Retrofit,我将在下文介绍Retrofit。
这就是app本身,有着resources, assets ,逻辑等等。
它也与 domain 正在运行的 usecases 相互影响着,比如,想获取某个特定时间的电影或者一部电影的指定时间。
在这个module中有着 presenters 和 views。
每个Activity,Fragment,Dialog都实现了MVPView接口,它指定了在View上进行显示、隐藏和绘画信息的操作。
举个例子,PopularMoviesView指定了显示当前电影列表的操作,并让MoivesActivity实现这些方法。
public interface PopularMoviesView extends MVPView {
void showMovies (List<TvMovie> movieList);
void showLoading ();
void hideLoading ();
void showError (String error);
void hideError ();
}
MVP模式使得视图变得尽可能简单,由presenter决定视图的行为
public class MoviesActivity extends ActionBarActivity implements
PopularMoviesView, ... {
...
private PopularShowsPresenter popularShowsPresenter;
private RecyclerView popularMoviesRecycler;
private ProgressBar loadingProgressBar;
private MoviesAdapter moviesAdapter;
private TextView errorTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
popularShowsPresenter = new PopularShowsPresenterImpl(this);
popularShowsPresenter.onCreate();
}
@Override
protected void onStop() {
super.onStop();
popularShowsPresenter.onStop();
}
@Override
public Context getContext() {
return this;
}
@Override
public void showMovies(List<TvMovie> movieList) {
moviesAdapter = new MoviesAdapter(movieList);
popularMoviesRecycler.setAdapter(moviesAdapter);
}
@Override
public void showLoading() {
loadingProgressBar.setVisibility(View.VISIBLE);
}
@Override
public void hideLoading() {
loadingProgressBar.setVisibility(View.GONE);
}
@Override
public void showError(String error) {
errorTextView.setVisibility(View.VISIBLE);
errorTextView.setText(error);
}
@Override
public void hideError() {
errorTextView.setVisibility(View.GONE);
}
...
}
usecase 将被 presenter 执行,他们将收到响应并对views管理。
public class PopularShowsPresenterImpl implements PopularShowsPresenter {
private final PopularMoviesView popularMoviesView;
public PopularShowsPresenterImpl(PopularMoviesView popularMoviesView) {
this.popularMoviesView = popularMoviesView;
}
@Override
public void onCreate() {
...
popularMoviesView.showLoading();
Usecase getPopularShows = new GetMoviesUsecaseController(GetMoviesUsecase.TV_MOVIES);
getPopularShows.execute();
}
@Override
public void onStop() {
...
}
@Override
public void onPopularMoviesReceived(PopularMoviesApiResponse popularMovies) {
popularMoviesView.hideLoading();
popularMoviesView.showMovies(popularMovies.getResults());
}
}
这个项目中我选择了一个Message Bus系统,这个系统十分有用于广播事件,它建立了两个组件之间的通信。
主要事件都是通过发送一个Bus,如果想去消费某个Bus,就需要去订阅这个Bus。
使用这个系统,整个module就会有着低耦合度。
为了实现这个系统,我使用了Square的Otto类库。
我声明了两个Bus,一个用于usecases的REST API 通信,另外一个用于表现层发送事件。
REST_BUS使用其它线程进行事件的处理,UI_BUS在UI线程,也就是主线程上进行事件的处理。
public class BusProvider {
private static final Bus REST_BUS = new Bus(ThreadEnforcer.ANY);
private static final Bus UI_BUS = new Bus();
private BusProvider() {};
public static Bus getRestBusInstance() {
return REST_BUS;
}
public static Bus getUIBusInstance () {
return UI_BUS;
}
}
这个类被用于管理 common module,因为所有的module可以访问它,并与bus相互影响。
dependencies {
compile 'com.squareup:otto:1.3.5'
}
最后,思考下面的例子,在用户打开App的时候显示大多数受欢迎的电影。
当OnCreate方法被Android视图调用的时候,这个presenter订阅UI_BUS用来接受事件。这个presenter在OnStop方法被调用的时候解除订阅,这个presenter运行着GetMoivesSerCase.
@Override
public void onCreate() {
BusProvider.getUIBusInstance().register(this);
Usecase getPopularShows = new GetMoviesUsecaseController(GetMoviesUsecase.TV_MOVIES);
getPopularShows.execute();
}
...
@Override
public void onStop() {
BusProvider.getUIBusInstance().unregister(this);
}
}
接受事件的话,这个presenter一定要实现一个方法,这个方法的参数要与发送事件的方法的参数相同,并且一定要使用Subscribe注解。
@Subscribe
@Override
public void onPopularMoviesReceived(PopularMoviesApiResponse popularMovies) {
popularMoviesView.hideLoading();
popularMoviesView.showMovies(popularMovies.getResults());
}