前回記事でMainActivityだけでなく追加Activityの作成および起動まで実装し、Android開発にもだいぶ慣れてきた。そこで、練習用のsqlite DBから卒業し、本番用を組み込んでいく。
以前の記事、Android用sqliteデータベースを外部で作成する-その1とAndroid用sqliteデータベースを外部で作成する-その2の2回にわたって本番仕様のDBをLinux Boxで作成してAndroidに読み込むという実験を実施済みだ。この実験のゴールは、Linux Box上でDB構造を定義しそのファイルをAndroidに転送して活用する、ということだった(AndroidでDB定義やテストを行うのはかったるいので)。
というわけで今回は、GUIは前回記事そのままで、本番DB入れ替え機構を実装し、動作確認するところを目標とする。
sqlite DBのテストが出来るGUIツール
「DB Browser for SQLite」はSQLiteデータベースのSQLクエリを簡単に実行出来るLinuxやWindowsで使えるGUIツールだ。このツールを使えば、sqlite DBのテーブルデータの中身や、SQL文のテストがGUI上で簡単に確認出来る。
まず、Android用sqliteデータベースを外部で作成する-その2で定義したデータベースにCSVでいくつかテスト用のレコードを追加したうえでツールに読み込ませた。そして、以下のListView一覧表示に必要なデータを抽出するSQLをテストする。
select ID as _id,Photo1,DateTime,CatName,SubName,Comment from (select * from record inner join subcategory on record.CatID=subcategory.CatID and record.SubID=subcategory.SubID) as sub1 inner join category on sub1.catID = category.catID;
CatName,SubNameはIDがrecordテーブルに記録されており、対応する文字列はそれぞれcategoryとsubcategoryの各テーブルに記録されていて、それをSQLのinner join句で引っ張ってきて取り出すというSQL文だ。このSQL文を、Windows版DB Browser for SQLiteで実行した時のGUIスクショは以下。
SQL文をAndroid上でデバッグしろと言われても難しいので、こんな風に手軽にGUI上でテスト出来るのは非常に有り難い。
DB Adapterの更新
ContentProviderにCursorを渡すためのLogDBAdapterクラスを新しいDBに対応させるとともに、DBファイルの入れ替えに対応させた。ソース全体は以下。
LogDBAdapter.java
package com.example.photologger;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class LogDBAdapter {
private static final String svModDBPath = "/storage/extSdCard/Temp/";
private static final String LOG_TAG = "PhotoLogger";
private static final String DATABASE_NAME = "photolog.db";
public static final String PHOTO_TABLE_NAME = "record";
public static final int COL_ID_IX = 0;
public static final int COL_PHOTO_URI_IX = 1;
private static Context context;
SQLiteDatabase db;
LogDBAdapter(Context con) {
context = con;
try {
File dbFile = con.getDatabasePath(DATABASE_NAME);
if ( ! dbFile.exists() ) {
// DBファイルが無い時→SDカードからコピー
String svDbFilePath = svModDBPath + DATABASE_NAME;
InputStream src = new FileInputStream(svDbFilePath);
OutputStream dst = new FileOutputStream(dbFile);
copyStream(dst,src);
}
} catch(IOException err){
Log.e(LOG_TAG, err.getMessage());
}
db = context.openOrCreateDatabase(DATABASE_NAME, Context.MODE_PRIVATE, null);
}
protected int copyStream(OutputStream dst, InputStream src) throws IOException
{
int cnt = 0;
byte[] fileReader = new byte[4096];
while (true) {
int read = src.read(fileReader);
if (read < 0) {
break;
}
dst.write(fileReader, 0, read);
cnt += read;
}
return cnt;
}
SQLiteDatabase getWritableDatabase() {
return db;
}
Cursor getURIcursor() {
final String query = "select ID as _id,Photo1,DateTime,CatName,SubName,Comment from (select * from record inner join subcategory on record.CatID=subcategory.CatID and record.SubID=subcategory.SubID) as sub1 inner join category on sub1.catID = category.catID;";
Cursor c = null;
try {
c = db.rawQuery(query, null);
int cnt = c.getCount();
Log.i(LOG_TAG, "getURIcursor(): count=" + cnt);
} catch(SQLiteException err){
Log.e(LOG_TAG, err.getMessage());
}
return c;
}
}
コンストラクタで、DBファイルの有無を確認し、無かったらSDカードの指定パス(/storage/extSdCard/Temp/photolog.db)からコピーしてくるようにした。これにより、デバッグの過程でDBの変更を行った時に、上記SDカード上のファイル指定位置に新しいDBファイルを置いたうえでアプリ内部のDBファイルを削除しておくと、SDカード上のDBファイルを勝手にアプリ内領域にコピーしてDBとして使うようになる。
getURIcursor()メソッドは上記「DB Browser for SQLite」でテスト実行したSQL文をそのまま実行させている。前回コードよりも多くのcolumnが取得されており、以降でそれらをListView表示出来るようにさせていく。
CursorAdapter拡張クラスを変更し、増えたcolumnをListViewに表示させる
上記LogDBAdapterのgetURIcursorメソッドでcolumnを増やしたので、その内容をListViewに表示させたい。今回の変更ではContentProviderはCursorをAdapterの間で媒介するだけで中身にタッチしないので、Adapterの変更だけでOK。但しListViewのレイアウト変更はせずに、Adapterメソッド内で文字列結合して簡易表示する方針とする。というわけで、基本的には以前の記事で作成したMyCursorAdapterのbindViewメソッドだけを以下のように変更すればよい。
MyCursorAdapter.java抜粋
@Override
public void bindView(View view, Context context, Cursor cursor) {
ViewHolder holder = (ViewHolder) view.getTag();
String photoUri = cursor.getString(LogDBAdapter.COL_PHOTO_URI_IX);
long tnid = Long.parseLong(photoUri.substring(photoUri.lastIndexOf("/") + 1));
Bitmap img = MediaStore.Images.Thumbnails.getThumbnail(mContentResolver, tnid, MediaStore.Images.Thumbnails.MICRO_KIND,null);
holder.tn_img.setImageBitmap(img);
String info = cursor.getString(2) + ',' + cursor.getString(3) + ',' + cursor.getString(4);
holder.uri_text.setText(info);
}
この実行結果は以下。
コメント