外部SDからDBファイルをコピーしアプリ内で利用する

前回記事でMainActivityだけでなく追加Activityの作成および起動まで実装し、Android開発にもだいぶ慣れてきた。そこで、練習用のsqlite DBから卒業し、本番用を組み込んでいく。

以前の記事、Android用sqliteデータベースを外部で作成する-その1Android用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);
    }

この実行結果は以下。

コメント