Google Samples 学习之 todo-mvp(二)
之前的文章对 todo-mvp 项目的 Gradle 依赖和运行效果进行了分析,本文则要对项目的代码组织结构进行简要分析。 前置文章:Google Samples 学习之 todo-mvp(一)
文件结构
总览
主要代码文件的组织情况如下图所示:
src 目录是编程工作的主要工作目录,按图中从上到下的顺序介绍它的子目录的内容有:
- androidTest:UI 测试类
- androidTestMock:UI 测试相关的 mock 测试类
- main:主要代码
- mock:对应 flavor 为 mock 的情况时,手动注入 Repository 实例(Model 层访问工具)
- prod:对应 flavor 为 prod 的情况时,手动注入 Repository 实例
- test:单元测试类
对 main 目录中的结构要特别的关注,在包目录下又细分了几个子目录,按照功能划分来管理代码:
- addedittask:增加新任务
- data:数据操作相关类,即 Model 层主要代码
- statistics:任务统计
- taskdetail:任务详情
- tasks:任务总览
- util:工具类
BaseView 和 BasePresenter
src 目录下还有两个单独的文件 BaseView 和 BasePresenter,分别是 View 和 Presenter 层必须实现的接口,定义了各自层中的成员的行为。
BaseView 的代码如下:
|
|
- 通过泛型保证 View 和 Presenter 的对应
- 接口起到了注入 Presenter 实例的作用
- 而且说明 View 对象要保持 Presenter 对象的引用
BasePresenter 的代码如下:
|
|
实际上接口没有对 Presenter 行为做出特别细致的定义,具体的 Presenter 对象的工作流程要靠程序员自己编写。
代码分析
连接 View 和 Presenter 的 Contract 接口
可以注意到界面相关的功能块都会涉及到 View 和 Presenter 的操作,它们的连接则是通过各种 Contract 接口定义。 比如,添加任务的相关接口为 AddEditTaskContract:
|
|
可以看到 Contract 接口内部包含两个接口 View 和 Presenter,继承 BaseView 和 BasePresenter,分别定义这个功能所需的 View 和 Presenter 的行为。 使用 Contract 接口的方法让代码结构非常清晰,而且维护非常方便。而且实现同一功能的代码都在一个目录,即使接口发生更改,后续的修改也不会它麻烦。
工具类
因为项目较为简单,util 目录下只有 3 个工具类
ActivityUtils
内部只有一个静态方法用来向 Activity 添加 Fragment 实例(毕竟这段代码重复度极高):
|
|
特别要注意的是 checkNotNull() 方法在 Guava 库中定义,用来检查对象的非空性:
|
|
注意:这么做就是为了一个简单的方法引入了庞大的 Guava 库,自己写代码的时候就没必要同样使用 Guava 了,不如另外写一个工具类包含 checkNotNull() 方法。
EspressoIldlingResource 和 SimpleCountingIldlingResource
app 的空闲状态与 Espresso 的测试方案
Espresso 的测试程序和被测应用是同步执行的。Espresso 等待待测试资源处于空闲状态,才会执行下个动作和检查下个断言。 默认情况下,Espresso 会检查 UI 线程任务以及 AsyncTask 线程池,确保当前没有任务使用资源,即空闲状态。但是,还有一些后台任务的线程状态不像以上两种那么容易检测(比如使用 Intentservice 进行按钮点击操作),默认检查流程会导致 Espresso 在还有后台任务的情况下误判资源处于空闲状态 —— 这时就需要程序员编写代码通知 Espresso 任务状态,以明确正确的测试开始时刻。 好在,Espresso 提供了接口 IdlingResource,实现它就可以轻松通知 Espresso 资源是否空闲。
更多指导内容可参考 IdlingResource 官方文档
SimpleCountingIldlingResource
这个简单的实现使用了一个内部计数器来实现空闲状态检查:
- 如果资源被一个线程使用则计数器加一
- 如果资源被一个线程释放、线程工作结束或中止,则计数减一
- 如果计数为零,则通知 Espresso 资源空闲
- 如果计数为负数,则说明计数器被破坏或者计数过程错误,要抛出异常
实际上最重要的代码是:
|
|
注意:要使用 AtomicInteger 以确保自增和自减操作的原子性,原有自增和自减操作符 ++、– 的操作不是原子的。
EspressoIldlingResource
其主要代码如下:
|
|
可以看出 EspressoIdlingResource 使用了 SimpleCountingIdlingResource 的功能实现了一个针对全局资源的空闲状态检查类。这是因为编写 app 的程序员知道:这个 app 中使用后台任务访问资源的行为都集中在 Model 层的数据获取行为,而 Model 层获取数据时使用的资源辅助类都是全局可用的(虽然其他层根本不会使用它)。 具体的使用方法也很简单:
- 在获取数据之前调用静态方法 increment
- 在获取数据成功后调用方法 decrement
后续文章
后续会对目录 addedittask、data 中的代码进行分析。statistics 和 taskdetail 目录同 addedittask 目录结构没有太大区别,代码的组织和实现方式也同 addedittask、tasks 相似,因此不重复分析。
- data 目录代码分析:Google Samples 学习之 todo-mvp(三)
- addedittask 目录代码分析:Google Samples 学习之 todo-mvp(四)
- tasks 目录代码分析:Google Samples 学习之 todo-mvp(五)