LoginSignup
120
128

More than 5 years have passed since last update.

- Android開発初心者向け - Content Providerの使い方

Last updated at Posted at 2014-10-08

AndroidでDB関係を実装するときによくわからなかったので勉強したことをまとめます。
自分はjavaは触ったことがあるがAndroidは初めてという人なので結構基本的なことから書いていると思います。
そんなこと知ってるわ!って人は最初の方を飛ばしてください。

Content Providerとは何か

AndroidでDBにデータを取得/保存する時に利用するクラスです。
このクラスを使えば、あるアプリが持っているデータを他のアプリも使用できるようになったりします。もともと用意されているものとしては、着信ログや電話帳、ブラウザのブックマークなどがあります。
異なるアプリで同じDBを触ることができるというあたり、様々利用できそうで夢が膨らみます。
これらのデータを利用するためにはgetContentResolver().query()メソッドで、目的のデータを示すURIを指定してCursorインスタンスを取得する必要があるそうです。

URI

URIとは目的のデータを示すために使われるものです。PCであればファイルをディレクトリで指定すると思いますが、AndoroidではURIで指定するといった感じだと思います(適当)。

Content ProviderのURIは3つの要素から構成されています。

(例) content://packagename.providername/sample

  1. prefix:"content://"
  2. authority(パッケージ名+任意のprovider名):"packagename.providername"
  3. table:"sample"

authorityは世界で唯一の名前になるのが好ましいらしく、パッケージ名+provider名でつけるのが良いらしいです。

SQLiteOpenHelper, BaseColumns

Content Providerを作成する前にDBが無いとデータを取得も保存もそもそもできないので、データ提供元となるDBを作成します。
これにはSQLiteOpenHelperクラスを継承したクラスを利用します。
DBを作成するときはDBの名前やテーブルの名前、カラムなどをBaseColumnsをimplementsしたクラスにまとめて書くのが良いと思います。
ここでは例としてユーザー情報を扱うDBを考えます。名前とメールアドレスだけのシンプルなDBです。

Columns
public class UserColumns implements BaseColumns{
    // URIパス
    public static final String PATH = "user";
    // コンテントURI
    public static final Uri CONTENT_URI = Uri.parse("content://" + MyContentProvider.AUTHORITY + "/" + PATH);
    // テーブル指定コンテントタイプ
    public static final String CONTENT_TYPE = "vnd.android.cursor.item/vnd.example.users";
    // レコード個別指定コンテントタイプ
    public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.dir/vnd.example.users";

    // テーブル名
    public static final String TABLE = "users";
    // カラム 名前
    public static final String NAME = "name";
    // カラム メールアドレス
    public static final String EMAIL = "email";

    // コンストラクタ(呼ばれることは無い)
    private RecordColumns() {}
}
SQLiteOpenHelper
public class UserDBHelper extends SQLiteOpenHelper{
    // DB名とバージョン
    private static final String DB_NAME = "database";
    private static final int DB_VERSION = 1;

    // コンストラクタ
    public UserDBHelper(Context context){
        super(context, DB_NAME, null, DB_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        // データベースのテーブルを作成する
        sqLiteDatabase.execSQL(/* TODO */);
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i2) {
        // DBに変更があったときに呼ばれる
    }
}

Content Provider

DBの準備ができたので、いよいよContent Providerの説明に入ります。

Content Providerを継承したクラスは以下のメソッドを実装する必要があります。
- onCreate() .. プロバイダの初期化
- query() .. データを取得。戻り値はCursor
- insert() .. 行を追加。戻り値は新たに追加された行のURI
- update() .. 各行を更新。戻り値は更新した行の数
- delete() .. 行を削除。戻り値は削除した行の数
- getType() .. ContentURIに対応するMIMEタイプを返す

だいたいの形は以下のようになります。

基本的には受け取ったUriをUriMatcherを用いてマッチングし、マッチングに応じた処理をswitch文で記述することになります。UriMatcherクラスはコンテンツプロバイダのURIのマッチングをサポートするユーティリティクラスです。

ContentProvider
public class UserContentProvider extends ContentProider{
    // Authority
    public static final String AUTHORITY = "com.example";

    // USERS テーブル URI ID
    private static final int USERS = 1;
    // USERS テーブル 個別 URI ID
    private static final int USER_ID = 2;

    // 利用者がメソッドを呼び出したURIに対応する処理を判定処理に使用します
    private static UriMatcher sUriMatcher;
    static {
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(AUTHORITY, UserColumns.PATH, USERS);
        sUriMatcher.addURI(AUTHORITY, UserColumns.PATH + "/#", USER_ID);
    }

    // DBHelperのインスタンス
    private UserDBHelper mDBHelper;

    // コンテンツプロバイダの作成
    @Override
    public boolean onCreate() {
        mDBHelper = new UserDBHelper(getContext());
        return true;
    }

    // query実行
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
        switch (sUriMatcher.match(uri)) {
            case USERS:
            case USER_ID:
                queryBuilder.setTables(UserColumns.TABLE);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        SQLiteDatabase db = mDBHelper.getReadableDatabase();
        Cursor cursor = queryBuilder.query(db, projection, selection, selectionArgs, null, null, sortOrder);
        return cursor;
    }

    // insert実行
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        String insertTable;
        Uri contentUri;
        switch (sUriMatcher.match(uri)) {
            case USERS:
                insertTable = UserColumns.TABLE;
                contentUri = UserColumns.CONTENT_URI;
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }

        SQLiteDatabase db = mDBHelper.getWritableDatabase();
        long rowId = db.insert(insertTable, null, values);
        if (rowId > 0) {
            Uri returnUri = ContentUris.withAppendedId(contentUri, rowId);
            getContext().getContentResolver().notifyChange(returnUri, null);
            return returnUri;
        } else {
            throw new IllegalArgumentException("Failed to insert row into " + uri);
        }
    }

    // update実行
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mDBHelper.getWritableDatabase();
        int count;
        String id = uri.getPathSegments().get(1);
        count = db.update(RecordColumns.TABLE, values, selection, selectionArgs);
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    // delete実行
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase db = mDBHelper.getWritableDatabase();
        int count;
        switch (sUriMatcher.match(uri)) {
            case USERS:
            case USER_ID:
                count = db.delete(RecordColumns.TABLE, selection, selectionArgs);
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }

    // コンテントタイプ取得
    @Override
    public String getType(Uri uri) {
        switch(sUriMatcher.match(uri)) {
            case USERS:
                return UserColumns.CONTENT_TYPE;
            case USER_ID:
                return UserColumns.CONTENT_ITEM_TYPE;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
    }
}   

また、Content Providerを利用するためにAndroidManifestに定義を追加する必要があります。

<provider
    android:authorities="com.example" // URIの定義で定義したauthorities
    android:name="UserContentProvider" // ContentProviderを継承したクラスの名前
    android:exported="false"> // 他アプリへの公開/非公開設定。デフォルトはtrue
</provider>

Content Resolverについて

これでContent Providerを利用する準備ができたのでデータを取得したり保存したりしたいと思います。
Content Providerを介してデータを取得するためにはContent Resolverインスタンスを利用します。

MainActibvity
public final class MainActivity extends Activity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        // テーブルにデータ投入.
        ContentValues values = new ContentValues();
        for (int i = 0; i < 3; i++) {
            values.clear();
            values.put(UserColumns.NAME, "name" + i);
            values.put(UserColumns.EMAIL, "email" + i);
            getContentResolver().insert(UserColumns.CONTENT_URI, values);
        }

        // テーブルのデータを全件検索. 表示.
        Cursor c = getContentResolver().query(UserColumns.CONTENT_URI, null, null, null, null);
        startManagingCursor(c);
        while (c.moveToNext()) {
            for (int i = 0; i < c.getColumnCount(); i++) {
                Log.d(getClass().getSimpleName(), c.getColumnName(i) + " : " + c.getString(i));
            }
        }
    }
}
120
128
3

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
120
128