アプリ間のデータ共有
Androidではアプリ間のデータを共有するのにContentProviderが使われることが多いです。
例えばこれを使えば電話帳のデータなど複数のアプリで使いまわしたいデータをデーターベース上に保管できます。
ここではその方法を紹介します。
データベースの作成
初めにデータを保存するデータベースを作成します。
例として次のようなデータベースを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つのメソッドをオーバーロードする必要があります。
- query
テーブルからデータを探し出すメソッド
- insert
データを新しく追加するメソッド
- update
ある行のデータを上書きするメソッド
- delete
行を削除するメソッド
- getType
UriのMIMEタイプを返すメソッド
最低限、この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でパッケージ名にプロバイダ名をドット区切りでつけることで一意性を保証しています。
その後のデータの種類がテーブル名に当たり、そのあとの数字がそれぞれのデータ行を表しています。
ContentProviderの追加
カスタムしたContentProviderを利用するにはAndroidManifest.xmlにprovider要素を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メソッドなどが呼ばれる仕組みになっています。
deleteやupdateについても同じようにすればOKです。
以上、ContentProviderでデータ共有でした。では、また!