demo地址:https://github.com/googlesamples/android-architecture
在这个项目里,每个包的分工都很明确,大体上来说,一个包会对应一个界面。一个界面就会对应一个MVP。
M:还是模型层和业务层
V:视图层。Activity或者Fragment,在这份代码里面,Google是把Fragment当作一个V,而不是Activity
P:Prensenter,用来控制V。
按我的理解是,MVP主要就是通过P来解耦M和V,P用来沟通M和V,使得两者不必直接对话,V可以安心的负责显示,与用户的交互,以及数据的操作工作都交给P来完成。这样毫无疑问,java文件会变多,但是整个的代码结构、思路会更加清晰,更容易打理和维护。
M:定义了一个接口TasksDataSource.java,从接口可以看出,主要也就是一些数据的操作,跟以前的MVC区别不大。这个没啥好说的。
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); }
这里将V和P的接口都放在一个***Contract.java中,这样便于管理,切一目了然。
/** * This specifies the contract between the view and the presenter. */ public interface TasksContract { interface View extends BaseView<Presenter> { void setLoadingIndicator(boolean active); void showTasks(List<Task> tasks); void showAddTask(); void showTaskDetailsUi(String taskId); void showTaskMarkedComplete(); void showTaskMarkedActive(); void showCompletedTasksCleared(); void showLoadingTasksError(); void showNoTasks(); void showActiveFilterLabel(); void showCompletedFilterLabel(); void showAllFilterLabel(); void showNoActiveTasks(); void showNoCompletedTasks(); void showSuccessfullySavedMessage(); boolean isActive(); void showFilteringPopUpMenu(); } interface Presenter extends BasePresenter { void result(int requestCode, int resultCode); void loadTasks(boolean forceUpdate); void addNewTask(); void openTaskDetails(@NonNull Task requestedTask); void completeTask(@NonNull Task completedTask); void activateTask(@NonNull Task activeTask); void clearCompletedTasks(); void setFiltering(TasksFilterType requestType); TasksFilterType getFiltering(); } }
//BaseView.java public interface BaseView<T> { void setPresenter(T presenter); } //BasePresenter.java public interface BasePresenter { void start(); }
V:前面说到Google将Fragment看作一个V而不是Activity,所以这里用Fragment来实现V接口。
public class TasksFragment extends Fragment implements TasksContract.View { public TasksFragment() { // Requires empty public constructor } public static TasksFragment newInstance() { return new TasksFragment(); } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mListAdapter = new TasksAdapter(new ArrayList<Task>(0), mItemListener); } @Override public void onResume() { super.onResume(); mPresenter.start(); } @Override public void setPresenter(@NonNull TasksContract.Presenter presenter) { mPresenter = checkNotNull(presenter); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { mPresenter.result(requestCode, resultCode); } @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.tasks_frag, container, false); // Set up tasks view // 也就是通常的findView等一些View的初始化常规操作 ListView listView = (ListView) root.findViewById(R.id.tasks_list); listView.setAdapter(mListAdapter); ………… ………… return root; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.menu_clear: mPresenter.clearCompletedTasks(); break; case R.id.menu_filter: showFilteringPopUpMenu(); break; case R.id.menu_refresh: mPresenter.loadTasks(true); break; } return true; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.tasks_fragment_menu, menu); } /** * Listener for clicks on tasks in the ListView. */ TaskItemListener mItemListener = new TaskItemListener() { @Override public void onTaskClick(Task clickedTask) { mPresenter.openTaskDetails(clickedTask); } @Override public void onCompleteTaskClick(Task completedTask) { mPresenter.completeTask(completedTask); } @Override public void onActivateTaskClick(Task activatedTask) { mPresenter.activateTask(activatedTask); } }; private static class TasksAdapter extends BaseAdapter { //adapter } public interface TaskItemListener { void onTaskClick(Task clickedTask); void onCompleteTaskClick(Task completedTask); void onActivateTaskClick(Task activatedTask); } }
可以看到,所有的交互操作,像点击一个task,标记完成task等,V并不处理,而是转交给P去处理,也就是这样,便将控制的权利交到了P手中。
P:那么P是如何扮演一个沟通者和控制者角色的呢?从代码中可以看到,P手握M和V两者的引用,这样便可以轻松的做好他的工作了:操作数据,控制显示,处理与用户交互。
public class TaskDetailPresenter implements TaskDetailContract.Presenter { private final TasksRepository mTasksRepository; private final TaskDetailContract.View mTaskDetailView; @Nullable private String mTaskId; public TaskDetailPresenter(@Nullable String taskId, @NonNull TasksRepository tasksRepository, @NonNull TaskDetailContract.View taskDetailView) { this.mTaskId = taskId; mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!"); mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!"); mTaskDetailView.setPresenter(this); } @Override public void start() { openTask(); } private void openTask() { if (null == mTaskId || mTaskId.isEmpty()) { mTaskDetailView.showMissingTask(); return; } mTaskDetailView.setLoadingIndicator(true); mTasksRepository.getTask(mTaskId, new TasksDataSource.GetTaskCallback() { @Override public void onTaskLoaded(Task task) { // The view may not be able to handle UI updates anymore if (!mTaskDetailView.isActive()) { return; } mTaskDetailView.setLoadingIndicator(false); if (null == task) { mTaskDetailView.showMissingTask(); } else { showTask(task); } } @Override public void onDataNotAvailable() { // The view may not be able to handle UI updates anymore if (!mTaskDetailView.isActive()) { return; } mTaskDetailView.showMissingTask(); } }); } @Override public void editTask() { if (null == mTaskId || mTaskId.isEmpty()) { mTaskDetailView.showMissingTask(); return; } mTaskDetailView.showEditTask(mTaskId); } @Override public void deleteTask() { mTasksRepository.deleteTask(mTaskId); mTaskDetailView.showTaskDeleted(); } @Override public void completeTask() { if (null == mTaskId || mTaskId.isEmpty()) { mTaskDetailView.showMissingTask(); return; } mTasksRepository.completeTask(mTaskId); mTaskDetailView.showTaskMarkedComplete(); } @Override public void activateTask() { if (null == mTaskId || mTaskId.isEmpty()) { mTaskDetailView.showMissingTask(); return; } mTasksRepository.activateTask(mTaskId); mTaskDetailView.showTaskMarkedActive(); } private void showTask(Task task) { String title = task.getTitle(); String description = task.getDescription(); if (title != null && title.isEmpty()) { mTaskDetailView.hideTitle(); } else { mTaskDetailView.showTitle(title); } if (description != null && description.isEmpty()) { mTaskDetailView.hideDescription(); } else { mTaskDetailView.showDescription(description); } mTaskDetailView.showCompletionStatus(task.isCompleted()); } }
我发现像之前那本《Android编程权威指南》还有Google官方这个例子,对Fragment都情有独钟,都在鼓励使用Fragment而不是Activity,所以Fragment理所当然的成为了V。那么在MVP中Activity干嘛呢?
public class TasksActivity extends AppCompatActivity { private static final String CURRENT_FILTERING_KEY = "CURRENT_FILTERING_KEY"; private DrawerLayout mDrawerLayout; private TasksPresenter mTasksPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tasks_act); // Set up the toolbar. Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar ab = getSupportActionBar(); ab.setHomeAsUpIndicator(R.drawable.ic_menu); ab.setDisplayHomeAsUpEnabled(true); // Set up the navigation drawer. mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); mDrawerLayout.setStatusBarBackground(R.color.colorPrimaryDark); NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); if (navigationView != null) { setupDrawerContent(navigationView); } 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); // Load previously saved state, if available. if (savedInstanceState != null) { TasksFilterType currentFiltering = (TasksFilterType) savedInstanceState.getSerializable(CURRENT_FILTERING_KEY); mTasksPresenter.setFiltering(currentFiltering); } } }
Activity主要用来创建Fragment、初始化P(也就是关联V和P)
在BaseView中有个setPresenter()方法,而通常在P的构造函数中得到V的实例后会调用该方法来实现V和P的绑定。
public TasksPresenter(@NonNull TasksRepository tasksRepository, @NonNull TasksContract.View tasksView) { mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null"); mTasksView = checkNotNull(tasksView, "tasksView cannot be null!"); mTasksView.setPresenter(this); }
在V通过setPresenter得到P后,便在onResume()中调用BasePresenter的start()方法来初始化P,其实也就是初始化UI界面,毕竟P掌管了V的控制权。
@Override public void onResume() { super.onResume(); mPresenter.start(); }