Android MVP - Google Sample 解讀 TaskDetail

好啦其實前一篇 Android MVP - Google Sample 解讀 截的圖就是為了接下來這一篇做準備。

android-mvp-google-sample-structure

TaskDetail

因為等下會有很多 code, 所以這裡先放個 index

  • View 介面 BaseView
  • Presenter 介面 BasePresenter
  • Contract 介面
  • Presenter 實作 TaskDetailPresenter
  • View 實作 TaskDetailFragment
  • Model 介面 TasksDataSource
  • Model 實作 TasksRepository

不囉唆,看 code 囉。中間有省略一些,只保留有一些提示意味的部分。

View 介面 BaseView

public interface BaseView<T> {  
  void setPresenter(T presenter);
}

Presenter 介面 BasePresenter

public interface BasePresenter {  
  void start();
}

Contract 介面

public interface TaskDetailContract {  
  interface View extends BaseView<Presenter> {
    // ...

    void hideTitle();

    void showTitle(String title);

    void showEditTask(String taskId);

    void showTaskMarkedComplete();

    void showTaskMarkedActive();

    void showTaskDeleted();
  }

  interface Presenter extends BasePresenter {

    void editTask();

    void completeTask();

    void activateTask();

    void deleteTask();
  }
}

這邊利用 TaskDetailContractView, Presenter 兩個綁起來,我覺得蠻漂亮的。一來可以直接看 Contract 便知道 view 跟 presenter 關係,有利於維護。二來名字簡單(待會可以在 TaskDetailPresenter 看到),畢竟最難的就是命名了。

Presenter 實作 TaskDetailPresenter

/**
 * Listens to user actions from the UI ([email protected] TaskDetailFragment}), retrieves the data and updates
 * the UI as required.
 */
public class TaskDetailPresenter implements TaskDetailContract.Presenter {

  private final TasksRepository mTasksRepository;
  private final TaskDetailContract.View mTaskDetailView;
  @Nullable private String mTaskId;

  /**
   * 利用 constructor 來提供 view 跟 model
   */
  public TaskDetailPresenter(@Nullable String taskId,
            @NonNull TasksRepository tasksRepository,
            @NonNull TaskDetailContract.View taskDetailView) {
    mTaskId = taskId;
    mTasksRepository = checkNotNull(tasksRepository, "tasksRepository cannot be null!");
    mTaskDetailView = checkNotNull(taskDetailView, "taskDetailView cannot be null!");

    mTaskDetailView.setPresenter(this);
  }

  @Override public void start() {
    openTask();
  }

  // ...

  @Override public void deleteTask() {
    if (Strings.isNullOrEmpty(mTaskId)) {
      mTaskDetailView.showMissingTask();
      return;
    }
    mTasksRepository.deleteTask(mTaskId);
    mTaskDetailView.showTaskDeleted();
  }
}

取得了 model 跟 view 後,就在各自的 function 中實作需要做的細節。

View 實作 TaskDetailFragment

請注意這裡還是有透過 Activity 把 Presenter 與 View (fragment) 串起來唷,但是代碼已經非常少了,也就是達到了我們最一開始想要的目的:讓 Activity 只管理 Android Life Cycle 的部分。

/**
 * Displays task details screen.
 */
public class TaskDetailActivity extends AppCompatActivity {

  @Override protected void onCreate(Bundle savedInstanceState) {
    // ...

    // Create the presenter
    new TaskDetailPresenter(
        taskId,
        Injection.provideTasksRepository(getApplicationContext()),
        taskDetailFragment);
  }
}

接下來就是 TaskDetailFragment 去實作 TaskDetailContract.View 的細節。而且 view 本身只是在相對應的 UI 位置,拿 presenter 去做事。

public class TaskDetailFragment extends Fragment implements TaskDetailContract.View {

  private TaskDetailContract.Presenter mPresenter;

  // ...
  @Override public void setPresenter(@NonNull TaskDetailContract.Presenter presenter) {
    mPresenter = checkNotNull(presenter);
  }

  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case R.id.menu_delete:
        mPresenter.deleteTask();
        return true;
    }
    return false;
  }

  @Override public void showCompletionStatus(final boolean complete) {
    Preconditions.checkNotNull(mDetailCompleteStatus);

    mDetailCompleteStatus.setChecked(complete);
    mDetailCompleteStatus.setOnCheckedChangeListener((buttonView, isChecked) -> {
      if (isChecked) {
        mPresenter.completeTask();
      } else {
        mPresenter.activateTask();
      }
    });
  }
}

上面我偷偷用了 lambda ,相信各位大大應該還是可以理解的 :P

Model 介面 TasksDataSource

Model 實作 TasksRepository

public interface TasksDataSource {

  interface LoadTasksCallback {
    // ...
  }

  interface GetTaskCallback {
    // ...
  }

  void getTasks(@NonNull LoadTasksCallback callback);

  void getTask(@NonNull String taskId, @NonNull GetTaskCallback callback);

  void completeTask(@NonNull Task task);

  void completeTask(@NonNull String taskId);

  // ...

  void deleteAllTasks();

  void deleteTask(@NonNull String taskId);
}

最後終於講到 Model 的部分了,不過我想這邊牽涉的範圍又更多了,還是繼續拆解到下一回合吧 :P

先引用一下延伸閱讀的一段話作結:

最后还剩下model层实现,项目中model层最大的特点是被赋予了数据获取的职责,与我们平常model层只定义实体对象截然不同,实例中,数据的获取、存储、数据状态变化都是model层的任务,presenter会根据需要调用该层的数据处理逻辑并在需要时将回调传入。这样model、presenter、view都只处理各自的任务,此种实现确实是单一职责最好的诠释。 -- Android官方MVP架构示例项目解析


延伸閱讀

陳俊安 Andro Chen

Android & Web Developer passionate about UX & Startup. My Dream is to create user-friendly products that could satisfy people and fulfill their imagination about future. 喜歡思考人生的意義。夢想是做出能滿足人們對於未來想像的產品


comments powered by Disqus