开始在 Android 上使用 SQLite






4.80/5 (9投票s)
使用 Android 上的 SQLite 数据库执行数据的 CRUD 操作,即创建、读取、更新和删除。
引言
每个应用程序都涉及数据。大多数数据由用户通过各种输入控件提供,例如文本字段、复选框、单选按钮组、下拉列表和按钮。虽然有些数据是瞬时的,但大多数数据需要即使在应用程序停止运行后也能保留下来。Android 提供了许多巧妙的技术来本地存储持久数据。在本文中,您将学习如何在 Android 上使用 SQLite 数据库执行数据的 **CRUD** 操作,即 **创建 (Create)**、**读取 (Read)**、**更新 (Update)** 和 **删除 (Delete)**。
准备工作
在您最喜欢的 Android IDE 中,启动一个新的 Android 应用项目。我们将应用程序名称命名为AndroidSQLite
,域名为peterleowblog.com
。您的项目生成的包名将是com.peterleowblog.androidsqlite
。
在该项目中,创建一个名为MainActivity
的 Android Activity。如图 1 所示,此MainActivity
的用户界面 (UI) 包含以下控件:
- 一个用于输入电子邮件的
EditText
文本字段。 - 一个包含两个
RadioButtons
的RadioGroup
控件,用于性别选择。 - 三个
CheckBox
控件,用于爱好选择。 - 一个用于星座选择的
Spinner
控件。 - 三个
Button
控件——分别用于保存、检索和更新数据。
用户可以输入新信息并将其保存到 SQLite 数据库。之后,用户可以使用电子邮件作为关键字检索信息,编辑并更新信息,或者将其从 SQLite 数据库中删除。
准备好资源
您需要在*strings.xml*中包含以下string
资源:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">SQLite on Android</string>
<string name="email">Email</string>
<string name="gender">Gender</string>
<string name="female">Female</string>
<string name="male">Male</string>
<string name="hobbies">Hobbies</string>
<string name="coding">Coding</string>
<string name="writing">Writing</string>
<string name="jogging">Jogging</string>
<string name="zodiac">Zodiac</string>
<string name="save">Save</string>
<string name="retrieve">Retrieve</string>
<string name="delete">Delete</string>
<string-array name="zodiac">
<item>Aries</item>
<item>Taurus</item>
<item>Gemini</item>
<item>Cancer</item>
<item>Leo</item>
<item>Virgo</item>
<item>Libra</item>
<item>Scorpio</item>
<item>Sagittarius</item>
<item>Capricorn</item>
<item>Aquarius</item>
<item>Pisces</item>
</string-array>
</resources>
渲染视图
负责渲染图 1 视图的 XML 代码包含在*activity_main.xml*中,如下所示:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text=""
android:id="@+id/textView"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:ems="10"
android:id="@+id/txtEmail"
android:layout_below="@+id/textView4"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/email"
android:id="@+id/textView4"
android:layout_below="@+id/textView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/gender"
android:id="@+id/textView5"
android:layout_below="@+id/txtEmail"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<RadioGroup
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/textView5"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:id="@+id/radioGroupGender">
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/male"
android:id="@+id/radMale"
android:checked="false" />
<RadioButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/female"
android:id="@+id/radFemale"
android:checked="false" />
</RadioGroup>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/hobbies"
android:id="@+id/textView6"
android:layout_below="@+id/radioGroupGender"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/coding"
android:id="@+id/chkCoding"
android:onClick="onCheckboxClicked"
android:checked="false"
android:layout_below="@+id/textView6"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/writing"
android:id="@+id/chkWriting"
android:onClick="onCheckboxClicked"
android:layout_below="@+id/chkCoding"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:checked="false" />
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/jogging"
android:id="@+id/chkJogging"
android:onClick="onCheckboxClicked"
android:layout_below="@+id/chkWriting"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:checked="false" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="@string/zodiac"
android:id="@+id/textView7"
android:layout_below="@+id/chkJogging"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<Spinner
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/spinnerZodiac"
android:layout_below="@+id/textView7"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/save"
android:id="@+id/btnSave"
android:onClick="save"
android:layout_below="@+id/spinnerZodiac"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/retrieve"
android:onClick="retrieve"
android:id="@+id/btnRetrieve"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true"
android:layout_centerInParent="false"
android:layout_centerVertical="false" />
<Button
android:text="@string/delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/spinnerZodiac"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:id="@+id/btnDelete" />
</RelativeLayout>
</ScrollView>
控制 Activity
最后但同样重要的是,处理 UI 控件及其行为的代码包含在*MainActivity.java*中,如下所示:
package com.peterleowblog.androidsqlite;
import android.app.Activity;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.Spinner;
import android.widget.TextView;
public class MainActivity extends Activity implements RadioGroup.OnCheckedChangeListener,
AdapterView.OnItemSelectedListener{
private String email, gender, hobbies, zodiac;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((TextView) findViewById(R.id.textView)).setText
(getResources().getString(R.string.app_name));
email = gender = hobbies = zodiac = "";
RadioGroup radioGroupGender = (RadioGroup) findViewById(R.id.radioGroupGender);
radioGroupGender.setOnCheckedChangeListener(this);
Spinner spinnerZodiac = (Spinner) findViewById(R.id.spinnerZodiac);
// Populate the spinner with data source
ArrayAdapter<CharSequence> adapter =
ArrayAdapter.createFromResource(this, R.array.zodiac,
android.R.layout.simple_spinner_item);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
spinnerZodiac.setAdapter(adapter);
}
@Override
public void onCheckedChanged(RadioGroup radioGroup, int i) {
int radioButtonId = radioGroup.getCheckedRadioButtonId();
RadioButton radioButton = (RadioButton)radioGroup.findViewById(radioButtonId);
gender = radioButton.getText().toString();
}
public void onCheckboxClicked(View view) {
CheckBox chkJogging = (CheckBox) findViewById(R.id.chkJogging);
CheckBox chkCoding = (CheckBox) findViewById(R.id.chkCoding);
CheckBox chkWriting = (CheckBox) findViewById(R.id.chkWriting);
StringBuilder sb = new StringBuilder();
if (chkJogging.isChecked()) {
sb.append(", " + chkJogging.getText());
}
if (chkCoding.isChecked()) {
sb.append(", " + chkCoding.getText());
}
if (chkWriting.isChecked()) {
sb.append(", " + chkWriting.getText());
}
if (sb.length() > 0) { // No toast if the string is empty
// Remove the first comma
hobbies = sb.deleteCharAt(sb.indexOf(",")).toString();
} else {
hobbies = "";
}
}
public void onItemSelected(AdapterView<?> parent,
View view, int position, long id) {
zodiac = parent.getItemAtPosition(position).toString();
}
public void onNothingSelected(AdapterView<?> parent) {
// An interface callback
}
public void save(View view) {
// Add code to insert/update data into SQLite
}
public void retrieve(View view) {
// Add code to retrieve data from SQLite
setupUI(); // A method to set the data on the UI controls
}
public void delete(View view) {
// Add code to delete data from SQLite
setupUI(); // A method to set the data on the UI controls
}
protected void setupUI(){
((EditText)findViewById(R.id.txtEmail)).setText(email);
RadioButton radMale = (RadioButton)findViewById(R.id.radMale);
RadioButton radFemale = (RadioButton)findViewById(R.id.radFemale);
if (gender.equals("Male")){
radMale.setChecked(true);
} else if (gender.equals("Female")){
radFemale.setChecked(true);
} else {
radMale.setChecked(false);
radFemale.setChecked(false);
}
CheckBox chkCoding = (CheckBox)findViewById(R.id.chkCoding);
CheckBox chkWriting = (CheckBox)findViewById(R.id.chkWriting);
CheckBox chkJogging = (CheckBox)findViewById(R.id.chkJogging);
chkCoding.setChecked(false);
chkWriting.setChecked(false);
chkJogging.setChecked(false);
if (hobbies.contains("Coding")) {
chkCoding.setChecked(true);
}
if (hobbies.contains("Writing")) {
chkWriting.setChecked(true);
}
if (hobbies.contains("Jogging")) {
chkJogging.setChecked(true);
}
Resources resource = getResources();
String[] zodiacArray = resource.getStringArray(R.array.zodiac);
for(int i = 0; i < zodiacArray.length; i++){
if(zodiacArray[i].equals(zodiac)){
((Spinner)findViewById(R.id.spinnerZodiac)).setSelection(i);
}
}
}
}
现在,您可以开始深入研究 SQLite 数据库的实现。让我们开始吧……
学习基础知识
SQLite 是一个开源的事务性 SQL 数据库引擎,它是独立的、无服务器的,并且不需要配置。Android 提供了android.database.sqlite包,其中包含应用程序可以用来创建和管理其自有私有数据库的类。
要实现SQLite数据库,推荐的方法是创建一个SQLiteOpenHelper的子类,并重写其onCreate()和onUpgrade()方法。当实例化该子类时,它将打开数据库(如果存在),如果不存在则创建它,或在必要时进行升级。事务用于确保数据库始终保持一致状态。您可以在子类中添加操作数据库的方法。例如,下面的代码显示了一个名为SqliteHelper
的SQLiteOpenHelper的子类:
public class SqliteHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "MyDatabase";
public static final int DATABASE_VERSION = 1;
public SqliteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE users
(email TEXT PRIMARY KEY, gender Text, hobbies Text, zodiac Text)";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i2) {
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
// Implement methods to manipulate the database
}
让我们继续探讨 SQLite 在 Android 上的 CRUD 实现。
Write
写入 SQLite 数据库涉及insert
或update
操作。请遵循以下步骤:
- 调用
SQLiteOpenHelper
的getWritableDatabase()方法以获取一个代表数据库的SQLiteDatabase
对象,并提供读写操作的方法。例如:SQLiteDatabase db = this.getWritableDatabase();
- 将要写入的值放入一个ContentValues对象中。例如:
ContentValues contentValues = new ContentValues(); contentValues.put("gender", gender); contentValues.put("hobbies", hobbies);
- 要将值**插入**为新记录,请调用
SQLiteDatabase
对象的insert()方法,并将ContentValues
对象作为第三个参数传递。第一个参数是表名。此方法将返回新插入行的行 ID,如果发生错误则返回-1
。例如:result = db.insert("users", null, contentValues);
- 要使用新值**更新**现有记录,请调用
SQLiteDatabase
对象的update()方法,并将ContentValues
对象作为第二个参数传递。第三个参数是WHERE
子句,最后一个参数是传递给WHERE
子句中?
占位符的参数。此方法将返回受影响的行数。例如:result = db.update("users", contentValues, "email=?", new String[] { email });
读取
要执行**读取**操作到 SQLite 数据库,请遵循以下步骤:
- 调用
SQLiteOpenHelper
的getReadableDatabase()方法以获取一个代表数据库的SQLiteDatabase
对象,并提供读取操作的方法。例如:SQLiteDatabase db = this.getReadableDatabase();
- 调用
SQLiteDatabase
对象的rawQuery()方法,并将 SQL 查询语句作为第一个参数传递。第二个参数是传递给 SQL 查询语句中?
占位符的参数。此方法将返回一个代表数据库查询返回的结果集的Cursor
对象。例如:String sql = "SELECT * FROM users WHERE email=?"; Cursor cursor = db.rawQuery(sql, new String[] { email });
删除
最后,要从 SQLite 数据库中**删除**数据,请遵循以下步骤:
- 调用
SQLiteOpenHelper
的getWritableDatabase()方法以获取一个代表数据库的SQLiteDatabase
对象。例如:SQLiteDatabase db = this.getWritableDatabase();
- 调用
SQLiteDatabase
对象的delete()方法。此方法的第二个参数是WHERE
子句,最后一个参数是用于替换WHERE
子句中?
占位符的参数。此方法将返回受影响的行数。例如:db.delete("users", "email=?", new String[] { email });
实现起来
现在是时候让您的项目具备 SQLite 功能了。
在您的项目中,添加一个名为SqliteHelper
的新 Java 类文件,该文件扩展您项目中的SQLiteOpenHelper
类。实例化时,它将创建一个名为MyDatabase
的数据库,其中包含一个名为users
的表,该表有四个字段:email
(主键)、gender
、hobbies
和zodiac
。
包含三个方法来处理 SQLite 的 CRUD 操作。saveUser()
方法执行插入操作(如果记录是新的,即找不到匹配的电子邮件),或更新现有记录(如果存在匹配的email
值)。getUser()
方法以一个email
值作为参数,并从users
表中检索与该电子邮件值匹配的记录。最后,deleteUser()
方法以一个电子邮件值作为参数,并从users
表中删除与该电子邮件值匹配的记录。*SqliteHelper.java*的完整代码如下所示:
package com.peterleowblog.androidsqlite;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class SqliteHelper extends SQLiteOpenHelper {
public static final String DATABASE_NAME = "MyDatabase";
private static final int DATABASE_VERSION = 1;
public SqliteHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE users
(email TEXT PRIMARY KEY, gender Text, hobbies Text, zodiac Text)";
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int i, int i2) {
db.execSQL("DROP TABLE IF EXISTS users");
onCreate(db);
}
public boolean saveUser (String email, String gender, String hobbies, String zodiac)
{
Cursor cursor = getUser(email);
SQLiteDatabase db = this.getWritableDatabase();
ContentValues contentValues = new ContentValues();
contentValues.put("gender", gender);
contentValues.put("hobbies", hobbies);
contentValues.put("zodiac", zodiac);
long result;
if (cursor.getCount() == 0) { // Record does not exist
contentValues.put("email", email);
result = db.insert("users", null, contentValues);
} else { // Record exists
result = db.update
("users", contentValues, "email=?", new String[] { email });
}
if (result == -1) {
return false;
} else {
return true;
}
}
public Cursor getUser(String email){
SQLiteDatabase db = this.getReadableDatabase();
String sql = "SELECT * FROM users WHERE email=?";
return db.rawQuery(sql, new String[] { email });
}
public void deleteUser(String email){
SQLiteDatabase db = this.getWritableDatabase();
db.delete("users", "email=?", new String[] { email });
}
}
一旦*SqliteHelper.java*准备就绪,转到*MainActivity.java*,按照以下步骤实现SQLite Database
选项:
- 声明并实例化一个
SqliteHelper
类变量,如下所示:public class MainActivity extends Activity implements RadioGroup.OnCheckedChangeListener, AdapterView.OnItemSelectedListener{ private String email, gender, hobbies, zodiac; SqliteHelper sqliteHelper; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); sqliteHelper = new SqliteHelper(this); // omitted
- 在
save()
方法中添加写入数据库的代码,如下所示:public void save(View view) { email = ((EditText)findViewById(R.id.txtEmail)).getText().toString(); if (email.isEmpty()){ Toast.makeText(getApplicationContext(), "Email cannot be empty!", Toast.LENGTH_LONG).show(); return; } boolean result = sqliteHelper.saveUser(email, gender, hobbies, zodiac); if (result){ Toast.makeText(getApplicationContext(), "Successfully saved!", Toast.LENGTH_LONG).show(); } else { Toast.makeText(getApplicationContext(), "Failed to save!", Toast.LENGTH_LONG).show(); } }
- 在
retrieve()
方法中添加从数据库读取的代码,如下所示:public void retrieve(View view) { email = ((EditText)findViewById(R.id.txtEmail)).getText().toString(); Cursor cursor = sqliteHelper.getUser(email); if (cursor.getCount() != 0) { cursor.moveToFirst(); email = cursor.getString(cursor.getColumnIndex("email")); gender = cursor.getString(cursor.getColumnIndex("gender")); hobbies = cursor.getString(cursor.getColumnIndex("hobbies")); zodiac = cursor.getString(cursor.getColumnIndex("zodiac")); if (!cursor.isClosed()) { cursor.close(); } } else { email = ""; gender = ""; hobbies = ""; zodiac = ""; } setupUI(); }
- 在
delete()
方法中添加从数据库删除的代码,如下所示:public void delete(View view) { email = ((EditText)findViewById(R.id.txtEmail)).getText().toString(); sqliteHelper.deleteUser(email); email = gender = hobbies = zodiac = ""; setupUI(); }
测试 1, 2, 3, ...
在真实设备或 AVD 上启动您的应用程序,您应该会看到如图 1 所示的视图。输入电子邮件,进行一些选择,然后单击Save
按钮将输入值保存到名为users
的数据库表中。每次使用新的电子邮件值保存都会在users
表中插入一条新记录。另一方面,每次使用数据库中已存在的电子邮件值保存都会更新该现有记录。要检索记录,请在Email
文本字段中输入该记录的电子邮件,然后按Retrieve按钮。要删除记录,只需在Email
文本字段中输入该记录的电子邮件,然后按Delete按钮。
最后但同样重要
当 SQLite 数据库关闭时,调用getWritableDatabase()
和getReadableDatabase()
方法会消耗昂贵的资源和时间。因此,您应该推迟调用它们,直到真正需要它们为止。然而,一旦成功打开,数据库就会被缓存,您可以随时调用这些方法来写入数据库,而无需额外开销。您应该利用数据库缓存,让数据库连接保持打开状态,直到需要为止。关闭数据库的一种确定方法是在调用 Activity 的生命周期结束时,即:
@Override
protected void onDestroy() {
sqliteHelper.close();
super.onDestroy();
}
还在等什么?将此代码添加到您的*MainActivity.java*!
历史
- 2017 年 1 月 1 日:初始版本