戌印-INUJIRUSHI- (Androidあれこれ)

Androidのプログラミングをメインにしてます。記事に貼られたソースコードはダブルクリックすることで行番号をはずしてコピーすることができます。

 
1
2
3
4
5
6
7
8
10
11
12
13
14
15
16
17
18
19
20
21
22
24
26
27
28
29
09

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

SQLite データベースをSDカードに保存する

Android で SQLite データベースをSDカードに保存するには、各メソッドの引数で渡すデータベース名をフルパス指定することで作成することができます
(ファイル名だけの場合はシステムメモリの /data/data/[パッケージ名]/databases/ に作成されます)
なお、SDカードにファイルを保存するにはパーミッションを追加する必要があります。

※ Android 2.1 以前は Context でのデータベース作成(取得)でスラッシュは許可されていません。
  対処法は後述。

AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="jp.inujirushi.android.sample14"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="4"
android:targetSdkVersion="15" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="SampleActivity"
android:label="@string/title_activity_main" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

SampleSQLiteOpenHelper.java
package jp.inujirushi.android.sample.sql;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class SampleSQLiteOpenHelper extends SQLiteOpenHelper {

private static final int DB_VERSION = 1;
private static final String DB_NAME = Environment
.getExternalStorageDirectory() + "/sample.db";

public SampleSQLiteOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}

@Override
public void onCreate(SQLiteDatabase db) {
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
----------------------------------------------------------------------
最初に書いたように、Android 2.1以前では Context でのデータベースの作成(取得)でスラッシュは許可されていません
SQLiteOpenHelper も Context を使用しています。

もしもスラッシュを含めた場合には、以下のようなエラーが発生します。

java.lang.IllegalArgumentException: File /sdcard/jp.inujirushi.android.sample/sample.db contains a path separator

エラーを出さずにデータベースを作成するには SQLiteDatabase#openOrCreateDatabase を使用します。
こちらのメソッドはスラッシュが許可されています。
ただしパーミッションが 644 (他アプリから読み書き可能)となるので注意してください


使い方のサンプルとして SQLiteOpenHelper の拡張クラスを作ってみました。
拡張クラスでは SQLiteOpenHelper#getWritableDatabase をオーバーライドしているだけです。
SQLiteOpenHelper#getReadableDatabase は内部で SQLiteOpenHelper#getWritableDatabase を呼び出しているため何もしていません。

なお、この拡張クラスではすでにデータベースが呼ばれていても新しいインスタンスを作成するので、使う場合には注意してください(Android 2.1 以前でパスを指定した場合のみ)
package jp.inujirushi.android.sample.sql;

import java.io.File;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.os.Build;

/**
* SQLiteOpenHelper の Android 2.1 以前対応版。
*
* @author Inujirushi
*
*/
public abstract class MySQLiteOpenHelper extends SQLiteOpenHelper {

// Android 2.1 以前で使用する
private String mMyName;
private int mMyVersion;

/**
* コンストラクタ
*
* @param context
* @param name
* @param factory
* @param version
*/
public MySQLiteOpenHelper(Context context, String name,
CursorFactory factory, int version) {
super(context, name, factory, version);
mMyName = name;
mMyVersion = version;
}

@Override
public SQLiteDatabase getWritableDatabase() {
// Android 2.1以前
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ECLAIR_MR1)
return getWriteDatabasePreEclair();

// Android 2.2以降
return super.getWritableDatabase();
}

/**
* Android 2.1 以前のデータベース取得処理
*
* @return SQLiteDatabase
*/
private SQLiteDatabase getWriteDatabasePreEclair() {
// Android 2.2 のソースコードを参考にしています。
// データベース取得を context から SQLiteDatabase に変更。
boolean success = false;
SQLiteDatabase db = null;
try {
// データベースを取得する
if (mMyName == null) {
db = SQLiteDatabase.create(null);
} else {
// スラッシュが含まれていなければ本来のメソッドを呼び出す
if (mMyName.indexOf(File.separator) < 0) {
return super.getWritableDatabase();
}

// ディレクトリが無ければ作成する
File parent = new File(mMyName).getParentFile();
if (!parent.exists())
parent.mkdirs();
parent = null;

// データベースを取得 or 作成して開く
db = SQLiteDatabase.openOrCreateDatabase(mMyName, null);
}

// バージョンが違えば作成 or アップグレードする
int version = db.getVersion();
if (version != mMyVersion) {
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
onUpgrade(db, version, mMyVersion);
}
db.setVersion(mMyVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}

// データベースをオープンする
onOpen(db);
success = true;
return db;
} finally {
// 失敗したらデータベースを閉じる
if (!success && db != null) {
db.close();
}
}
}
}
スラッシュが含まれているときだけ処理が違う(パーミッションが変わる)のは嫌という方は63~66行目をコメントアウトするか、消してください。

【関連記事】
SQLite データベースを取得・作成する:openOrCreateDatabase
スポンサーサイト

SQLite データベースを取得・作成する:openOrCreateDatabase

Android では SQLite データベースを扱う API が複数用意されています。
その中で、データベースを作成、取得する方法は以下のとおり。

SQLiteOpenHelper
SQLiteDatabase#openOrCreateDatabase
Context#openOrCreateDatabase

上記のいずれを使ってもデータベースを作成することができますが、それぞれ作成されるデータベースのファイルパーミッションが異なります
どのようなファイルパーミッションが設定されるかは上から順に以下のとおり。

・660 (他のアプリからアクセス不可)
・644(他のアプリから読み取り可能)
・指定可能(660 or 662 or 664 or 666)

なお、データベースはファイル名だけを指定した場合、システムメモリ(/data/data[パッケージ名]/databases/)に作成されます。
SDカードに保存する場合は『SQLite データベースをSDカードに保存する』を参照してください。

参考サイト: CodeZine - AndroidアプリにおけるDBファイルの正しい使い方

API Reference: SQLiteOpenHelper, SQLiteDatabase, Context

以下、それぞれの使い方。
----------------------------------------------------------------------
SQLiteOpenHelper
package jp.inujirushi.android.sample;

import jp.inujirushi.android.sample.sql.SampleSQLiteOpenHelper;
import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;

public class MainActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// SQLiteOpenHelper クラスを使ってデータベースを取得する。
SampleSQLiteOpenHelper helper = new SampleSQLiteOpenHelper(this);

// 書き込み可能なデータベースを取得(作成)する
SQLiteDatabase writableDB = helper.getWritableDatabase();

// 読み取り専用のデータベースを取得する(作成はされない)
SQLiteDatabase readableDB = helper.getReadableDatabase();
}
}
SQLiteOpenHelperContext#openOrCreateDatabase を使用してデータベースを作成しています。
Context#openOrCreateDatabase ではファイルパーミッションを指定することができますが、こちらは他のアプリケーションからアクセスできないように固定されています

SQLiteOpenHelperクラスの使い方は『SQLite データベースを管理する : SQLiteOpenHelper』を参照してください。

----------------------------------------------------------------------
SQLiteDatabase#openOrCreateDatabase
package jp.inujirushi.android.sample;

import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;

public class MainActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// SQLiteDatabase クラスを使ってデータベースを取得 or 作成する
SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase(
"sample.db", null);
}
}
SQLiteDatabase#openOrCreateDatabase ではファイルパーミッションが指定されておらず、他のアプリから読み取り可能となっています
アプリケーションの脆弱性の原因となるので使う際には注意してください。

----------------------------------------------------------------------
Context#openOrCreateDatabase
package jp.inujirushi.android.sample;

import android.app.Activity;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;

public class MainActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Context クラスを使ってデータベースを取得(作成)する
SQLiteDatabase database = openOrCreateDatabase("sample.db",
Context.MODE_PRIVATE, null);
}
}
唯一、ファイルパーミッションを指定してデータベースを作成することができます
ファイルパーミッションは第二引数で指定することができ、指定できる値は以下になります。

Context.MODE_PRIVATE
660:呼び出し元のアプリケーションのみ読み書き可

Context.MODE_WORLD_READABLE
664:他のアプリケーションも読み込み可

Context.MODE_WORLD_WRITEABLE
662:他のアプリケーションも書き込み可

Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE
666:他のアプリケーションも読み書き可

----------------------------------------------------------------------
この内容だけを見ると、SQLiteDatabase#openOrCreateDatabase はいらないと思います。

しかし、Android 2.1 以前は Context を使用したデータベースの作成(参照)でスラッシュを使うことができません
そのため、Android 2.1 以前でSDカードに保存する場合には、スラッシュが許可されている SQLiteDatabase#openOrCreateDatabase を使う必要があります。

使い方については(そのままメソッド呼ぶだけですが…)、『SQLite データベースをSDカードに保存する』で SQLiteOpenHelper を使った実装例を紹介しています。

SQLite データベースを管理する : SQLiteOpenHelper

Android でデータベース(SQLiteDatabase) の管理を行うには SQLiteOpenHelper クラスを使うことが推奨されています。

SQLiteOpenHelper を使うことで、データベースの作成、アップグレードを簡単に行うことができます。
※ 実際にデータベースの操作を行うのは SQLiteDatabase クラスです

API Reference: SQLiteOpenHelper, SQLiteDatabase, SQLiteException

ソフトウェア技術ドキュメントを勝手に翻訳
Android 開発ガイド - フレームワークトピック - 8. データ保存 データベースの使用

----------------------------------------------------------------------
SQLiteOpenHelper を使うにはサブクラスを作成します。
Android 3.0 HoneyComb (API 11) からはダウングレードにも対応するようになりました。
onDowngrade() をオーバーライドせずにダウングレードを行うと以前と同様に SQLiteException が発生します。
package jp.inujirushi.android.sample.sql;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class SampleSQLiteOpenHelper extends SQLiteOpenHelper {

private static final int DB_VERSION = 1;
private static final String DB_NAME = "sample.db";

/**
* コンストラクタ
* 作成するデータベース名、バージョンの指定はここで行います。
*/
public SampleSQLiteOpenHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}

/**
* データベース作成処理
* データベースが存在しないときに呼ばれます。
*/
@Override
public void onCreate(SQLiteDatabase db) {
StringBuffer sql;

// テーブルを作成する
sql = new StringBuffer();
sql.append("CREATE TABLE T_USER (");
sql.append(" USER_ID integer primary key autoincrement,");
sql.append(" NAME text NOT NULL");
sql.append(")");
db.execSQL(sql.toString());

// レコードを挿入する
db.execSQL("INSERT INTO T_USER (NAME) VALUES ('テスト太郎')");
db.execSQL("INSERT INTO T_USER (NAME) VALUES ('テスト花子')");
}

/**
* データベースのアップグレード処理
* コンストラクタで指定されたバージョンが、現在使用しているデータベースのバージョンより大きい時に呼ばれます。
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// Upgrade Database ver.2
if (oldVersion < 2) {
StringBuffer sql = new StringBuffer();
sql.append("ALTER TABLE T_USER ADD COLUMN ");
sql.append(" AGE integer NOT NULL DEFAULT 0;");
db.execSQL(sql.toString());
}
// Upgrade Database ver.3
if (oldVersion < 3) {
StringBuffer sql = new StringBuffer();
sql.append("ALTER TABLE T_USER ADD COLUMN ");
sql.append(" ADDRESS text;");
db.execSQL(sql.toString());
}
}

/**
* データベースのダウングレード処理
* コンストラクタで指定されたバージョンが、現在使用しているデータベースのバージョンより小さい時に呼ばれます。
* ※ Android 3.0(API 11) から追加されました
*/
@Override
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}

}

使い方は以下のとおり。
package jp.inujirushi.android.sample;

import jp.inujirushi.android.sample.sql.SampleSQLiteOpenHelper;
import android.app.Activity;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;

public class SampleActivity extends Activity {

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// SQLiteOpenHelper のインスタンスを生成します
// まだデータベースは作成されません
SampleSQLiteOpenHelper helper = new SampleSQLiteOpenHelper(this);

// データベースを書き込み可能で取得する
// ここでデータベースが作成されます(onCreate or onUpgrade)
SQLiteDatabase database = helper.getWritableDatabase();

// データベースを閉じる
database.close();
}
}
データベースの作成、アップグレードは書き込み可能なデータベースを取得する SQLiteOpenHelper#getWritableDatabase() が呼ばれたときに行われます。

データベースが存在しない時、またはアップグレードが行われていない時に読み取り専用のデータベースを取得する SQLiteOpenHelper#getReadableDatabase() を呼ぶと SQLiteException が発生するので注意してください。

----------------------------------------------------------------------
SQLiteOpenHelper#getWritableDatabase() でのデータベース作成・更新時の処理は以下のようになっています。
android.database.sqlite.SQLiteOpenHelper#getWritableDatabase()
データベース作成部を抜粋 (API 15)
int version = db.getVersion();
if (version != mNewVersion) {
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
データベースの作成・更新時にはトランザクションが張られているので onCreate()、onUpgrade() でのトランザクションの記述は行わないようにしてください。

【関連記事】
SQLite データベースを取得・作成する:openOrCreateDatabase
SQLite データベースをSDカードに保存する

Android ビルドバージョンの取得と判定:Build.VERSION

自身が使っているデバイスの Android バージョンは Build.VERSION クラスの Build.VERSION.SDK_INT から取得することができます。
(Build.VERSION.SDK で同じ値をString型で取得することができますが、こちらは非推奨(@Deprecated)となっています)

取得した Android バージョンは Build.VERSION_CODES クラスに定義されたビルドバージョンの定数値と比較することで、バージョンに応じた処理分岐を行うことができます。

API Reference: Build.VERSION, Build.VERSION_CODES

参考サイト:
Y.A.M の 雑記帳 - Android ビルドバージョンで処理を分岐する
Serendip Web Studio - 端末の Android OS バージョンを調べる

比較方法:
switch (Build.VERSION.SDK_INT) {
case Build.VERSION_CODES.BASE:
// Android 1.0 (API 1)
break;
case Build.VERSION_CODES.BASE_1_1:
// Android 1.1 (API 2)
break;
case Build.VERSION_CODES.CUPCAKE:
// Android 1.5 (API 3)
break;
case Build.VERSION_CODES.DONUT:
// Android 1.6 (API 4)
break;
case Build.VERSION_CODES.ECLAIR:
// Android 2.0 (API 5)
break;
case Build.VERSION_CODES.ECLAIR_0_1:
// Android 2.0.1 (API 6)
break;
case Build.VERSION_CODES.ECLAIR_MR1:
// Android 2.1 (API 7)
break;
case Build.VERSION_CODES.FROYO:
// Android 2.2 (API 8)
break;
case Build.VERSION_CODES.GINGERBREAD:
// Android 2.3 (API 9)
break;
case Build.VERSION_CODES.GINGERBREAD_MR1:
// Android 2.3.3 (API 10)
break;
case Build.VERSION_CODES.HONEYCOMB:
// Android 3.0 (API 11)
break;
case Build.VERSION_CODES.HONEYCOMB_MR1:
// Android 3.1 (API 12)
break;
case Build.VERSION_CODES.HONEYCOMB_MR2:
// Android 3.2 (API 13)
break;
case Build.VERSION_CODES.ICE_CREAM_SANDWICH:
// Android 4.0 (API 14)
break;
case Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1:
// Android 4.0.3 (API 15)
break;
case Build.VERSION_CODES.JELLY_BEAN:
// Android 4.1 (API 16)
break;
case Build.VERSION_CODES.CUR_DEVELOPMENT:
// 未リリースの開発用バージョン
break;
default:
break;
}
以上が2012/09/09時点の全バージョンになります。
一般販売されているのは Android 1.5 以降のため、以下の3つは考える必要はありません。
・Build.VERSION_CODES.BASE
・Build.VERSION_CODES.BASE_1_1
・Build.VERSION_CODES.CUR_DEVELOPMENT

(すでに 1.6 すら対応必要なさそうですが…)

これを書くまで気づかなかったけど、Androidのコードネームはアルファベット順だったんですね。
お菓子の名前を適当につけているだけだと思っていました。
今は「J」で次は「K」。Android 5.0 で「Key Lime Pie」みたい。
Android 'Key Lime Pie' comes after Jelly Bean
プロフィール

とむ・やむくん

Author:とむ・やむくん
管理人について

Windows 7 / 64bit
Eclipse 4.2 Juno (日本語パッチ済)

スポンサーサイト
最新トラックバック
検索フォーム
ブロとも申請フォーム
QRコード
QR
Twitter
2013/01/04 19:00 カウント開始

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。