2014年2月21日金曜日

Android - ContentProvider

ContentProviderを扱ったので、メモ。

初めは、さっぱり理解できなかったが、四苦八苦しながら作っていくうちにContentProviderの便利さが少しずつ分かってきました。

ContentProviderは、アプリ間でデーターを共有する仕組み。


1. AndroidManifest.xmlにproviderを設定。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.test" >
    <application>
        <provider
            android:name="com.example.test.provider.TestProvider"
            android:authorities="com.example.test.provider"
            android:exported="false" />
    </application>
</manifest>
 
android:name
これは、ContentProviderを実装しているクラス名。
上記の例でみると、
 
「com.example.test」パッケージのサブ「provider」にある「TestProvider」クラス
 
という事になります。
また、もう一つの記述方法として、
 
.provider.TestProvider
 
と記述する事も出来ます。
一文字目の「.」がパッケージ名に置き換わります。
 
android:authorities
ここには、ContentProviderを識別する重複しない名前を設定。
通常は、パッケージ名。
 
android:exported
これは、他のアプリとデータを共有するかどうか。
デフォルトは、true(共有する)
 
この他にも、たくさん設定項目がありますが、基本はこの三つかな。


2. ContentProviderを継承したクラスの作成

TestProvider.java
public class TestProvider extends ContentProvider {
    private DatabaseOpenHelper mDatabaseOpenHelper;
    
    @Override
    public boolean onCreate() {
        mDatabaseOpenHelper = new DatabaseOpenHelper(getContext());
        return true;
    }
    
    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
        SQLiteDatabase readableDb = mDatabaseOpenHelper.getReadableDatabase();
        queryBuilder.setTables(uri.getPathSegments().get(0));
        Cursor cursor = queryBuilder.query(readableDb, projection, selection, selectionArgs, null, null, sortOrder);
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;
    }
    
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        SQLiteDatabase writableDb = mDatabaseOpenHelper.getWritableDatabase();
        String tableName = uri.getPathSegments().get(0);
        long rowId = writableDb.insertOrThrow(tableName, null, values);
        Uri newURI = ContentUris.withAppendedId(uri, rowId);
        getContext().getContentResolver().notifyChange(newURI, null);
        return newURI;
    }
    
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
        SQLiteDatabase writableDb = mDatabaseOpenHelper.getWritableDatabase();
        String tableName = uri.getPathSegments().get(0);
        int rowNumber = writableDb.update(tableName, values, selection, selectionArgs);
        getContext().getContentResolver().notifyChange(uri, null);
        return rowNumber;
    }
    
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        SQLiteDatabase writableDb = mDatabaseOpenHelper.getWritableDatabase();
        String tableName = uri.getPathSegments().get(0);
        int rowNumber = writableDb.delete(tableName, selection, selectionArgs);
        getContext().getContentResolver().notifyChange(uri, null);
        return rowNumber;
    }
    
    @Override
    public String getType(Uri uri) {
        return null;
    }
}

DatabaseOpenHelper.java
public class DatabaseOpenHelper extends SQLiteOpenHelper {
    private static final String DATABASE_NAME = "test.sqlite";
    private static final CursorFactory FACTORY = null;
    private static final int DATABASE_VERSION = 1;

    public DatabaseOpenHelper(Context context) {
        super(context, DATABASE_NAME, FACTORY, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

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

特に、難しいことはしていません。
query()以外は、データーベースの内容を変更した後、ContentResolverに対してnotifyChange()を呼び出して、データーベースの内容が変更したことを教えています。

getType()は、今の段階ではよく分かりませんでした。ブレークポイントを置いても止まってくれず、どのタイミングで呼び出されるのかが不明です。名前の通り、データーの種類を取得するのだと思うのですが、その辺は必要になった時に改めて調べてみるつもりです。


3. ContentProviderを使う

TestFragment.java
public class TestFragment extends Fragment {
    private static final String PROVIDER_NAME = "com.example.test.provider";
    private static final String TABLE_NAME = "test";
    private static final String USER_ID = "user_id";
    private List<String> mUserId;
    
    public void ConfigurationFragment() {}
    
    LoaderManager.LoaderCallbacks<Cursor> userIdCallbacks = new LoaderManager.LoaderCallbacks<Cursor>() {

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
            final String URI = "content://" + PROVIDER_NAME + File.separator + TABLE_NAME;
            final Uri CONTENT_URI = Uri.parse(URI);
            String[] projection = { USER_ID };
            String selection = null;
            String[] selectionArgs = null;
            String sortOrder = null;
            return new CursorLoader(getActivity(), CONTENT_URI, projection, selection, selectionArgs, sortOrder);
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
            if (data.getCount() > 0) {
                while(data.moveToNext()) {
                    mUserId.add(data.getString(0));
                }
            }
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
        }
    };
    
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mUserId = new ArrayList<String>();
        insertUserId();
        getLoaderManager().initLoader(0, null, userIdCallbacks);
    }

    public void insertUserId() {
        final String URI = "content://" + PROVIDER_NAME + File.separator + TABLE_NAME;
        final Uri CONTENT_URI = Uri.parse(URI);
        ContentValues contentValus = new ContentValues();
        contentValus.put(USER_ID, "ユーザーID");
        Uri uri = getActivity().getContentResolver().insert(CONTENT_URI, contentValus);
        contentValus.clear();
    }
}
 
ContentProviderを使うためには、「content://」で始まるURIを作成する必要があります。
PROVIDER_NAMEには、AndroidManifest.xmlのandroid:authoritiesで設定した値が入ります。そして、最後には対象のデーターベースのテーブル名が入ります。
 
もっと細かな設定が出来るのですが、今回はデーターベースのテーブルに対して操作をしたかったので、この設定で十分でした。
 
データーベースへの操作が多くなってくると、このContentProviderを経由したやり方がとても楽に感じます。まだ初歩的な使い方しかできていませんが、ContentProviderを使っていくうちにもう少し知識が増えたら、またこのブログに掲載したいと思っています。
 
こんなところかな。

0 件のコメント:

コメントを投稿