2012年10月30日火曜日

[Android]Content Provider

Androidでデータを保存するにはContent Providerを使用するのが基本です。端末内のアプリ専用エリアに作成され、アクセス権限を定義すれば他のアプリからも使用可能と夢が広がる機能です。システムのデータもこれで作成されていて、電話帳などにアクセスするアプリを作ることも可能です。
メリットは上記の通りで、これで作っておけば問題無いと言いたいところなんだけど、端末の容量が数十メガバイトしかなく(少なくともdev phoneは)、あまり消費させたくないというのが実情なので、必要に応じて外部の(SDカードとか)記憶エリアを併用するのが賢いやり方だと思います。

AndroidManifest.xmlの更新
まずはContent Providerを使用するための設定をAndroidManifest.xmlに追加します。
<provider android:name="TestProvider" android:authorities="com.matabii.test.testprovider" />
これで、com.matabii.test.testproviderという名前(URL)を使用して、TestProviderにアクセス出来ます。他のアプリからもアクセス出来るようにするために、パッケージ名を含めたクラス名にするのが良いみたいです。ActivityからはこのURLを使ってProviderにアクセスします。
AndroidManifest.xmlについて、今回はこれだけの編集で終了です。

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.matabii.test"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<provider android:name="TestProvider" android:authorities="com.matabii.test.testprovider" />
<activity android:name=".ProviderTest"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
<uses-sdk android:minSdkVersion="3" />
</manifest>

Providerクラスの作成
AndroidManifest.xmlを保存すると、TestProviderが無いというエラーが出るので作成します。Content Providerを使用するには、ContentProviderのサブクラスを作る必要があります。ContentProviderは抽象クラスで、onCreate()、query()、insert()、delete()などのメソッドをオーバーライドさせるようになってます。何のためのクラスなのか、何となくわかりますね。
TestProviderクラスを作り、ContentProviderを継承、必要なメソッドをeclipseで自動で実装しておきます。
package com.matabii.test;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

public class TestProvider extends ContentProvider{
@Override
public boolean onCreate() {
// TODO Auto-generated method stub
return false;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// TODO Auto-generated method stub
return null;
・・・略


Helperクラスの作成
TestProviderに最低限の実装をしていきます。今回は、テーブルの作成とデータの追加の動きを見るだけにします。ContentProvider自身にはデータベースをどうこうする手段は無いので、別にSQLiteOpenHelperの実装クラスを作る必要があります。NotePadでもそうしてるので、ここではTestProviderのインナークラスとして定義します。
テーブルを作成する処理はここに書きます。プライマリーキーとして、_IDが必要みたいです。後はnameとdescriptionだけのシンプルなテーブルを作ることにします。
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, "test.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE test ("
+ BaseColumns._ID + " INTEGER PRIMARY KEY,"
+ "name TEXT,"
+ "description TEXT"
+ ");");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("DROP TABLE IF EXISTS test");
onCreate(db);
}
}
SQLiteOpenHelperについて→http://d.hatena.ne.jp/isher/20091108
DatabaseHelperへのアクセス変数を作成します。
DatabaseHelper databaseHelper;
OnCreate()の実装
重要なメソッドですが、Helperに振るだけなのでこれだけです。最初にアクセスした時に呼ばれるので、DatabaseHelperのインスタンスを作ります。上で作ったDatabaseHelperクラスのonCreate()が呼び出され、データベースが無ければ作成して、インスタンスが返されます。
@Override
public boolean onCreate() {
databaseHelper = new DatabaseHelper(getContext());
return true;
}
insert()の実装
データ登録用のメソッドです。DatabaseHelperのインスタンスから更新用のSQLiteDatabaseインスタンスを取得して、insertメソッドを呼び出すだけです。実際はテーブル名で分岐させたり、登録日なんてカラムがあるならここでセットしてしまえばいいですね。
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.insert("test", null, values); //testがテーブル名
return null;
}
query()の実装
データ検索用のメソッドです。DatabaseHelperのインスタンスから参照用のSQLiteDatabaseインスタンスを取得してCursorを返すことになります。ここもテーブル名で分岐させたりするんだろうけど、固定でいっちゃいます。
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables("test"); //テーブル名
Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, null);
return c;
}
Provider作成完了
updateやdeleteが作成されてないし実際使い物にならないけど、動作を見るくらいならこれで動きます。
package com.matabii.test;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.provider.BaseColumns;

public class TestProvider extends ContentProvider {
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, "test.db", null, 1);
}

@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE test (" + BaseColumns._ID
+ " INTEGER PRIMARY KEY," + "name TEXT,"
+ "description TEXT" + ");");
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// TODO Auto-generated method stub
db.execSQL("DROP TABLE IF EXISTS test");
onCreate(db);
}
}

DatabaseHelper databaseHelper;

@Override
public boolean onCreate() {
// TODO Auto-generated method stub
databaseHelper = new DatabaseHelper(getContext());
return true;
}

@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.insert("test", null, values);
return null;
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
qb.setTables("test");
Cursor c = qb.query(db, projection, selection, selectionArgs, null,
null, null);
return c;
}

@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}

@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}

@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// TODO Auto-generated method stub
return 0;
}
}


Activityからのテスト
いよいよActivityから呼び出してみますが、簡単な動作確認だけにしてしまいます。
content://com.matabii.test.testproviderがAndroidManifest.xmlに定義したURLです。電話帳を使いたい時はcontent://contacts/peopleを使います。直接クラスを使わずにURLをIntentにセットすることでアプリケーション間で連携させるわけです。
package com.matabii.test;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;

public class ProviderTest extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getIntent().setData(Uri.parse("content://com.matabii.test.testprovider"));

ContentValues values = new ContentValues();
values.put("name", "Pen");
values.put("description", "This is a pen");
getContentResolver().insert(getIntent().getData(), values);

Cursor cur = managedQuery(getIntent().getData(), null, null, null, null);
while (cur.moveToNext()) {
Log.d(cur.getString(1), cur.getString(2));
}

setContentView(R.layout.main);
}
}
結果確認
エミュレーターを実行して、Activityが表示されれば成功です。結果はログを見ないとわかりませんが。
データベースは/data/data/パッケージ名、上の例だと/data/data/com.matabii.testの下にdatabasesディレクトリが作られて、その中にデータベースが作成されてます。
エミュレータを立ち上げてadb shellしてのぞいてみましょう。
# cd /data/data/com.matabii.test/
# ls
databases
lib
# cd databases
# ls
test.db
test.dbが作られてます。sqlite3で中身を見てみます。
# sqlite3 test.db
SQLite version 3.5.9
Enter ".help" for instructions
sqlite> .schema
CREATE TABLE android_metadata (locale TEXT);
CREATE TABLE test (_id INTEGER PRIMARY KEY,name TEXT,description TEXT);
sqlite> select * from test;
1|Pen|This is a pen
2|Pen|This is a pen
testテーブルが作成されていて、内容も登録されてました。2回実行したので、2件です。_idは自動採番されてます。

0 件のコメント:

コメントを投稿