一逛揩、Model層結(jié)構(gòu)
Model層類(lèi)位于data包下,組織結(jié)構(gòu)如圖:
二麸俘、Model層分析
1. 實(shí)體類(lèi)
Tasks作為實(shí)體類(lèi)辩稽,定義的屬性和方法,具體可以查看代碼
2. 接口
TasksDataSource定義了Model的回調(diào)接口和方法
public interface TasksDataSource {
interface LoadTasksCallback {
void onTasksLoaded(List<Task> tasks);
void onDataNotAvailable();
}
interface GetTaskCallback {
void onTaskLoaded(Task task);
void onDataNotAvailable();
}
void getTasks(@NonNull LoadTasksCallback callback);
void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);
void saveTask(@NonNull Task task);
void completeTask(@NonNull Task task);
void completeTask(@NonNull String taskId);
void activateTask(@NonNull Task task);
void activateTask(@NonNull String taskId);
void clearCompletedTasks();
void refreshTasks();
void deleteAllTasks();
void deleteTask(@NonNull String taskId);
}
3.實(shí)現(xiàn)類(lèi)
3.1 TasksLocalDataSource
TasksLocalDataSource是TasksDataSource的本地?cái)?shù)據(jù)源實(shí)現(xiàn)類(lèi)从媚,用于從本地?cái)?shù)據(jù)庫(kù)獲取數(shù)據(jù)逞泄,采用單例模式,并且有兩個(gè)幫助類(lèi)TasksDbHelper和TasksPersistenceContract用于從操作數(shù)據(jù)庫(kù)
3.1 TasksRemoteDataSource
TasksRemoteDataSource是TasksDataSource的遠(yuǎn)程數(shù)據(jù)源實(shí)現(xiàn)類(lèi)拜效,用于從服務(wù)端獲取數(shù)據(jù)(demo里只是模仿從服務(wù)端獲扰缰凇),采用單例模式
3.1 TasksRepository
Repository也是TasksDataSource的實(shí)現(xiàn)類(lèi)紧憾,其中持有兩個(gè)兩個(gè)TasksDataSource對(duì)象到千,一般為一個(gè)TasksLocalDataSource對(duì)象和一個(gè)TasksRemoteDataSource對(duì)象,用于統(tǒng)一管理獲取數(shù)據(jù)的方式赴穗,采用單例模式
public class TasksRepository implements TasksDataSource {
private static TasksRepository INSTANCE = null;
// 遠(yuǎn)程數(shù)據(jù)源對(duì)象
private final TasksDataSource mTasksRemoteDataSource;
// 本地?cái)?shù)據(jù)源對(duì)象
private final TasksDataSource mTasksLocalDataSource;
// 存放緩存數(shù)據(jù)
Map<String, Task> mCachedTasks;
// 緩存數(shù)據(jù)是否可用
boolean mCacheIsDirty = false;
// 構(gòu)造方法
private TasksRepository(@NonNull TasksDataSource tasksRemoteDataSource,
@NonNull TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = checkNotNull(tasksRemoteDataSource);
mTasksLocalDataSource = checkNotNull(tasksLocalDataSource);
}
// 獲取TasksRepository實(shí)例憔四,采用單例模式
public static TasksRepository getInstance(TasksDataSource tasksRemoteDataSource,
TasksDataSource tasksLocalDataSource) {
if (INSTANCE == null) {
INSTANCE = new TasksRepository(tasksRemoteDataSource, tasksLocalDataSource);
}
return INSTANCE;
}
public static void destroyInstance() {
INSTANCE = null;
}
// 獲取數(shù)據(jù)
@Override
public void getTasks(@NonNull final LoadTasksCallback callback) {
checkNotNull(callback);
// 如果緩存可用,則立即返回內(nèi)存中的數(shù)據(jù)
if (mCachedTasks != null && !mCacheIsDirty) {
callback.onTasksLoaded(new ArrayList<>(mCachedTasks.values()));
return;
}
if (mCacheIsDirty) {
// 如果緩存數(shù)據(jù)已經(jīng)不可用般眉,則從服務(wù)端更新數(shù)據(jù)
getTasksFromRemoteDataSource(callback);
} else {
// 從數(shù)據(jù)庫(kù)獲取數(shù)據(jù)了赵,如果沒(méi)有,則從服務(wù)端獲取
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);
}
});
}
}
// 從服務(wù)端獲取數(shù)據(jù)
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();
}
});
}
}
可以看到煤篙,在實(shí)例化TasksRepository時(shí)需要傳入兩個(gè)TasksDataSource對(duì)象斟览;在獲取數(shù)據(jù)時(shí)先從緩存中獲取,如果緩存中沒(méi)有辑奈,則從數(shù)據(jù)庫(kù)獲取,數(shù)據(jù)庫(kù)沒(méi)有已烤,再?gòu)姆?wù)端獲取鸠窗,若為強(qiáng)制刷新,則直接從服務(wù)端獲取胯究。
三稍计、Model使用
1.TasksDataSource的實(shí)例化
在MVP中,Presenter對(duì)象持有Model對(duì)象裕循,如:
public class AddEditTaskPresenter implements AddEditTaskContract.Presenter,
TasksDataSource.GetTaskCallback {
// TasksDataSource對(duì)象
@NonNull
private final TasksDataSource mTasksRepository;
// 實(shí)例化時(shí)傳入TasksDataSource對(duì)象
public AddEditTaskPresenter(@Nullable String taskId, @NonNull TasksDataSource tasksRepository,
@NonNull AddEditTaskContract.View addTaskView) {
mTaskId = taskId;
mTasksRepository = checkNotNull(tasksRepository);
mAddTaskView = checkNotNull(addTaskView);
mAddTaskView.setPresenter(this);
}
}
Presenter在實(shí)例化是需要傳入TasksDataSource對(duì)象臣嚣,Presenter是在Activity中完成實(shí)例化的
public class AddEditTaskActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.addtask_act);
...
// 實(shí)例化Presenter,傳入TasksDataSource對(duì)象
new AddEditTaskPresenter(
taskId,
Injection.provideTasksRepository(getApplicationContext()),
addEditTaskFragment);
}
}
可以看到TasksDataSource對(duì)象是由Injection類(lèi)的provideTasksRepository方法生成
2.Gradle動(dòng)態(tài)配置
在main/java目中并沒(méi)有Injection類(lèi)剥哑,通過(guò)查看app/build.gradle文件可以知道硅则,項(xiàng)目有兩個(gè)生產(chǎn)版本:mock和prod,如下:
productFlavors {
mock {
applicationIdSuffix = ".mock"
}
prod {
}
}
在項(xiàng)目的目錄結(jié)構(gòu)中也有對(duì)應(yīng)的src/mock和src/prod兩個(gè)目錄目錄
在mock和prod中?分別有一個(gè)Injection類(lèi)株婴,這里利用了gradle中的productFlavor創(chuàng)建了多個(gè)版本怎虫,其中每個(gè)版本都可以在工程src目錄下建立與main同級(jí)的文件夾,文件夾名字就是ProductFlavor的名字,可以在其中創(chuàng)建java目錄并添加相應(yīng)的java文件大审,不用的版本可以有相同名字不同實(shí)現(xiàn)的類(lèi)蘸际,具體可查看Android Plugin for Gradle。
prod目錄下的Injection
public class Injection {
public static TasksRepository provideTasksRepository(@NonNull Context context) {
checkNotNull(context);
return TasksRepository.getInstance(TasksRemoteDataSource.getInstance(),
TasksLocalDataSource.getInstance(context));
}
}
mock目錄下的Injection
public class Injection {
public static TasksRepository provideTasksRepository(@NonNull Context context) {
checkNotNull(context);
return TasksRepository.getInstance(FakeTasksRemoteDataSource.getInstance(),
TasksLocalDataSource.getInstance(context));
}
}
可以看到:
prod的Injection創(chuàng)建TasksRepository實(shí)例時(shí)傳入的是TasksRemoteDataSource實(shí)例和TasksLocalDataSource實(shí)例
mock的Injection創(chuàng)建TasksRepository實(shí)例時(shí)傳入的是FakeTasksRemoteDataSource實(shí)例和TasksLocalDataSource實(shí)例徒扶,其中FakeTasksRemoteDataSource是在mock目錄下創(chuàng)建的TasksDataSource的實(shí)現(xiàn)類(lèi)粮彤。
通過(guò)這種方式可以實(shí)現(xiàn)不同版本注入不同的TasksDataSource實(shí)現(xiàn)類(lèi)。