概述 該示例(TODO MVP)是後續各種示例演變的基礎,它主要演示了在不帶架構性框架的情況下實現M V P模式。其採用手動依賴註入的方式來提供本地數據源和遠程數據源倉庫。非同步任務通過回調處理。 註意:MVP中View的概念是有所不同的: android.view.View類我們叫它 "Androi ...
概述
該示例(TODO-MVP)是後續各種示例演變的基礎,它主要演示了在不帶架構性框架的情況下實現M-V-P模式。其採用手動依賴註入的方式來提供本地數據源和遠程數據源倉庫。非同步任務通過回調處理。
註意:MVP中View的概念是有所不同的:
- android.view.View類我們叫它 "Android View"
- MVP中從P接收命令的東西我們叫它”View”,後文說了,就是Fragment
Fragment
採用Fragment作為視圖的載體有兩個原因:
- Activity 和 Fragment分離恰好適合用來實現MVP:Activity是創建和連接View與Presenter的總控制器
- 適配平板設備的佈局或多視圖屏幕的場景都可利用Fragment框架的優勢
關鍵概念
該App(指官方TODO-MVP示例)中有四個功能:
- Tasks
- TaskDetail
- AddEditTask
- Statistics
每個功能擁有下列代碼分工:
- 定義VIew和Presenter之間的“契約”介面
- 一個Activity類負責創建Fragment和Presenter
- 一個Fragment類實現VIew介面
- 一個Presenter類實現Presenter介面
一般來說,業務邏輯存在於Presenter中並依賴View完成Android中UI相關的工作。
View中幾乎不包含邏輯:它將Presenter的命令轉換為UI動作,並監聽用戶動作傳遞給Presenter。
“契約”介面用於定義View和Presenter之間的聯繫。
(譯註:
有人立馬就問到M哪去了,官方實例里M是輕的,與Repository和DataSource概念分離。
/**
* Immutable model class for a Task.
*/
public final class Task {
@NonNull
private final String mId;
@Nullable
private final String mTitle;
@Nullable
private final String mDescription;
private final boolean mCompleted;
@NonNull
public String getId() {
return mId;
}
@Nullable
public String getTitle() {
return mTitle;
}
@Nullable
public String getTitleForList() {
if (!Strings.isNullOrEmpty(mTitle)) {
return mTitle;
} else {
return mDescription;
}
}
@Nullable
public String getDescription() {
return mDescription;
}
public boolean isCompleted() {
return mCompleted;
}
public boolean isActive() {
return !mCompleted;
}
public boolean isEmpty() {
return Strings.isNullOrEmpty(mTitle) &&
Strings.isNullOrEmpty(mDescription);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Task task = (Task) o;
return Objects.equal(mId, task.mId) &&
Objects.equal(mTitle, task.mTitle) &&
Objects.equal(mDescription, task.mDescription);
}
@Override
public int hashCode() {
return Objects.hashCode(mId, mTitle, mDescription);
}
@Override
public String toString() {
return "Task with title " + mTitle;
}
}
DataSource、Repository相關代碼佈局:
契約介面定義:
package com.example.android.architecture.blueprints.todoapp.taskdetail;
import com.example.android.architecture.blueprints.todoapp.BasePresenter;
import com.example.android.architecture.blueprints.todoapp.BaseView;
/**
* This specifies the contract between the view and the presenter.
*/
public interface TaskDetailContract {
interface View extends BaseView<Presenter> {
void setLoadingIndicator(boolean active);
void showMissingTask();
void hideTitle();
void showTitle(String title);
void hideDescription();
void showDescription(String description);
void showCompletionStatus(boolean complete);
void showEditTask(String taskId);
void showTaskDeleted();
void showTaskMarkedComplete();
void showTaskMarkedActive();
boolean isActive();
}
interface Presenter extends BasePresenter {
void editTask();
void deleteTask();
void completeTask();
void activateTask();
}
}
)
項目依賴
- Common Android support 庫 (com.android.support.*)
- Android Testing Support 庫 (Espresso, AndroidJUnitRunner…)
- Mockito
- Guava (null checking)
特性
複雜度 - 易理解性
使用架構性框架/庫/工具類:
無
概念複雜度
低,基於Android的純MVP實現。
可測試性
單元測試
高,Presenter可被單元測試,和倉庫與數據源一樣。
UI 測試
高,允許使用fake模塊註入偽造(fake)數據進行測試。
代碼對比
相比於不使用架構的傳統項目,該示例引入了額外的類和介面:Presenter、Repositiy、契約介面等,所以MVP中代碼行數更多一些。
語言 | 文件 | 空行 | 註釋 | 代碼 |
---|---|---|---|---|
Java | 46 | 1075 | 1451 | 3451 |
XML | 34 | 97 | 337 | 601 |
SUM | 80 | 1172 | 1788 | 4052 |
可維護性
簡化功能的維護或新功能的迭代
高
學習成本
低
相關功能代碼能容易查找定位、代碼職責清晰。開發者無需熟悉任何外部的依賴就能對該項目開展工作。
Github項目地址:https://github.com/googlesamples/android-architecture/