Genta Hirauchi

公開日:2020/04/15
更新日:2020/08/03

【Kotlin MVP】todo-mvp-kotlinでMPVを学ぶ #3 | MVPの処理の流れについて

  • MVPって何?
  • MVPの実装方法を1から学びたい

アプリ開発におけるアーキテクチャ(設計)の1つに、MVPというものがあります。

本記事は、Googleが公開している、todo-mvp-kotlinというサンプルアプリを通して、MVPについて学ぶシリーズの第3弾です。

今回は、MVPの処理の流れについて解説していきます。

目次

シーケンス図

前回の記事で、todo-mvp-kotlinアプリが、タスク一覧画面、タスク追加・編集画面、タスク詳細画面、統計画面で構成されていると解説致しました。

今回MVPの処理の流れを解説するにあたり、タスク一覧画面に焦点を当て解説していきますが、基本的な処理の流れは、他の画面も同じです。

ではまず、タスク一覧画面にて、アプリ起動から、タスクの一覧表示までのシーケンスを紹介致します。

TasksActivityのonCreateでは、ViewのTasksFragmentの生成、ModelのTasksRepositoryの生成、PresenterのTasksPresenterの生成を行なっております。

onResumeが呼ばれると、
TasksActivity → TasksFragment → TasksPresenter → TasksRepository → データベース
TasksActivity ← TasksFragment ← TasksPresenter ← TasksRepository ← データベース
という流れで、データの取得を行います。

詳しい処理については、次項で解説いたします。

MVPの処理の流れについて

では、シーケンス図で紹介した処理のコードを詳しく見ていきます。
なお、今回はMVPの処理の流れに焦点をあてているため、それ以外の部分は”(省略)”と記載しております。

TasksActivity.onCreate()

override fun onCreate(savedInstanceState: Bundle?) {
    (省略)

    // TasksFragmentの生成
    val tasksFragment = supportFragmentManager.findFragmentById(R.id.contentFrame)
            as TasksFragment? ?: TasksFragment.newInstance().also {
        replaceFragmentInActivity(it, R.id.contentFrame)
    }

    // TasksRepository、TasksPresenterの生成
    tasksPresenter = TasksPresenter(Injection.provideTasksRepository(applicationContext),
            tasksFragment).apply {
        (省略)
    }
}

TasksActivityのonCreateでは、TasksFragment、TasksRepository、TasksPresenterのインスタンスの生成を行なっております。

TasksRepositoryは、TasksPresenter生成時の引数の、Injection.provideTasksRepository()部分で生成処理が行われております。

TasksFragment.newInstance()

fun newInstance() = TasksFragment()

TasksFragmentのnewInstance()では、TasksFragmentのインスタンスの生成を行なっております。

TasksRepository.getInstance()

@JvmStatic fun getInstance(tasksRemoteDataSource: TasksDataSource,
        tasksLocalDataSource: TasksDataSource): TasksRepository {
    return INSTANCE ?: TasksRepository(tasksRemoteDataSource, tasksLocalDataSource)
            .apply { INSTANCE = this }
}

TasksRepositoryのgetInstance()では、TasksRepositoryのインスタンスがまだ生成されていない場合、新たにインスタンスの生成を行なっております。

getInstance()は、InjectionのprovideTasksRepository()から呼ばれます。

TasksPresenter.TasksPresenter()

class TasksPresenter(val tasksRepository: TasksRepository, val tasksView: TasksContract.View)
    : TasksContract.Presenter {
    (省略)

    init {
        tasksView.presenter = this
    }

    (省略)
}

TasksPresenterのコンストラクタでは、TasksRepositoryとTasksFragment(引数のtasksView)を受け取っております。

また、initにて、TasksFragmentのpresenterに、自身を指定しております。こうすることで、TasksFragmentから、TasksPresenterを呼び出せるようになります。

TasksFragment.onResume()

override fun onResume() {
    super.onResume()
    presenter.start()
}

TasksFragmentのonResume()では、TasksPresenterのstart()を呼び出しております。

TasksPresenter.start()

override fun start() {
    loadTasks(false)
}

override fun loadTasks(forceUpdate: Boolean) {
    // Simplification for sample: a network reload will be forced on first load.
    loadTasks(forceUpdate || firstLoad, true)
    firstLoad = false
}

private fun loadTasks(forceUpdate: Boolean, showLoadingUI: Boolean) {
    (省略)

    tasksRepository.getTasks(object : TasksDataSource.LoadTasksCallback {
        (省略)
    })
}

TasksPresenterのstart()では、自身のloadTasks()を呼び出しております。

loadTasks()では、TasksRepositoryのgetTasks()を呼び出しております。

TasksRepository.getTasks()

override fun getTasks(callback: TasksDataSource.LoadTasksCallback) {
    (省略)

    if (cacheIsDirty) {
        (省略)
    } else {
        // Query the local storage if available. If not, query the network.
        tasksLocalDataSource.getTasks(object : TasksDataSource.LoadTasksCallback {
            override fun onTasksLoaded(tasks: List) {
                refreshCache(tasks)
                callback.onTasksLoaded(ArrayList(cachedTasks.values))
            }

            override fun onDataNotAvailable() {
                getTasksFromRemoteDataSource(callback)
            }
        })
    }
}

TasksRepositoryのgetTasks()では、TasksLocalDataSourceのgetTasks()を呼び出し、データベースからデータの取得を行なっております。

データの取得が終わると、引数で受け取ったコールバックのonTasksLoaded()を呼び出し、結果を返しております。

TasksPresenter.onTasksLoaded()

override fun onTasksLoaded(tasks: List) {
    val tasksToShow = ArrayList()

    (省略)

    processTasks(tasksToShow)
}

private fun processTasks(tasks: List<Task>) {
    if (tasks.isEmpty()) {
        // Show a message indicating there are no tasks for that filter type.
        processEmptyTasks()
    } else {
        // Show the list of tasks
        tasksView.showTasks(tasks)
        // Set the filter label's text.
        showFilterLabel()
    }
}

TasksPresenterのonTasksLoaded()では、自身のprocessTasks()を呼び出しております。

processTasks()では、TasksFragmentのshowTasks()を呼び出しております。

TasksFragment.showTasks()

override fun showTasks(tasks: List) {
    listAdapter.tasks = tasks
    tasksView.visibility = View.VISIBLE
    noTasksView.visibility = View.GONE
}

TasksFragmentのshowTasks()では、タスクを一覧表示する処理が実装されております。

以上が大まかなMVPの処理の流れとなります。大体の流れはつかめましたでしょうか?

次回からは、todo-mvp-kotlinを参考にしながら、実際にTODOアプリを作成していきたいと考えております。
ありがとうございました。