android应用框架系列一,架构
2015-10-19 15:50
411 查看
A useful stack on android #1, architecture
02 Feb 2015原文链接:原文地址原文作者:Saúl
Molinero
这是一个关于如何设置开发一个可扩展、可维护、可测试的安卓环境主题系列文章的第一篇,在这个系列中,我将介绍一些设计模式和类库的使用方法避免android开发者在日常开发中感到发狂。
Scenario
在例子中我将会依赖以下项目,一个简单电影分类项目做概念验证,一个能被标记作为视图或挂起的项目。电影的有关信息是调用themoviedb 这个公共接口,你能在 Apiary里找到详细的文档。
这个项目基于 Model View Presenter 设计模式,也实现了一些 Material
Design 的设计,像页面转换、结构、动画、颜色、等等。。。
所有的可用代码在 Github, 可以随意看看,同时这里也有一个 视频展示这个应用的行为。
Architecture
在架构设计中我采用了设计模式:Model View Presenter,是ModelView Controller模式的演变模式。
这种模式尝试去抽象表现层的业务逻辑,这在android中是很重要的,因为在我们的框架促进了这两部分与数据层的耦合,一个清晰的例子是 Adapters 或者CursorLoaders.
这种架构促使替换界面不需要修改业务逻辑层和数据层。这样就可以很简单的去重用代码或者改变不同的数据源例如数据库和REST API。
Overview
架构可以被分割为三个主要的层:presentation
model
domain
Presentation
表现层是负责显示图形界面和为其提供数据。
Model
模型层是负责提供信息,这一层不知道领域和表现层的内容,可以实现一个数据库的连接和接口、REST API、或者其它任何数据持久的方式。在这一层同样实现了应用的的实体类,表示一个电影的类,类别等等。。。
Domain
领域层完全独立与表现层,它里面是属于应用的业务逻辑。
Implementation
在Android 应用里,domain 和 model 层是分离的两个java模块,presentation层由app模块代表,还有一个common模块是用来提供公共类库和工具类的。
Domain module
在domain模块里存放着用例和它们的实现,是应用的业务逻辑。这个模块完全独立于android框架。
他依赖于model模块和common模块。
一个用例需要获得各种主题电影的总评分,看看哪一类最受欢迎,用例需要获得电影信息然后做出计算,电影信息由model层提供。
[code]dependencies { compile project (':common') compile project (':model') }
Model module
model module负责管理信息,选择、保存、删除信息等等。。。在第一个版本我仅仅管理电影信息API的操作。它同样实现了实体类,例如
TvMovie,表示一个电影。
这个模块至今只依赖common模块,这个类库被用来管理API的请求,这样我需要使用Square的Retrofit,我会在下面介绍retrofit。
[code]dependencies { compile project(':common') compile 'com.squareup.retrofit:retrofit:1.9.0' }
Presentation module
是Android应用本身的模块,有自己resources, assets, 逻辑等。。。他也与domain通过运行的用例互相作用,在例子中需要获取某一时间的当前电影列表,或者从电影请求具体的数据。
在这个模块里有presenters 和 views。
每个
Activity,
Fragment,
Dialog,
都实现一个
MVPView接口,它指定了显示、隐藏、打印信息等需要支持显示的操作。
例如,
PopularMoviesView视图接口指定了显示当前电影的列表的操作,由
MoviesActivity类实现这些方法。
[code]public interface PopularMoviesView extends MVPView { void showMovies (List<TvMovie> movieList); void showLoading (); void hideLoading (); void showError (String error); void hideError (); }
模式:MVP 中的视图尽可能简单,presenter决定视图的行为。
[code]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); } ... }
这个用例由presenters执行,它将会接收response 和管理views的行为。
[code]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()); } }
Communication
这个项目我选择了一个消息总线系统,这个系统在广播事件和建立两个组件之间的通信非常有用,尤其是后者配合的十分完美。主要的事件都通过总线发送,感兴趣的消费类需要在总线订阅。
使用这个系统,模块之间将会有一个低耦合度。
我使用Square的类库Otto实现这个系统。
我定义了两个总线,一个使用REST API和用例通信,另一个向表现层发送事件。
REST_BUS使用任何线程处理事件,
UI_BUS发送事件通过主线程(默认UI主线程)。
[code]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模块管理,因为所有的模块都会获取它并与主线互相作用。
[code]dependencies { compile 'com.squareup:otto:1.3.5' }
最后,思考下面的例子,在用户打开应用的时候显示最受欢迎的电影。
当
onCreate方法在 Android
View调用时,presenter
在UI_BUS上订阅接收事件,
onStop()方法调用时presenter取消自身订阅,例如
presenter 运行
GetMoviesUseCase用例。
[code] @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。
[code] @Subscribe @Override public void onPopularMoviesReceived(PopularMoviesApiResponse popularMovies) { popularMoviesView.hideLoading(); popularMoviesView.showMovies(popularMovies.getResults()); }
Resources:
Architecting Android…The clean way? - Fernando
Cejas
Effective Android UI - Pedro Vicente Gómez Sanchez
Reactive programming and message buses for mobile - Csaba Palfi
The clean architecture - Uncle Bob
MVP Android - Antonio Leiva
相关文章推荐
- ARM GPU 架构简介
- 秒杀系统架构优化思路
- 灾备模式的基本体系架构
- JS网站当前日期在IE9、Chrome和FireFox中年份显示为115年的解决方法
- 微服务架构: 人脑与工具间最佳、最高效的匹配工作模式
- Storm系列(十六)架构分析之Executor-Bolt
- 架构之路:性能与单元测试
- 巩义网站托管价格-来自网渔科技的最新报价
- 搜狗刘建:架构师=学习能力+好奇心+持续实践
- 嵌入式系统软件架构设计
- 机器学习:实例1--推荐系统架构
- Storm系列(十五)架构分析之Executor-Spout
- iOS 应用架构谈 动态部署方案
- 网站被DDOS攻击了怎么办
- 用户访问网站的基本流程
- curl用法:获取网站的header头及状态码
- 手机网站的几点注意
- 一个web.py搭建的个人网站
- 一种多租户系统架构
- iOS 应用架构谈 动态部署方案