公開日:2020/04/24更新日:2020/08/03
【Kotlin MVP】MVPアーキテクチャで、TODOアプリを作成する #5

アプリ開発におけるアーキテクチャ(設計)の1つに、MVPというものがあります。
本記事は、ToDoアプリの作成を通して、MVPについて学習するシリーズの第5弾です。
第5弾の今回は、Repositoryを実装します。
なお、本シリーズで実装するTODOアプリのソースコードは、todo-sample | GitHubにて公開しております。
目次
Repositoryの実装
ライブラリの導入
Repositoryを実装するにあたりまして、RxJavaを使用したいと思います。
RxJavaを使用するために、build.gradleを修正し、ライブラリの導入を行います。
- build.gradle
dependencies {
(省略)
implementation "androidx.room:room-rxjava2:2.2.5"
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
}
Daoの修正
続いて、TaskDaoの修正を行います。
- TaskDao.kt
@Dao
interface TaskDao {
@Query("SELECT * FROM task")
fun getAll(): Single<List<Task>>
@Insert
fun insert(task: Task): Completable
@Update
fun update(task: Task): Completable
@Delete
fun delete(task: Task): Completable
}
getAll()では、Singleを返すようにしました。 処理が成功した場合はonSuccessが、失敗するとonErrorが呼び出し元で呼ばれます。
onSuccessでは、指定した戻り値(サンプルではList<Task>)を受け取ることができ、onErrorでは、Throwableを受け取ることができます。
insert()update()delete()では、Completableを返すようにしました。処理が問題なく実行されるとonCompleteが、失敗するとonErrorが呼び出し元で呼ばれます。
onCompleteには引数はなく、onErrorでは、Throwableを受け取ることができます。
参考:Room 🔗 RxJava – Android Developers – Medium
interfaceの実装
続いて、Repositoryクラスでimplementするinterfaceを実装します。取得、挿入、更新、削除のメソッドと、それらを呼んだ後のCallbackを定義します。
- TaskDataSource.kt
interface TaskDataSource {
interface LoadTaskCallback {
fun onLoadTasks(tasks: List<Task>)
fun onError(error: Throwable)
}
interface Callback {
fun onSuccess()
fun onError(error: Throwable)
}
fun loadTasks(callback: LoadTaskCallback)
fun insertTask(task: Task, callback: Callback)
fun updateTask(task: Task, callback: Callback)
fun deleteTask(task: Task, callback: Callback)
}
LoadTaskCallbackは、取得処理のCallbackです。loadTasks()の結果通知に使用します。
PresenterクラスにてOverrideしておき、取得処理が成功した場合は、onLoadTasks()でタスク一覧を返し、失敗した場合は、onError()でThrowableを返します。
Callbackは、挿入、更新、削除のCallbackです。loadTasks()、updateTask()、deleteTask()の結果通知に使用します。
PresenterクラスにてOverrideしておき、挿入、更新、削除処理が成功した場合は、onSuccess()を呼び出し、失敗した場合は、onError()でThrowableを返します。
Repositoryの実装
続いて、Repositoryクラスを実装します。Repositoryクラスでは、先ほどのTaskDataSourceをimplementし、取得、挿入、更新、削除処理を実装します。
- TaskRepository.kt
class TaskRepository(private val taskDao: TaskDao): TaskDataSource {
val disposables = CompositeDisposable()
override fun loadTasks(callback: TaskDataSource.LoadTaskCallback) {
taskDao.getAll()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onSuccess = {
callback.onLoadTasks(it)
},
onError = {
callback.onError(it)
}
).addTo(disposables)
}
override fun insertTask(task: Task, callback: TaskDataSource.Callback) {
taskDao.insert(task)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onComplete = {
callback.onSuccess()
},
onError = {
callback.onError(it)
}
).addTo(disposables)
}
override fun updateTask(task: Task, callback: TaskDataSource.Callback) {
taskDao.update(task)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onComplete = {
callback.onSuccess()
},
onError = {
callback.onError(it)
}
).addTo(disposables)
}
override fun deleteTask(task: Task, callback: TaskDataSource.Callback) {
taskDao.delete(task)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeBy(
onComplete = {
callback.onSuccess()
},
onError = {
callback.onError(it)
}
).addTo(disposables)
}
}
各メソッドでは、RxJavaの処理が実装されておりますが、詳しい解説は省かせて頂きます。TaskDaoの処理が成功すると、onSuccessやonCompleteが、失敗すると、onErrorが呼ばれるという認識をしていただければと思います。
onLoadTasks()では、取得処理が成功すると、TaskDataSource.LoadTaskCallbackのonLoadTasks()を呼ぶように実装しております。”it”は、List<Task>を参照しております。失敗した場合は、onErrorを呼び出します。”it”はThrowableを参照しております。
insertTask()、updateTask()、deleteTask()では、挿入、更新、削除処理が成功すると、TaskDataSource.CallbackのonSuccess()を呼ぶように実装しております。失敗した場合は、onErrorを呼び出します。”it”はThrowableを参照しております。
この章の修正内容はRepositoryの実装 | GitHubからも確認頂けます。
Presenter、Viewの修正
Presenterの修正
これまで、TaskDaoの処理を呼び出し、データベースにアクセスしていた部分を、Repositoryの処理を呼び出すように修正します。
今回、新たにエラーのハンドリングが追加されましたので、まずは、MainFragmentへエラーを通知するためのCallbackを、TasksContractのViewにを追加します。
- TaskContract.kt
interface TaskContract {
interface View {
fun onLoadTasks(tasks: List<Task>)
fun showError(message: String?)
}
(省略)
}
続いて、TaskPresenterを修正していきます。
- TaskPresenter.kt
class TaskPresenter(private val taskRepository: TaskRepository, private val view: TaskContract.View): TaskContract.Presenter {
override fun loadTasks() {
taskRepository.loadTasks(object : TaskDataSource.LoadTaskCallback {
override fun onLoadTasks(tasks: List<Task>) {
view.onLoadTasks(tasks)
}
override fun onError(error: Throwable) {
view.showError(error.message)
}
})
}
override fun insertTask(description: String) {
val task = Task(0, 0, description)
taskRepository.insertTask(task, object : TaskDataSource.Callback {
override fun onSuccess() {
loadTasks()
}
override fun onError(error: Throwable) {
view.showError(error.message)
}
})
}
override fun updateTaskState(task: Task) {
task.state++
if (task.state > 2) task.state = 0
taskRepository.updateTask(task, object : TaskDataSource.Callback {
override fun onSuccess() {
loadTasks()
}
override fun onError(error: Throwable) {
view.showError(error.message)
}
})
}
override fun updateTaskDescription(task: Task, description: String) {
task.description = description
taskRepository.updateTask(task, object : TaskDataSource.Callback {
override fun onSuccess() {
loadTasks()
}
override fun onError(error: Throwable) {
view.showError(error.message)
}
})
}
override fun deleteTask(task: Task) {
taskRepository.deleteTask(task, object : TaskDataSource.Callback {
override fun onSuccess() {
loadTasks()
}
override fun onError(error: Throwable) {
view.showError(error.message)
}
})
}
}
これまで、コンストラクタで、TaskDaoを受け取っていたのですが、その部分を、TaskRepositoryを受け取るように修正しました。
loadTasks()では、TaskRepositoryのloadTasks()を呼びだしております。引数の設定部分では、object : TaskDataSource.LoadTaskCallback {}という形で、無名クラスによるCallbackの実装を行なっております。
処理が成功した場合は、onLoadTasks()が呼ばれ、TaskContract.ViewのonLoadTasks()を呼び出し、MainFragmentへタスク一覧を渡すように実装しております。
処理が失敗した場合は、onError()が呼ばれ、TaskContract.ViewのshowError()を呼び出し、MainFragmentへエラー内容を渡すように実装しております。
タスクの挿入、更新、削除に関しましては、取得処理と同じような実装であるため、解説を割愛させて頂きます。
Viewの修正
最後に、MainFragmentの修正を行います。
- MainFragment.kt
class MainFragment : Fragment(), TaskContract.View {
(省略)
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
(省略)
val db = TaskDatabase.getInstance(context)
presenter = TaskPresenter(TaskRepository(db.taskDao()), this)
presenter.loadTasks()
(省略)
}
(省略)
override fun showError(message: String?) {
Toast.makeText(context, message, Toast.LENGTH_SHORT).show()
}
(省略)
}
TaskPresenterの生成時に、TaskRepositoryを渡すようにしました。
また、新たに追加したCallbackの、showErrorをOverrideし、トーストでエラー内容が表示されるように実装しました。
実行結果
この章の修正内容はPresenter、Viewの修正 | GitHubからも確認頂けます。
今回の実装はここまでとなります。これで、MVPの処理の流れに関する部分は実装が完了しました。
次回は、タスクの追加や更新時に、内容を入力するダイアログ表示の実装していく予定です。おそらくシリーズラストの記事になると思います。
ありがとうございました。