アプリ間のデータ共有

Androidではアプリ間のデータを共有するのにContentProviderが使われることが多いです。

例えばこれを使えば電話帳のデータなど複数のアプリで使いまわしたいデータをデーターベース上に保管できます。

ここではその方法を紹介します。

  1. データベースの作成
  2. プロバイダークラスの作成
  3. AndroidManifestの設定
  4. 使い方

データベースの作成

初めにデータを保存するデータベースを作成します。

例として次のようなデータベースをSQLiteOpenHelperで作ってみました。

/**ContentProviderでデータの記録に使うデータベース*/
private class ProviderDBHelper extends SQLiteOpenHelper
{
    private static final String DB_NAME = "my_db";
    private static final int DB_VERSION = 1;
    
    public static final String TABLE_NAME = "table";
    public static final String NAME_KEY = "name";
    
    public SharedInAppItemDBHelper(Context context)
    {
        super(context, DB_NAME, null, DB_VERSION);
    }
    
    @Override
    public void onCreate(SQLiteDatabase db)
    {
        db.execSQL(
            "CREATE TABLE " + TABLE_NAME + "(" +
            "    _id INTEGER PRIMARY KEY AUTOINCREMENT, " + 
            "    " + NAME_KEY + " text" + 
            ");");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){}
}

こんな感じでデータベースクラスを作ります。

onCreateでデータベースを初期化するときは必ず_idカラムが必要なので忘れずに書きましょう。

このようなデータベースにContentProviderがアクセスしてデータを記録したり、拾ってきたりています。

ContentProvidrの実装

次は独自のContentProviderの実装です。

ContentProviderには次の5つのメソッドをオーバーロードする必要があります。

最低限、この5つだけは実装しないといけません。

そのようにして実装したのが次のMyProviderです。

public class MyProvider extends ContentProvider 
{
    private ProviderDBHelper dbHelper;
    
    /** ContentProviderのURI. */
    private static final String AUTHORITY = "com.xxx.myapp.myprovider";
    
    /**テーブルのURIや列名を定義したクラス*/
    public static class Table
    {
        public static final Uri contentUri = Uri.parse(
            "content://" + AUTHORITY + "/" + ProviderDBHelper.TABLE_NAME);
        public static class Columns
        {
            public static final String NAME_KEY = ProviderDBHelper.NAME_KEY;
        }
    }

    @Override
    public boolean onCreate()
    {
        dbHelper = new ProviderDBHelper(getContext());
        return false;
    }
    
    /**データを検索して見つかった行を返す。*/
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, 
                    String[] selectionArgs, String sortOrder) 
    {
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        if(uri.getLastPathSegment() != null)
            return db.query(ProviderDBHelper.TABLE_NAME, projection,
                        selection, selectionArgs, null, null, sortOrder);
        else
            return db.query(ProviderDBHelper.TABLE_NAME, projection,
                    "_id = ?", new String[]{uri.getLastPathSegment()}, 
                    null, null, sortOrder);
    }
    
    /**データの挿入*/
    @Override
    public Uri insert(Uri uri, ContentValues values) 
    {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        long id = db.insert(ProviderDBHelper.TABLE_NAME, null, values);
        Uri insertedUri = (id >= 0) 
                    ? Uri.withAppendedPath(uri, String.valueOf(id)) : null;
        db.close();
        return insertedUri;
    }
    
    /**MIMEタイプ(今回は無視)*/
    @Override
    public String getType(Uri uri){ return null; }
    
    /**データ行の上書き*/
    @Override
    public int update(Uri uri, ContentValues values, String selection, 
                    String[] selectionArgs) 
    {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        if(uri.getLastPathSegment() != null)
            return db.update(ProviderDBHelper.TABLE_NAME, 
                    values, selection, selectionArgs);
        else
            return db.update(ProviderDBHelper.TABLE_NAME, values, 
                    "_id = ?", new String[] {uri.getLastPathSegment()});
    }
    
    /**データの削除*/
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) 
    {
        SQLiteDatabase db = dbHelper.getWritableDatabase();
        if(uri.getLastPathSegment() != null)
            return db.delete(ProviderDBHelper.TABLE_NAME, 
                    selection, selectionArgs);
        else
            return db.delete(ProviderDBHelper.TABLE_NAME, "_id = ?",
                    new String[] {uri.getLastPathSegment()});
    }
}

ContentProviderからテーブルのデータにアクセスするにはURIを使い、そのURIは次の図のような形式を持ちます。

コンテンツプロバイダーでアクセスするURIの構造

上の図のプロバイダのURIがプロバイダーを識別するためのURIでパッケージ名にプロバイダ名をドット区切りでつけることで一意性を保証しています。

その後のデータの種類がテーブル名に当たり、そのあとの数字がそれぞれのデータ行を表しています。

ContentProviderの追加

カスタムしたContentProviderを利用するにはAndroidManifest.xmlprovider要素をapplication要素以下に追加する必要があります。

<provider android:name=".MyProvider"
    android:authorities="com.xxx.myapp.myprovider" />

ただし、この宣言が必要なのはプロバイダーが定義されているアプリ内です。

その外側(別のアプリ)からプロバイダーを利用するときにはproviderを宣言する必要はありません。

使用例

では、実際にカスタムしたプロバイダーを使ってみたいと思います。

class MainActivity extends Activity
{
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        ///データ行の追加
        for(int i = 0; i < 25; ++i){
            ContentValues values = new ContentValues();
            values.put(MyProvider.Table.Columns.NAME_KEY,
                            "Name" + i);
            getContentResolver().insert(
                MyProvider.Table.contentUri, values);
        }
        
        ///データの読み出し
        Cursor cursor = getContentResolver().query(MyProvider.Table.contentUri, 
                        null, MyProvider.Table.Columns.NAME_KEY + " like ?", 
                        new String[]{"Name"}, null);
            ///nameカラムに"Name"という文字列が含まれる列を検索
        cursor.moveToFirst();
        int index = cursor.getColumnIndex(
                MyProvider.Table.Columns.NAME_KEY);
        while(!cursor.isAfterLast()){
            if(index != -1)
            cursor.moveToNext();
        }
        cursor.close();
    }
}

getContentResolverからqueryなどのメソッドを呼ぶときにカスタムしたプロバイダーのURIを渡すだけで自動的にそこからカスタムしたプロバイダーのqueryメソッドなどが呼ばれる仕組みになっています。

deleteupdateについても同じようにすればOKです。

以上、ContentProviderでデータ共有でした。では、また!

関連項目
プライバシーポリシー