65.9K
CodeProject 正在变化。 阅读更多。
Home

开始在 Android 上使用 SQLite

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.80/5 (9投票s)

2017年1月1日

CPOL

6分钟阅读

viewsIcon

31703

downloadIcon

1432

使用 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文本字段。
  • 一个包含两个RadioButtonsRadioGroup控件,用于性别选择。
  • 三个CheckBox控件,用于爱好选择。
  • 一个用于星座选择的Spinner控件。
  • 三个Button控件——分别用于保存、检索和更新数据。

用户可以输入新信息并将其保存到 SQLite 数据库。之后,用户可以使用电子邮件作为关键字检索信息,编辑并更新信息,或者将其从 SQLite 数据库中删除。

Figure 1: MainActivity

图 1:MainActivity

准备好资源

您需要在*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()方法。当实例化该子类时,它将打开数据库(如果存在),如果不存在则创建它,或在必要时进行升级。事务用于确保数据库始终保持一致状态。您可以在子类中添加操作数据库的方法。例如,下面的代码显示了一个名为SqliteHelperSQLiteOpenHelper的子类:

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 数据库涉及insertupdate操作。请遵循以下步骤:

  1. 调用SQLiteOpenHelpergetWritableDatabase()方法以获取一个代表数据库的SQLiteDatabase对象,并提供读写操作的方法。例如:
    SQLiteDatabase db = this.getWritableDatabase();
  2. 将要写入的值放入一个ContentValues对象中。例如:
    ContentValues contentValues = new ContentValues();
    contentValues.put("gender", gender);
    contentValues.put("hobbies", hobbies);
  3. 要将值**插入**为新记录,请调用SQLiteDatabase对象的insert()方法,并将ContentValues对象作为第三个参数传递。第一个参数是表名。此方法将返回新插入行的行 ID,如果发生错误则返回-1。例如:
    result = db.insert("users", null, contentValues);
  4. 要使用新值**更新**现有记录,请调用SQLiteDatabase对象的update()方法,并将ContentValues对象作为第二个参数传递。第三个参数是WHERE子句,最后一个参数是传递给WHERE子句中?占位符的参数。此方法将返回受影响的行数。例如:
    result = db.update("users", contentValues, "email=?", new String[] { email });

读取

要执行**读取**操作到 SQLite 数据库,请遵循以下步骤:

  1. 调用SQLiteOpenHelpergetReadableDatabase()方法以获取一个代表数据库的SQLiteDatabase对象,并提供读取操作的方法。例如:
    SQLiteDatabase db = this.getReadableDatabase();
  2. 调用SQLiteDatabase对象的rawQuery()方法,并将 SQL 查询语句作为第一个参数传递。第二个参数是传递给 SQL 查询语句中?占位符的参数。此方法将返回一个代表数据库查询返回的结果集的Cursor对象。例如:
    String sql = "SELECT * FROM users WHERE email=?";
    Cursor cursor =  db.rawQuery(sql, new String[] { email });

删除

最后,要从 SQLite 数据库中**删除**数据,请遵循以下步骤:

  1. 调用SQLiteOpenHelpergetWritableDatabase()方法以获取一个代表数据库的SQLiteDatabase对象。例如:
    SQLiteDatabase db = this.getWritableDatabase();
  2. 调用SQLiteDatabase对象的delete()方法。此方法的第二个参数是WHERE子句,最后一个参数是用于替换WHERE子句中?占位符的参数。此方法将返回受影响的行数。例如:
    db.delete("users", "email=?", new String[] { email });

实现起来

现在是时候让您的项目具备 SQLite 功能了。

在您的项目中,添加一个名为SqliteHelper的新 Java 类文件,该文件扩展您项目中的SQLiteOpenHelper类。实例化时,它将创建一个名为MyDatabase的数据库,其中包含一个名为users的表,该表有四个字段:email(主键)、genderhobbieszodiac

包含三个方法来处理 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 日:初始版本
© . All rights reserved.