大量データの読み書き、さらに検索したい場合はデータベースが便利で、AndroidではSQLiteを使います。ここでは簡単な例を試してみます。
2021.2.1
SQLite
最初にGoogleはデータベースとして Room を推奨しています。
Room は、SQLite 全体を対象とする抽象化レイヤを提供して、
SQLite を最大限に活用しつつ、スムーズなデータベース アクセスを可能にしてくます
SQLiteが使えないわけではないのですが、GoogleはをRoomがお勧めしています
Roomの関連ページです
Room を使用してローカル データベースにデータを保存する
Room | Android デベロッパー | Android Developers
Room 永続ライブラリ | Android デベロッパー | Android …
~~~
それでもSQLiteが…という人のため残しておきます
SQLiteのデータは表のような構造で、関係(Relation)と呼ぶ概念でモデル化されています。例えば企業の株価をこのようにデータベース化されているとすると
番号 | 企業名 | 株価(USD) |
001 | Apple | 217 |
002 | 1105 | |
003 | Amazon | 1764 |
004 | Microsoft | 108 |
… |
このようなデータベースをSQLiteで作りAndroidで表示させてみましょう。
SQLiteOpenHelper
データベースの作成、データの追加・削除などを管理するためにSQLiteOpenHelperのクラスがあります。これを継承したクラスを作成するところから始めます。
例えばSQLiteOpenHelperを継承したTestOpenHelperというクラスを作ります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class TestOpenHelper(context: Context) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { override fun onCreate(db: SQLiteDatabase) { db.execSQL(SQL_CREATE_ENTRIES) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // This database is only a cache for online data, so its upgrade policy is // to simply to discard the data and start over db.execSQL(SQL_DELETE_ENTRIES) onCreate(db) } override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { onUpgrade(db, oldVersion, newVersion) } companion object { // If you change the database schema, you must increment the database version. const val DATABASE_VERSION = 1 const val DATABASE_NAME = "FeedReader.db" } } |
onCreate() でテーブルを作成して、バージョンをあげると onUpgrade() にて新規バージョンのテーブルが作成されバージョンアップします。
TestDB.dbは
/data/data/[your.package.name]/database/
以下に作成されています
データベースの作成・削除
テーブル作成やアップデートの判別の execSQL() にはSQL文で実行文を文字列で入れて指定します。例えばSQL_CREATE_ENTRIESはこのように記述します。
1 2 |
"CREATE TABLE testdb ( _id INTEGER PRIMARY KEY, items TEXT, "+ "price INTEGER)" |
これを汎用性と意味がわかるようにしてまとめるとこうなります。(注)文なのでスペースは意味がありますので消さないように
1 2 3 4 5 |
private const val SQL_CREATE_ENTRIES = "CREATE TABLE " + TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY," + COLUMN_NAME_TITLE + " TEXT," + COLUMN_NAME_SUBTITLE + " INTEGER)" |
同様にSQL_DELETE_ENTRIESもこのように書き換えられます。
1 2 |
private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + TABLE_NAME |
データ追加
データの書き込みは execSQL() を使うこともできますが、ContentValueを使いマップデータ型のkeyとvalueとして扱うことも可能です。
1 2 3 4 5 6 |
private fun insertData(db: SQLiteDatabase, com: String, price: Int) { val values = ContentValues() values.put("company", com) values.put("stockprice", price) db.insert("testdb", null, values) } |
データ読み出し
読み出しは query() メソッドを使います。実際にここではテーブル名とコラム名の配列を設定するだけの簡単なものとして作りました。
1 2 3 4 5 6 7 8 9 |
val cursor = db.query( TABLE_NAME, // The table to query projection, // The array of columns to return (pass null to get all) selection, // The columns for the WHERE clause selectionArgs,// The values for the WHERE clause null, // don't group the rows null, // don't filter by row groups sortOrder // The sort order ) |
サンプルコード
まとめてみます。EditTextからデータを入力してデータベースを作ってみます。
TestOpenHelper.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
//package your.package.name import android.content.Context import android.database.sqlite.SQLiteDatabase import android.database.sqlite.SQLiteOpenHelper class TestOpenHelper internal constructor(context: Context?) : SQLiteOpenHelper(context, DATABASE_NAME, null, DATABASE_VERSION) { override fun onCreate(db: SQLiteDatabase) { // テーブル作成 // SQLiteファイルがなければSQLiteファイルが作成される db.execSQL( SQL_CREATE_ENTRIES ) } override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { // アップデートの判別 db.execSQL( SQL_DELETE_ENTRIES ) onCreate(db) } override fun onDowngrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) { onUpgrade(db, oldVersion, newVersion) } companion object { // データーベースのバージョン private const val DATABASE_VERSION = 1 // データーベース名 private const val DATABASE_NAME = "TestDB.db" private const val TABLE_NAME = "testdb" private const val _ID = "_id" private const val COLUMN_NAME_TITLE = "company" private const val COLUMN_NAME_SUBTITLE = "stockprice" private const val SQL_CREATE_ENTRIES = "CREATE TABLE " + TABLE_NAME + " (" + _ID + " INTEGER PRIMARY KEY," + COLUMN_NAME_TITLE + " TEXT," + COLUMN_NAME_SUBTITLE + " INTEGER)" private const val SQL_DELETE_ENTRIES = "DROP TABLE IF EXISTS " + TABLE_NAME } } |
ActivityからContextをSQLiteOpenHelperを継承したTestOpenHelperに渡し、データベースを作成。読み出しをボタンでデータベースから読出し表示させます。
MainActivity.kt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
//package your.package.name import android.content.ContentValues import android.database.sqlite.SQLiteDatabase import android.os.Bundle import android.widget.Button import android.widget.EditText import android.widget.TextView import androidx.appcompat.app.AppCompatActivity class MainActivity : AppCompatActivity() { private lateinit var helper: TestOpenHelper private lateinit var db: SQLiteDatabase override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val editTextKey = findViewById<EditText>(R.id.edit_text_key) val editTextValue = findViewById<EditText>(R.id.edit_text_value) val insertButton = findViewById<Button>(R.id.button_insert) insertButton.setOnClickListener { helper = TestOpenHelper(applicationContext) db = helper.writableDatabase val key: String = editTextKey.text.toString() val value: String = editTextValue.text.toString() // 価格は整数を想定 insertData(db, key, value.toInt()) } val readButton = findViewById<Button>(R.id.button_read) readButton.setOnClickListener { readData() } } private fun readData() { helper = TestOpenHelper(applicationContext) db = helper.readableDatabase val cursor = db.query( "testdb", arrayOf("company", "stockprice"), null, null, null, null, null ) cursor.moveToFirst() val sbuilder = StringBuilder() for (i in 0 until cursor.count) { sbuilder.append(cursor.getString(0)) sbuilder.append(": ") sbuilder.append(cursor.getInt(1)) sbuilder.append("\n") cursor.moveToNext() } // 忘れずに! cursor.close() val textView = findViewById<TextView>(R.id.text_view) textView.text = sbuilder.toString() } private fun insertData(db: SQLiteDatabase, com: String, price: Int) { val values = ContentValues() values.put("company", com) values.put("stockprice", price) db.insert("testdb", null, values) } } |
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#dfe" tools:context=".MainActivity"> <EditText android:id="@+id/edit_text_key" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="50dp" android:autofillHints="@string/hint_key" android:background="#fff" android:hint="@string/hint_key" android:inputType="text" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintRight_toLeftOf="@+id/edit_text_value" app:layout_constraintVertical_bias="0.05" /> <EditText android:id="@+id/edit_text_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="50dp" android:autofillHints="@string/hint_value" android:background="#fff" android:hint="@string/hint_value" android:inputType="numberDecimal" android:textSize="30sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toRightOf="@+id/edit_text_key" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.05" /> <Button android:id="@+id/button_insert" android:layout_width="0dp" android:layout_height="wrap_content" android:text="@string/insert" android:textSize="20sp" android:layout_marginStart="20dp" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintVertical_bias="0.1" app:layout_constraintRight_toLeftOf="@+id/button_read" app:layout_constraintTop_toBottomOf="@+id/edit_text_key" /> <Button android:id="@+id/button_read" android:layout_width="0dp" android:layout_height="wrap_content" android:text="@string/read" android:textSize="20sp" android:layout_marginEnd="20dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintVertical_bias="0.1" app:layout_constraintLeft_toRightOf="@+id/button_insert" app:layout_constraintTop_toBottomOf="@+id/edit_text_value" /> <TextView android:id="@+id/text_view" android:layout_width="250dp" android:layout_height="wrap_content" android:textSize="32sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toBottomOf="@+id/button_read" app:layout_constraintVertical_bias="0.2" /> </androidx.constraintlayout.widget.ConstraintLayout> |
リソース
strings.xml
1 2 3 4 5 6 7 |
<resources> <string name="app_name">Your App Name</string> <string name="hint_key">company</string> <string name="hint_value">US$price</string> <string name="insert">insert</string> <string name="read">read</string> </resources> |
データとして企業名と株価をEditTextに入れてデータベースを作っていきます。尚、価格は整数で扱うようにしていますので小数点を使う場合はコードを変更してください。
次は、実際にデータが保存されているのを確認します。
関連ページ:
References:
Save data using SQLite | Android Developers
SQLite Home Page