276 views
この記事は最終更新から 547日 が経過しています。
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スレッドで表示を更新 } }