前言
老板让我搭建一个APP,我该怎么快速上手?
现在常用的Android应用总体架构是什么样的?
Android开发现在有哪些流行的新技术和工具,可以提高我工作效率?
这里介绍这样一个工程模板,让你快速搭建健壮(strong),易扩展(scalable),易测试(testable),易维护(maintainable)的Android工程。
什么是架构
本文主要讨论Android软件架构,那么何谓软件架构?软件体系结构是构建计算机软件实践的基础。与建筑师设定建筑项目的设计原则和目标,作为绘图员画图的基础一样,软件架构师或者系统架构师陈述软件架构以作为满足不同客户需求的实际系统设计方案的基础。软件的架构是后续软件开发实施的规则和骨架,好的架构可以极大地降低软件的开发成本和维护成本。
Android开发架构的演进
Android应用架构在一直演进,没有最好的架构,只有最适合的架构。通过对历史过程中的架构的了解,可以了解每个架构的优缺点和适用范围,知道为什么我们经历了这些变化。
MVC
MVC模式最早由Trygve Reenskaug在1978年提出。目的是实现一种动态的程序设计,使后续对程序的修改和扩展简化,并且使程序某一部分的重复利用成为可能。它将软件系统分为三个部分:模型,视图和控制器。
MVC在Android上的应用
- View:对应于xml布局文件
- Model:模型数据
- Controllor:对应于Activity业务逻辑,数据处理和界面相应
MVC的问题
这里引用一段苹果开发者网站对MVC的论述
However, there is a theoretical problem with this design. View objects and model objects should be the most reusable objects in an application. View objects represent the "look and feel" of an operating system and the applications that system supports; consistency in appearance and behavior is essential, and that requires highly reusable objects. Model objects by definition encapsulate the data associated with a problem domain and perform operations on that data. Design-wise, it's best to keep model and view objects separate from each other, because that enhances their reusability.
意思大致是:View和Model都是需要重用的模块,在MVC中View监听Model的通知导致View依赖了Model,降低了View的可重用性。现实中常常在Android上实现的MVC里,View是xml文件,Activity是Controller其中又包含了大量和Model数据耦合的代码又要响应用户事件,导致承担功能过多,代码量过大难以维护和测试。而且如果修改了Model的代码,也可能会影响Activity中的逻辑。所以大型的工程,往往需要将Activity中一些和界面无关的职责拆分出来,提高工程的可维护性和可测试性。
MVP
为了彻底的将Model和View解耦,MVP对MVC进行了一些改进,将View和Model之间的通知和调用去掉,所有的数据流都经过Presenter。
MVP的案例
这里可以参考google在github上的示例。这个工程里面实现了很多架构,每个架构都是单独一个分支,其中分支TODO-MVP就是演示的MVP。下面简单解读一下这个工程:
1 工程结构
- 按照功能模块划分包
本工程按照不同的功能来分包,如todoList,taskdetail,statistic(统计页面)等,每个功能都是相对独立的。通常来讲,我们可以用功能分包或者按照层次(如view,presenter,model等)来分包。关于那种分包模式好,Clean Architecture的作者做了一些有意义的论述,其中旗帜鲜明的支持按照功能分包。 - 功能模块的结构
在每个功能模块中,主要包括了该功能的Activity、Fragment、Presenter、Contract四个类文件。Activity用于创建Presenter和View,Fragment实现了View的接口,Presenter实现了各项逻辑,Contract用于把Presenter和View联系在一起。Model在各个功能模块之外,为各模块提供支持。
2 MVP在工程中的使用
- 使用contract把同一个功能的view和presenter联系在一起,如:
public interface TasksContract {
interface View extends BaseView<Presenter>{}
interface Presenter extends BasePresenter{}
}
- Fragment继承View,来实现界面的具体逻辑
public class TasksFragment extends Fragment implements TasksContract.View {
}
- 在Activity中构造Presenter和View
TasksFragment tasksFragment =
(TasksFragment) getSupportFragmentManager().findFragmentById(R.id.contentFrame);
if (tasksFragment == null) {
// Create the fragment
tasksFragment = TasksFragment.newInstance();
ActivityUtils.addFragmentToActivity(
getSupportFragmentManager(), tasksFragment, R.id.contentFrame);
}
// Create the presenter
mTasksPresenter = new TasksPresenter(
Injection.provideTasksRepository(getApplicationContext()), tasksFragment);
- 在Presenter里处理各类逻辑
public class TasksPresenter implements TasksContract.Presenter {
@Override
public void loadTasks(boolean forceUpdate) {}
@Override
public void addNewTask() {}
}
MVP的问题
那MVP会有什么问题呢?试想一下下面的场景:假设要实现一个类似QQ的最近会话列表功能,那么可以从服务器拉到一个最近会话的列表如下[{id:1,message:"hello1"},{id:2,message:"hello2"}],列表中每一项有一个id和一个最近消息。好了,这样就可以显示这个会话列表了。可是过了一段时间,产品又要求如果最近会话里面有好友,需要显示好友的备注,而不是id。这时我们不得不又向服务器请求好友列表,然后把是好友的id替换为备注名显示在界面上。这里就出现了一个问题,当程序中已有一个功能是显示好友列表,此处实现了一套拉取好友列表的功能了,而现在又需要再重复实现一次。聪明的读者这个时候也许已经想到了一个解决方案,把两个功能重复的地方拿出来实现,从而达到重用的目的。
Clean Architecture
为了解决上面的问题,就有人提出了Clean Architecture的架构。架构图如下:
- 依赖是由外向内的,最外层蓝色的包括UI,Model(本地数据库或网络数据),绿色层是Presenter,红色层是Use Case,这里的Use Case指的就是外层可重用的模块,如上文中提到的好友资料相关的逻辑,黄色层称为Entities为更基础的模块,如整个APP可共用的逻辑,或者一个公司所有APP都可以共用的模块(如果程序比较简单,就没有这一层)。
- 每一层都有单独的数据结构,数据从一层到另一层的时候都需要经过转换,如果不希望这个程序都使用同样的数据结构的话,这么做是值得的。
- 具体的实现我们还是参考google的demo,如图所示:
整个工程分三个模块,分别是Data,Domain,Presentation。Data是原MVP中的Model,在Clean Architecture中处于最外层;Domain处于Use Case层,用于实现各个可以重用的业务模块;Presentation包含了UI和Presenter和自己的Model - 独立的UI,独立的数据库,独立的业务逻辑,更易测试和维护
Clean Architecture的案例
这里解读一下google的工程,如下图:
可以看到这个工程和前文的MVP工程结构没有太大变化,只是在功能模块中加入了domain目录,里面实现了Use Case。
RxJava和Dagger
在工程整体结构确定了后,这里还可以使用时下比较热门的两个库,帮助我们写出更优美,更易测试的代码。
RxJava
RxJava是用于压缩异步操作的库,可以让多层callback回调变成一串连续的事件,从而减少层次让代码更简洁。更多详细介绍
Dagger
Dagger是一个轻量级的依赖注入库。关于Dagger2的使用方法可以参考这里。使用Dagger的主要好处包括:
- 依赖是在外部注入和配置的,所以方便了模块的可重用性
- 可以注入抽象类和接口,从而更好的解耦
- 可以注入mock的依赖,方便测试
工程地址
这里实现了一个Clean Architecture + RxJava + Dagger的框架示例, 该示例使用了必应中国的每日图片API。