0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

ContentProviderを使って別のアプリ間でデータをやりとりする

Last updated at Posted at 2023-02-03

2023/02/06追記: 以下の方法は別のアプリ間でデータをやりとりできますが、それはapkの署名が同じである場合に限るようです。
異なる署名間のアプリでは、この方法だけではContentProviderのデータを読み取ることができませんでした。
セキュリティ的にはそりゃそうだよね〜という感じです。

データを提供する側のアプリ: ContentProviderを実装
データを読み取る側のアプリ: ContentResolverのAPIを呼び出す

データを提供する側

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    >

    <!-- 自分のContentProviderにアクセスさせるための権限を宣言する -->
    <permission android:name="com.example.contentproviertest.READ_DATA" />

    <application>
        <!-- 自分のContentProviderの宣言を追加 -->
        <provider
            android:name=".MyContentProvider"
            android:authorities="com.example.contentprovidertest.myprovider"
            android:enabled="true"
            android:exported="true"
            android:readPermission="com.example.contentproviertest.READ_DATA"
            />
    </application>
</manifest>

ポイント

  • ContentProvider実装クラスをAndroidManifest.xmlに宣言する
  • <provider>
    • name: クラス名(.で始めた場合はパッケージ名からの相対名)
    • authorities: オーソリティ名。他のアプリと被ってはいけない。パッケージ名のように、ドメイン名の逆順とかで付ける
    • readPermission: <permission>で宣言した名前と揃える

ContentProviderの実装は以下のような感じになります。

com.example.contentprovidertest.MyContentProvider.kt
class MyContentProvider : ContentProvider() {
    override fun onCreate(): Boolean {
        return true
    }

    override fun delete(uri: Uri, selection: String?, selectionArgs: Array<String>?): Int {
        return 0 // TODO 未実装
    }

    override fun getType(uri: Uri): String? {
        return null // TODO 未実装(nullのままでもデータの取得には問題ない)
    }

    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        return null // TODO 未実装(外からデータを読み取るだけならこのままでも問題ない)
    }

    override fun update(
        uri: Uri,
        values: ContentValues?,
        selection: String?,
        selectionArgs: Array<String>?
    ): Int {
        return 0 // TODO 未実装(外からデータを読み取るだけならこのままでも問題ない)
    }

    override fun query(
        uri: Uri,
        projection: Array<String>?,
        selection: String?,
        selectionArgs: Array<String>?,
        sortOrder: String?
    ): Cursor {
        // TODO なんらかの方法でデータを取得し、Cursor型として返却する。
        // データがSQLiteテーブルにあるなら引数Cursorをそのまま返す。
        // データがSQLiteテーブルではないなら、MatrixCursorを使って擬似的なテーブルのような構造でデータを返す。
        // ここでは固定の値を返すサンプル
        return MatrixCursor(
            arrayOf("name", "address"),
        ).apply {
            addRow(arrayOf("Taro", "Tokyo"))
            addRow(arrayOf("Yuka", "Okinawa"))
        }
    }
}

このContentProviderにデータをクエリすると、以下のようなテーブル(のような構造)が返却されるはずです。

name address
Taro Tokyo
Yuka Okinawa

データを読み取る側

ContentProviderのデータを利用する側のAndroidManifestに、「このContentProviderを使いますよ」という宣言を入れる。

<?xml version="1.0" encoding="utf-8"?>
<manifest
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- ContentProvider側の <permission> と同じ名前を <uses-permission> で宣言 -->
    <uses-permission android:name="com.example.contentproviertest.READ_DATA" />

    <queries>
        <!-- ContentProvider側の <provider> の authorities と同じものを指定 -->
        <provider android:authorities="com.example.contentprovidertest.myprovider" />
    </queries>

    <application>
        <!-- 以下、省略 -->
    </application>
</manifest>

ポイント

  • ContentProvider側で指定している<permission><uses-permission>に書く
  • ContentProvider側で指定している<provider>android:authorities<queries>内の<provider android:authorities>に書く

AndroidManifestの定義がきちんとできていれば、別のアプリからContentProviderのデータをクエリできます。その際、content://${authorities}という形式のURIを使用してContentResolver.query()メソッドを呼びます。

val authority = "com.example.contentprovidertest.myprovider"
val uri = "content://$authority"
context.contentResolver.query(Uri.parse(uri), null, null, null, null)?.use { cursor ->
  val nameIndex = cursor.getColumnIndex("name")
  val addressIndex = cursor.getColumnIndex("address")
  while (cursor.moveToNext()) {
    val name = cursor.getString(nameIndex)
    val address = cursor.getString(addressIndex)
  }
}

以上、ContentProviderを使ったアプリ間のデータ共有方法のすごくシンプルな実装を紹介しました。
最低限の説明のために非常にシンプルな呼び出し方を使用しましたが、実際にはデータを識別するためにURIの末尾にパスを指定したり、query()メソッドの残りの引数に欲しいカラム名や条件を絞り込むためのクエリ文字列を入れたりすることが可能です。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?