基础
mvc中,c指的是activity等,但它同时又承担了一部分的v工作,显得混乱冗长。而mvp中,将v当作activity,新添加一层做为p,m层不变。
对于官方的demo,整体包结构采用的是按模板划分包的。如下:
其中util指的是一些工具类,data指的是数据层(m层),本demo中使用到bean也是定义在这里的。其余的就是一些各个任务模板。
总结每一个任务模板可以看出:每一个模板中有activity,presenter,contract,fragment四个类以及一些额外的本模板需要的类。各类的功能如下。
整体架构如下:
分类详解
contract
合同。定义了本模板中m与p层,p层与v层之间的接口。如下:
public interface AddEditTaskContract {
interface View extends BaseView<Presenter> {
void showEmptyTaskError();
void showTasksList();
void setTitle(String title);
void setDescription(String description);
boolean isActive();
}
interface Presenter extends BasePresenter {
void createTask(String title, String description);
void updateTask(String title, String description);
void populateTask();
}
}
当然,也可以将该类省略,将其中的View与Presenter单独生成两个接口类。只不过放在一起,有助于一次性了解各个模板之间的接口、交互。
presenter
即p层,用于具体处理业务逻辑。也是m,v层交互的桥梁。并且,由于p的初始化并不是在v中,所以在p的构造函数中需要为v指定当前p是哪个。如下:
public StatisticsPresenter(@NonNull TasksRepository tasksRepository,
@NonNull StatisticsContract.View statisticsView) {
mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null");
mStatisticsView = checkNotNull(statisticsView, "StatisticsView cannot be null!");
mStatisticsView.setPresenter(this);
}
调用了v层的setPresenter,为v层指定p。
fragment
即v层。
activity
本demo中,所有的v层都是由fragment实现的,而activity是为p指定m与v的地方。如:
new StatisticsPresenter(
Injection.provideTasksRepository(getApplicationContext()), statisticsFragment);
从这里可以看出,p层关联的v,m都是在activity中指定的,而不是直接在fragment中直接new一个p。只不过所有的v都需要有一个setPresenter()方法,用于指定该v关联的p是哪个。这个方法的调用在p的构造函数中。
BaseView
很简单,只有一个用来指定p的方法。这是因为所有的p都不是在v中进行的初始化,所以任何一个v都必须得具有设置P的方法。如下:
public interface BaseView<T> {
void setPresenter(T presenter);
}
BasePresenter
它是将所有p的共性操作都抽取出来的。如下:
public interface BasePresenter {
void start();
}
start()用于开始加载数据,可以发现在fragment中onResume中会调用该方法。
其实,还应该有一个end()方法,用于将各个层的引用删除,防止出现内存泄露。
Model
public void getTasks(@NonNull final LoadTasksCallback callback) {
checkNotNull(callback);
// Respond immediately with cache if available and not dirty
if (mCachedTasks != null && !mCacheIsDirty) {
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
return;
}
if (mCacheIsDirty) {
// If the cache is dirty we need to fetch new data from the network.
getTasksFromRemoteDataSource(callback);
} else {
// Query the local storage if available. If not, query the network.
mTasksLocalDataSource.getTasks(new LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
refreshCache(tasks);
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
}
@Override
public void onDataNotAvailable() {
getTasksFromRemoteDataSource(callback);
}
});
}
}
private void getTasksFromRemoteDataSource(@NonNull final LoadTasksCallback callback) {
mTasksRemoteDataSource.getTasks(new LoadTasksCallback() {
@Override
public void onTasksLoaded(List<Task> tasks) {
refreshCache(tasks);//刷新内存缓存
refreshLocalDataSource(tasks);//刷新到数据库中
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
}
@Override
public void onDataNotAvailable() {
callback.onDataNotAvailable();
}
});
}
从上述代码可以看出,在local、remote与p层之间添加了一个缓存区。
总结
扩展
protected void onStartLoading() {
// Deliver any previously loaded data immediately if available.
if (mRepository.cachedTasksAvailable()) {
deliverResult(mRepository.getCachedTasks());//将缓存数据给返回
}
// Begin monitoring the underlying data source.
mRepository.addContentObserver(this);
if (takeContentChanged() || !mRepository.cachedTasksAvailable()) {
// When a change has been delivered or the repository cache isn't available, we force
// a load.
forceLoad();
}
}
逻辑很简单,有缓存数据就返回缓存数据,没有就forceLoad。因为继承的是AsyncTaskLoader,所以forceLoad会调用起doInBackground()。这个方法实现也很简单,直接使用mRepository.getTasks()。这就将数据的加载过程放到了子线程中。