804 views
この記事は最終更新から 1130日 が経過しています。
1. やりたいこと
Androidアプリで簡易データベースである SQLiteを使いたい。
制御が簡単(?)な Roomライブラリを使って実現してみた。
Roomとは?
については Android公式サイトで丁寧に解説されているのでこちらを参照されたい。
https://developer.android.com/training/data-storage/room?hl=ja

2. やってみる
UIから操作しながら動作確認をしたかったので、以下のような画面を持つアプリを作った。
・[登録]ボタンを押下すると、EditText欄に入力した文字列を DBに書き込む。
・[全消去]ボタンを押下すると、DBに保存されているすべてのデータを消去する。
・[DB削除]ボタンを押下すると、DB自体を削除する。(開発作業中に DBを作り直したい場合があるので…)

以下の 4個のプログラムソースファイルで実装した。
1) AppDatabase.java
2) Memo.java
3) MemoDao.java
4) MainActivity.java
(0) build.gradle (:app)
前述の Android公式サイト に書かれている通りに、アプリの build.gradle ファイルに次の依存関係を追加する。
※記述後の Sync.now を忘れずに!
dependencies {
def room_version = "2.5.0"
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
(1) AppDatabase.java
データベース本体を定義する。
package com.example.testroomsqlite;
import androidx.room.Database;
import androidx.room.RoomDatabase;
@Database(entities = {Memo.class}, version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
public abstract MemoDao memoDao();
}
(2) Memo.java
テーブルレコードになるデータ、すなわちエンティティ(Entity)を定義する。
主キーに AUTO_INCREMENT を付与したい場合、主キーのアノテーションの右側に以下のように書き足す。
@PrimaryKey(autoGenerate = true)
package com.example.testroomsqlite;
import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity
public class Memo {
public Memo(){
}
@PrimaryKey(autoGenerate = true)
public int mid;
@ColumnInfo(name = "memo_content")
public String memoContent;
@ColumnInfo(name = "memo_datetime")
public String memoDateTime;
public String getMemoContent(){
return this.memoContent;
}
public void setMemoContent(String memoContent){
this.memoContent = memoContent;
}
}
(3) MemoDao.java
memoテーブルへの DAO(Data Access Object)を定義する。
・@Queryアノテーションを書いた場合、その右側にSQLコマンドを書く。
・insertメソッドの戻り値を voidではなく longにすると、上記の @PrimaryKey(autoGenerate=true) で付与された midの値が返される。
package com.example.testroomsqlite;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.Query;
import java.util.List;
@Dao
public interface MemoDao {
@Query("SELECT * FROM memo")
List<Memo> getAll();
@Insert
long insert(Memo memo);
// 戻り値: 自動的に割り当てられた主キー Memo#mid の値が返る。
@Delete
void delete(Memo memo);
// SQLiteでは DROPコマンドが使えない。→ DELETE FROMで全削除する。
@Query("DELETE FROM memo")
void deleteAll();
}
(4) MainActivity.java
アプリ本体の制御を書く。
注意点はただ一つ、それは…
DB制御は非UIスレッドに書くこと!
でないと、以下のように例外が発生し、アプリがエラー終了してしまう。
Caused by: java.lang.IllegalStateException: Cannot access database on the main thread
since it may potentially lock the UI for a long period of time.
長時間 UIがロックされる可能性があるため、メインスレッドでDBアクセスはダメ
とのことだ。
よって、下記のプログラムソースコードでは、
DBアクセスは、別スレッドを起動して実行している。
package com.example.testroomsqlite;
import androidx.appcompat.app.AppCompatActivity;
import androidx.room.Room;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import java.util.List;
import java.util.concurrent.Executors;
public class MainActivity extends AppCompatActivity {
private static AppDatabase dbInst = null; // DB
private final static String dbName = "my-database"; // DB名称
private List<Memo> memoList; // DBからロードしたメモデータ
private final Handler hMainThread = new Handler(); // Main threadのハンドル
// DBインスタンスを取得
public static AppDatabase getDb(Context context) {
if (dbInst == null) {
dbInst = Room.databaseBuilder(context, AppDatabase.class, dbName).build();
}
return dbInst;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.UpdateText(); // DBからロードしたデータで表示を更新
}
// DBからロードしたデータで表示を更新
private void UpdateText() {
// DBデータ読み出しのスレッドを生成&実行
// ※DBアクセスは非UIスレッド(=非Mainスレッド)で実行する必要がある。
// ※スレッドインスタンスの再利用はできない → 毎度インスタンスを生成する。
Thread th = new Thread(runRecallFromDB);
th.start();
}
// DBデータ読み出しスレッドの実処理
private final Runnable runRecallFromDB = new Runnable() {
@Override
public void run() {
System.out.println("BEGIN: runRecallFromDB");
AppDatabase db = getDb(getApplicationContext());
MemoDao dao = db.memoDao(); // DAOを生成
memoList = dao.getAll(); // DBからデータをロード
hMainThread.post(runUpdateText); // UIスレッドに表示を依頼
System.out.println("END: runRecallFromDB");
}
};
// UIスレッドでの表示更新処理
private final Runnable runUpdateText = new Runnable() {
@Override
public void run() {
// memoListに格納されたメモデータを順に表示する。
System.out.println("BEGIN: runUpdateText");
StringBuilder sb = new StringBuilder();
for (Memo memo : memoList) {
sb.append(memo.getMemoContent() + "\n");
}
// 生成した文字列を TextViewに表示する。
TextView tv = findViewById(R.id.tv_list);
tv.setText(sb.toString());
System.out.println("END: runUpdateText");
}
};
// [登録]ボタン押下イベントハンドラ
// ・EditTextに入力された文字列を DB登録し、メモ一覧の表示を更新する。
public void OnPushAddButton(View v) {
// EditTextに入力された文字列を取得する。
EditText et = findViewById(R.id.et_text);
String txt = et.getText().toString();
et.setText(""); // 入力欄をクリア
System.out.println("et_text : " + txt);
// DB更新を非UIスレッドで実行する。
// ※ここでは Runnableを無名クラスで使う。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("BEGIN: OnPushAddButton Thread");
AppDatabase db = getDb(getApplicationContext());
MemoDao dao = db.memoDao(); // DAOを生成
Memo memo = new Memo(); // DBに書き込むデータを作成
memo.setMemoContent(txt); // :
dao.insert(memo); // DBに書き込みを実行
System.out.println("END: OnPushAddButton Thread");
UpdateText(); // UIスレッドで表示を更新
}
}).start(); // 上記の処理を別スレッドで実行する。
}
// [全消去]ボタン押下イベントハンドラ
// DBに登録されている全メモデータを消去する。
public void OnPushAllDeleteButton(View v) {
// DB更新を非UIスレッドで実行する。
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("BEGIN: OnPushAllDeleteButton Thread");
AppDatabase db = getDb(getApplicationContext());
MemoDao dao = db.memoDao(); // DAOを生成
dao.deleteAll(); // 全レコードを削除
System.out.println("END: OnPushAllDeleteButton Thread");
UpdateText(); // UIスレッドで表示を更新
}
}).start(); // 上記の処理を別スレッドで実行する。
}
// [DB削除]ボタン押下イベントハンドラ
// DB自体を削除する。
public void OnPushRemoveDBButton(View v) {
System.out.println("BEGIN: OnPushRemoveDBButton Thread");
Context context = getApplicationContext();
context.deleteDatabase(dbName);
dbInst = null;
System.out.println("END: OnPushRemoveDBButton Thread");
UpdateText(); // UIスレッドで表示を更新
}
}