はじめに
前回は、入力した値をリスト表示するところまで行いましいた。今回は、そのデータをデータベースに保存するようにします。
目標
Roomを追加するだけなので、今回はレイアウトの変更はありません。
アプリを閉じて、再起動したときにデータが保持されてるかを確認してください。
環境
- Android Studio 3.6.3
- Kotlin 1.3.72
作成手順
Gradle
apply plugin: 'kotlin-kapt'
...
dependencies {
...
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"
def room_version = "2.2.5"
implementation "androidx.room:room-runtime:$room_version"
implementation "androidx.room:room-ktx:$room_version"
kapt "androidx.room:room-compiler:$room_version"
}
実装
Roomは以下の3つの要素で構成されています。
- Entity
- Dao (DataAccessObjects)
- Room Database
Entity
データベース内のテーブルを示します。
各Entityは、少なくとも一つの主キーを定義する必要があります。それは@PrimaryKey
アノテーションで定義できます。
autoGenerate
を設定すると自動でPrimaryKeyを発行してくれます。
@Entity
data class Todo(
@PrimaryKey(autoGenerate = true) val tid: Int,
@ColumnInfo(name = "todo_title") val todoTitle: String
)
Dao
Daoではデータベースにアクセスするためのメソッドを定義します。
Interfaceに@Dao
をつけるだけで定義できます。
今回は、挿入と全データの取得を行うメソッドを定義しました。
getAll()
の戻り値をLiveData
にすることにより、Room
がデータベースに変更があった場合に通知してくれる内部コードを生成してくれるらしいです。とても便利ですね。
@Dao
interface TodoDao {
@Insert
suspend fun insert(todo: Todo)
@Query("SELECT * FROM Todo")
fun getAll(): LiveData<List<Todo>>
}
Database
データベースの定義では、抽象クラスに@Database
をつけると定義できます。
先程定義したEntityをentities
に指定します。
そしてDaoを取得するためのメソッドを用意します。
@Database(entities = [Todo::class], version = 1)
abstract class TodoDatabase : RoomDatabase() {
abstract fun todoDao(): TodoDao
}
Repository
ViewModelとデータベースの中継としてRepositoryを作成します。
class TodoRepository(private val todoDao: TodoDao) {
val allTodoList = todoDao.getAll()
@WorkerThread
suspend fun insert(todo: Todo) {
todoDao.insert(todo)
}
}
ViewModel
Repositoryからデータを受け取って処理します。
データベースの処理はメインスレッドで実行することができないので、Coroutineを使って非同期処理を行います。
class MainViewModel(private val repository: TodoRepository) : ViewModel() {
val todoList = repository.allTodoList
fun insert(todo: Todo) = viewModelScope.launch {
repository.insert(todo)
}
}
Activity
class MainActivity : AppCompatActivity() {
private lateinit var mainViewModel: MainViewModel
private lateinit var adapter: RecyclerAdapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val db = Room.databaseBuilder(this, TodoDatabase::class.java, "database_name").build()
val dao = db.todoDao()
val repository = TodoRepository(dao)
mainViewModel = MainViewModel(repository)
adapter = RecyclerAdapter()
main_recycler_view.layoutManager = LinearLayoutManager(this)
main_recycler_view.adapter = adapter
main_recycler_view.setHasFixedSize(true)
add_item_button.setOnClickListener {
mainViewModel.insert(Todo(0, submit_text.text.toString()))
}
mainViewModel.todoList.observe(this, Observer {
adapter.setItem(it)
})
}
}
Adapter
新しく加えたデータをAdapterにセットします。
class RecyclerAdapter : RecyclerView.Adapter<RecyclerAdapter.RecyclerViewHolder>(){
private val todoList = mutableListOf<Todo>()
fun setItem(items: List<Todo>) {
todoList.clear()
todoList.addAll(items)
notifyDataSetChanged()
}
...
}
おわりに
Room
を使用してデータベースに値を保存することができました。
次回は削除機能の追加をしたいと思います。
最近勉強したDaggerを使って、DIしてみてもいいかな!