Android Kotlin基礎講座 06.2: コルーチンとRoom

タスク:ViewModelを追加する

現在アプリにはデータベースとUIが存在します。ここからはデータを収集し、そのデータをデータベースに追加、そしてデータを表示する必要があります。これらの働きはすべてビューモデル内でなされます。sleep-trackerビューモデルによってボタンクリック操作、DAOを通じたデータベースとのやり取り、LiveDataを通じたUIへのデータの提供などが行われます。全てのデータベース操作はメインUIスレッドからは切り離される必要があり、コルーチンを使ってそれを実現していきます。

ステップ1:SleepTrackerViewModelを追加する

  1. sleeptrackerパッケージのSleepTrackerViewModel.ktを開いてください。
  2. SleepTrackerViewModelクラスを見てください。以下のコードのようにスターターアプリではなっているはずです。このクラスがAndroidViewModelを継承していることを確認してください。このクラスはViewModelと同じですが、コンストラクタのパラメータとしてapplicationコンテクストを受け取り、それをプロパティとして使えるようにします。これは後に必要になります。
class SleepTrackerViewModel(
       val database: SleepDatabaseDao,
       application: Application) : AndroidViewModel(application) {
}

ステップ2:SleepTrackerViewModelFactoryを追加する

  1. sleeptrackerパッケージのSleepTrackerViewModelFactory.ktを開いてください。
  2. 以下に記されたファクトリー用のコードを確認してください。
class SleepTrackerViewModelFactory(
       private val dataSource: SleepDatabaseDao,
       private val application: Application) : ViewModelProvider.Factory {
   @Suppress("unchecked_cast")
   override fun <T : ViewModel?> create(modelClass: Class<T>): T {
       if (modelClass.isAssignableFrom(SleepTrackerViewModel::class.java)) {
           return SleepTrackerViewModel(dataSource, application) as T
       }
       throw IllegalArgumentException("Unknown ViewModel class")
   }
}

以下のことを確認してください。

  • SleepTrackerViewModelFactoryはViewModelと同じ引数を受け取り、ViewModelProvider.Factoryを継承しています。
  • ファクトリーの内側では、create()をオーバーライドしており、これは引数としてどのクラス型でも受け取り、ViewModelを返します。
  • create()の中では、SleepTrackerViewModelクラスが利用可能かをチェックしており、もし利用可能なSleepTrackerViewModelが存在する場合、それのインスタンスを返します。そうでない場合は例外が投げられます。

Tip: これはほとんどボイラープレートコードなので、別のビューモデルファクトリー用にコードを再利用することができます。

ステップ3:SleepTrackerFragmentをアップデートする

  1. SleepTrackerFragment内で、applicationコンテクストの参照を取得します。参照はonCreateView()ないのbindingの下に置いてください。

    requireNotNull関数は値がnullの場合にIllegalArgumentExceptionを投げます。
val application = requireNotNull(this.activity).application
  1. DAOの参照を通じてデータソースへの参照が必要です。onCreateView()内のreturn文の前でdataSourceを定義してください。データベースのDAOへの参照を得るために、SleepDatabase.getInstance(application).sleepDatabaseDaoを使ってください。
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
  1. onCreateView()内のreturn文の前で、viewModelFactoryのインスタンスを作成してください。それにdataSourceとapplicationを渡す必要があります。
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)
  1. これでファクトリーが手に入りました。次にSleepTrackerViewModelの参照を取得します。SleepTrackerViewModel::class.javaパラメーターはこのオブジェクトのランタイムJavaクラスを参照しています。
val sleepTrackerViewModel =
       ViewModelProvider(
               this, viewModelFactory).get(SleepTrackerViewModel::class.java)
  1. 最終的なコードは以下のようになります。
// Create an instance of the ViewModel Factory.
val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao
val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)

// Get a reference to the ViewModel associated with this fragment.
val sleepTrackerViewModel =
       ViewModelProvider(
               this, viewModelFactory).get(SleepTrackerViewModel::class.java)

以下がここまでのonCreateView()メソッドになります。

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {

        // Get a reference to the binding object and inflate the fragment views.
        val binding: FragmentSleepTrackerBinding = DataBindingUtil.inflate(
                inflater, R.layout.fragment_sleep_tracker, container, false)

        val application = requireNotNull(this.activity).application

        val dataSource = SleepDatabase.getInstance(application).sleepDatabaseDao

        val viewModelFactory = SleepTrackerViewModelFactory(dataSource, application)

        val sleepTrackerViewModel =
                ViewModelProvider(
                        this, viewModelFactory).get(SleepTrackerViewModel::class.java)

        return binding.root
    }

ステップ4:ビューモデル用のデータバインディングを追加する

基本的なViewModelを配置したら、SleepTrackerFragmentでデータバインディングの設定を完了して、ViewModelをUIに接続する必要があります。

fragment_sleep_tracker.xmlレイアウトファイル内:

  1. <data>ブロック内で、SleepTrackerViewModelクラスを参照する<variable>を作成してください。
<data>
   <variable
       name="sleepTrackerViewModel"
       type="com.example.android.trackmysleepquality.sleeptracker.SleepTrackerViewModel" />
</data>

SleepTrackerFragment内:

  1. 現在のアクティビティをバインディングのライフサイクルオーナーとして設定してください。以下のコードをonCreateView()メソッドのreturn文の前に追加してください。
binding.setLifecycleOwner(this)
  1. sleepTrackerViewModelバインディング変数をsleepTrackerViewModelに代入してください。以下のコードをonCreateView()のSleepTrackerViewModelを作成しているコードの下に追加してください。
binding.sleepTrackerViewModel = sleepTrackerViewModel
  1. もしかしたらバインディングオブジェクトを再生成する必要があるためにエラーが表示されるかもしれません。その場合、プロジェクトをクリーンアップ、リビルドしてエラーを取り除いてください。
  2. エラーが表示されず、アプリが起動するかビルドして確認してください。