【Android/Kotlin】ContentProviderの挿入・削除・更新

#ContentProviderとは
　ContentProviderはデバイスのストレージにあるDB（データーベース）にアクセスし、データを管理する為に使用されます。具体的には、自身のアプリでDBを使ったり、他アプリのDB（連絡先など）を流用したい時です。今となっては他のアプリと連携させたアプリなど往々にして存在しますが、そのような機能の一端を担えるのがこのContentProviderです。　ContentProviderを導入するか否かの指針については公式のドキュメントに記述があります。以下がその項目です。
・検索フレームワークを用いてカスタム検索候補を提供する。
・アプリデータをウィジェットに公開する
・他のアプリに複雑なデータやファイルを提供する。
・AbstractThreadedSyncAdapterクラス（デバイスとサーバとの間でデータを転送するタスクのコードのカプセル化に使う）、CoursorAdapter（カーソルとビューの架け橋を担う）、CorsorLoader（カーソルからデータを取得する）を使う。
　これらの項目に当てはまる状況の時、ContentProviderが推奨されているようです。

#ContentProviderクラス

 ContentProviderを実際に使うには、ContentProviderクラスを実装したクラスを用意することが必要です。そのインスタンスは様々な処理を経て、デバイスのデータへアクセスすることが可能になります。その際に用いられるメソッドとして、6つの必須メソッドが存在します。以下でそのそれぞれについて解説していきます。

##query()
　このメソッドはプロバイダからデータを取得する際に使用されます。実装すると以下のようになるはずです。

```
override fun query(
        uri: Uri,
        projection: Array<out String>?,
        selection: String?,
        selectionArgs: Array<out String>?,
        sortOrder: String?
    ): Cursor? {
    }
```
引数それぞれについて解説していきます。`uri`はqueryメソッドを使って取り出したいデータがあるデータベースのURIです。具体的には、`content://パッケージ名.provider/テーブル名`で該当するテーブルに、`content://パッケージ名.provider/テーブル名/ID`で該当するテーブルの該当するIDを持つ行にアクセスすることが出来ます。`projection`は対象とするテーブルのカラム名を持つ文字列配列で、queryはここで指定したカラムのデータのみを取得します。`selection`はSQLのWHERE文における、`WHERE =`に続く文字列を渡してあげることで、テーブルに対してWHERE文を使ってデータの選択を行うことが出来ます。`selectionArgs`も`selection`と同じ類のものですが、SQLインジェクションを防ぐためのもので、`selection`と連携して使います。本筋とはズレてしまうので、詳しい説明は割愛で。`setOrder`は取得したデータにおける行の順番を入れ替えることが出来ます。`ORDER BY`に相当する指定を行うことが可能で、例えば`COLUMN_NAME ASC`という文字列を渡したとすると、返ってくるデータは`COLUMN_NAME`というカラムが昇順になるよう順番が入れ替えられます。
　このメソッドの返り値は`Cursor`オブジェクトです。

##insert()
　このメソッドはプロバイダに対して新しい行を挿入する時に使われます。非常にシンプルに扱うことが出来ます。

```
override fun insert(uri: Uri, values: ContentValues?): Uri? {}
```
`uri`は`query`と同じく、データを挿入したいテーブルのURIを渡してあげます。`value`は`ContentValues`のオブジェクトで、データベースに挿入したいデータをカラムの名前とデータのペアで保持します。`ContentValues`がNullableなように、この値はnullになることが可能です。
　返り値は新しく挿入されたアイテムのURIです。具体的には、先ほど説明した末尾がIDのタイプのURIです。もしデータの挿入が上手く行かなかったら、この値はnullになります。

##update()
　このメソッドはプロパイダ内の既存の行を更新します。引数で更新する行の細かな指定が可能です。

```
override fun update(
        uri: Uri,
        values: ContentValues?,
        selection: String?,
        selectionArgs: Array<out String>?
    ): Int {
```
引数はほぼほぼ`query`メソッドと同じです。唯一異なるのは`values`がある点です。しかし、`values`に関しては`insert`メソッドで説明したものと大体同じで、更新に使われるデータを渡します。
　返り値は実際に更新した行の数をInt型で渡してくれます。

##delete()
　プロパイダから行を削除する為に使用されます。

```
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int 
```
引数や返り値に関しては`update`メソッドとほぼ同じです。`value`が引数にない点だけ異なります。

##getType()
　与えられたURIに対するMIMEタイプを返します。

```
override fun getType(uri: Uri): String?
```
MIMEタイプとは、拡張子と対になるような概念で、.pngというファイルならimage/pngというMIMEタイプを持ち、データの種類を表してくれます。どのような場面で必要になるかというと、`ContentProvider`を通してやり取りを行うデータに画像や文字列が混在する時、このメソッドを用いてそのそれぞれの処理を分けることが可能です。返り値は先述したように、MIMEタイプを文字列で返してくれます。

##onCreate()
　プロパイダを初期化するメソッドです。プロバイダが作成された時、Androidシステムがこのメソッドを即座に呼び出してくれます。

```
override fun onCreate(): Boolean {
        Log.d(TAG, "onCreate: starts")
        return true
    }
```
　このメソッドはデータベースとやり取りを行ったり、ユーザーに何かを提供したりといった事はありません。リファレンスに極力冗長にならないようにと書かれているように、不用意にコードを書くとプロバイダの起動が遅くなってしまいます。返り値はtrueを返せば正常に動作します。

#ContentResolver
　`ContentProvider`を実装したクラスだけでは当然データのやり取りを行うことは出来ません。データのやりとりを行わせたいアクティビティで、`ContentResolver`づてに`ContentProvider`のメソッドを呼び出す必要があります。とはいってもそこまで複雑ではなく、単純なやり取りであれば非常に簡単なコードで済みます。以下は挿入・削除・更新の簡単な例です。

```
private fun testInsert() {
        val values = ContentValues().apply {
            put(Column_NAME, "INSERT")
        }
﻿
        val uri = contentResolver.insert(TEST_URI, values)
    }

private fun testUpdate() {
        val values = ContentValues().apply {
            put(Column_NAME, "UPDATE")
        }
﻿
        val selection = "Colimn_NAME" + " = ?"
        val selectionArgs = arrayOf("INSERT")

        val rowsAffected = contentResolver.update(TEST_URI, values, selection, selectionArgs)
    }

private fun testDelete() {
﻿
        val selection = "Column_NAME" + " = ?"
        val selectionArgs = arrayOf("UPDATE")
﻿
        val rowsAffected = contentResolver.delete(TEST_URI, selection, selectionArgs)
    }
```
どのメソッドも、`ContentResolver`を使ってクエリをDBに送っていることが何となくわかると思います。このように、`ContentProvider`での実装は`ContentResolver`を経由して扱います。
　具体的に各メソッドの説明をします。`testInsert`メソッドは`Column_NAME`というカラムに`INSERT`という文字列を持つ新たな行を挿入するためのメソッドです。`testUpdate`は`Column_NAME`が`INSERT`という行全てを`UPDATE`という文字列に更新します。`testDelete`は`Column_NAME`が`UPDATE`という行を全て削除します。
　`selection`と`selectionArgs`は↑のように使ってあげることで、SQLインジェクションを防ぐ事が出来ます。`selection`の`?`が順に`selectionArgs`の配列に格納されます。`WHERE =`に続くSQL文において、実際に指定する値については`selectionArgs`に格納しているだけです。
　これらのメソッドをアクティビティで使ってあげるだけで、簡単にプロバイダの操作が出来ます。ぜひ参考にしてみてください。