Android 如何使用SQLite保存數(shù)據(jù)
簡(jiǎn)介
對(duì)于重復(fù)數(shù)據(jù)或結(jié)構(gòu)化數(shù)據(jù)(例如聯(lián)系信息),將數(shù)據(jù)保存到數(shù)據(jù)庫(kù)是理想選擇。android.database.sqlite 軟件包中提供了在 Android 上使用數(shù)據(jù)庫(kù)所需的 API。本篇文章介紹在 Android 上使用 SQLite 數(shù)據(jù)庫(kù)。
定義架構(gòu)和協(xié)定
SQL 數(shù)據(jù)庫(kù)的主要原則之一是架構(gòu),即數(shù)據(jù)庫(kù)組織方式的正式聲明。架構(gòu)反映在你用于創(chuàng)建數(shù)據(jù)庫(kù)的 SQL 語(yǔ)句中。您可能會(huì)發(fā)現(xiàn)創(chuàng)建伴隨類(稱為協(xié)定類)很有用,該類以系統(tǒng)化、自記錄的方式明確指定了架構(gòu)的布局。
協(xié)定類是定義 URI、表和列名稱的常量的容器。通過(guò)協(xié)定類,您可以在同一軟件包的所有其他類中使用相同的常量。這樣一來(lái),您就可以在一個(gè)位置更改列名稱并將其傳播到整個(gè)代碼中。
組織協(xié)定類的一種良好方法是將對(duì)整個(gè)數(shù)據(jù)庫(kù)而言具有全局性的定義放入類的根級(jí)別。然后,為每個(gè)表創(chuàng)建一個(gè)內(nèi)部類。每個(gè)內(nèi)部類都枚舉相應(yīng)表的列。
注意:通過(guò)實(shí)現(xiàn)
BaseColumns接口,您的內(nèi)部類可以繼承名為_ID的主鍵字段。
例如,以下協(xié)定定義了表示 RSS Feed 的單個(gè)表的表名稱和列名稱:
public final class FeedReaderContract {
// To prevent someone from accidentally instantiating the contract class,
// make the constructor private.
private FeedReaderContract() {}
/* Inner class that defines the table contents */
public static class FeedEntry implements BaseColumns {
// 表名
public static final String TABLE_NAME = "entry";
// 列名
public static final String COLUMN_NAME_TITLE = "title";
// 列名
public static final String COLUMN_NAME_SUBTITLE = "subtitle";
}
}使用 SQL 創(chuàng)建數(shù)據(jù)庫(kù)
定義了數(shù)據(jù)庫(kù)的結(jié)構(gòu)后,應(yīng)實(shí)現(xiàn)用于創(chuàng)建和維護(hù)數(shù)據(jù)庫(kù)和表的方法。以下是用于創(chuàng)建和刪除表的一些語(yǔ)句:
// 創(chuàng)建數(shù)據(jù)庫(kù)表
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
FeedEntry._ID + " INTEGER PRIMARY KEY," +
FeedEntry.COLUMN_NAME_TITLE + " TEXT," +
FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";
// 刪除數(shù)據(jù)庫(kù)表
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;就像您在設(shè)備的內(nèi)部存儲(chǔ)空間中保存文件一樣,Android 會(huì)將您的數(shù)據(jù)庫(kù)存儲(chǔ)在您應(yīng)用的私有文件夾中。您的數(shù)據(jù)很安全,因?yàn)樵谀J(rèn)情況下,其他應(yīng)用或用戶無(wú)法訪問(wèn)此區(qū)域。
SQLiteOpenHelper 類包含一組用于管理數(shù)據(jù)庫(kù)的實(shí)用 API。當(dāng)您使用此類獲取對(duì)數(shù)據(jù)庫(kù)的引用時(shí),系統(tǒng)僅在需要時(shí)才執(zhí)行可能需要長(zhǎng)時(shí)間運(yùn)行的數(shù)據(jù)庫(kù)創(chuàng)建和更新操作,而不是在應(yīng)用啟動(dòng)期間執(zhí)行。您僅需調(diào)用 getWritableDatabase() 或 getReadableDatabase() 即可。
注意:由于這些操作可能會(huì)長(zhǎng)時(shí)間運(yùn)行,因此請(qǐng)務(wù)必在后臺(tái)線程中調(diào)用
getWritableDatabase()或getReadableDatabase()。
如需使用 SQLiteOpenHelper,請(qǐng)創(chuàng)建一個(gè)用于替換 onCreate() 和 onUpgrade() 回調(diào)方法的子類。您可能還需要實(shí)現(xiàn) onDowngrade() 或 onOpen() 方法,但這些方法并非必需。
例如,下面展示了使用上述一些命令的 SQLiteOpenHelper 實(shí)現(xiàn):
public class FeedReaderDbHelper extends SQLiteOpenHelper {
// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;
public static final String DATABASE_NAME = "FeedReader.db";
private static final String SQL_CREATE_ENTRIES =
"CREATE TABLE " + FeedEntry.TABLE_NAME + " (" +
FeedEntry._ID + " INTEGER PRIMARY KEY," +
FeedEntry.COLUMN_NAME_TITLE + " TEXT," +
FeedEntry.COLUMN_NAME_SUBTITLE + " TEXT)";
private static final String SQL_DELETE_ENTRIES =
"DROP TABLE IF EXISTS " + FeedEntry.TABLE_NAME;
public FeedReaderDbHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
public void onCreate(SQLiteDatabase db) {
// 創(chuàng)建數(shù)據(jù)庫(kù)表
db.execSQL(SQL_CREATE_ENTRIES);
}
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 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);
}
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
onUpgrade(db, oldVersion, newVersion);
}
}如需訪問(wèn)您的數(shù)據(jù)庫(kù),請(qǐng)實(shí)例化 SQLiteOpenHelper 的子類:
FeedReaderDbHelper dbHelper = new FeedReaderDbHelper(getContext());
將信息添加到數(shù)據(jù)庫(kù)
通過(guò)將 ContentValues 對(duì)象傳遞給 insert() 方法,將數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中:
// Gets the data repository in write mode SQLiteDatabase db = dbHelper.getWritableDatabase(); // Create a new map of values, where column names are the keys ContentValues values = new ContentValues(); values.put(FeedEntry.COLUMN_NAME_TITLE, title); values.put(FeedEntry.COLUMN_NAME_SUBTITLE, subtitle); // Insert the new row, returning the primary key value of the new row long newRowId = db.insert(FeedEntry.TABLE_NAME, null, values);
insert()函數(shù)介紹
public long insert (String table,
String nullColumnHack,
ContentValues values)| Parameters | Details |
|---|---|
| table | String: 要插入行的表名,該值不能為空 |
| nullColumnHack | String: 可選;可為null。SQL 不允許在未命名至少一個(gè)列名的情況下插入完全為空的記錄。如果提供的值為空,則不知道列名,無(wú)法插入空行。如果不設(shè)置為空,nullColumnHack 參數(shù)會(huì)提供可空的列名,以便在值為空的情況下明確插入 NULL。 |
| values | ContentValues: 該映射包含該行的初始列值。鍵應(yīng)該是列名,值應(yīng)該是列值。該值可以為空。 |
| Returns | Details |
|---|---|
| long | 新插入行的行 ID,如果發(fā)生錯(cuò)誤則為 -1 |
返回新創(chuàng)建行的 ID;如果在插入數(shù)據(jù)時(shí)出錯(cuò),會(huì)返回 -1。如果您的數(shù)據(jù)與數(shù)據(jù)庫(kù)中已有的數(shù)據(jù)之間存在沖突,就會(huì)出現(xiàn)這種情況。
從數(shù)據(jù)庫(kù)中讀取信息
如需從數(shù)據(jù)庫(kù)中讀取信息,請(qǐng)使用 query() 方法,向其傳遞您的選擇條件和所需的列。該方法合并了 insert() 和 update() 元素,不過(guò)列表定義了要提取的數(shù)據(jù)(“預(yù)測(cè)值”),而不是要插入的數(shù)據(jù)。查詢結(jié)果會(huì)包含在 Cursor 對(duì)象中返回給您。
SQLiteDatabase db = dbHelper.getReadableDatabase();
// Define a projection that specifies which columns from the database
// you will actually use after this query.
String[] projection = {
BaseColumns._ID,
FeedEntry.COLUMN_NAME_TITLE,
FeedEntry.COLUMN_NAME_SUBTITLE
};
// Filter results WHERE "title" = 'My Title'
String selection = FeedEntry.COLUMN_NAME_TITLE + " = ?";
String[] selectionArgs = { "My Title" };
// How you want the results sorted in the resulting Cursor
String sortOrder =
FeedEntry.COLUMN_NAME_SUBTITLE + " DESC";
Cursor cursor = db.query(
FeedEntry.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
);delete()函數(shù)介紹
public Cursor query (String table, // 要查詢的表名,不能為空
String[] columns, // 要返回的列的列表。傳遞 null 將返回所有列
String selection, // 聲明要返回哪些行的過(guò)濾器,傳遞 null 將返回給定表的所有行
String[] selectionArgs, // 可以在選擇中包含 ?,它將被選擇參數(shù)中的值替換,按照它們?cè)谶x擇中出現(xiàn)的順序
String groupBy, // 分組過(guò)濾器,傳遞 null 將導(dǎo)致行不被分組
String having, // 如果使用行分組,則過(guò)濾器聲明游標(biāo)中包含哪些行組,傳遞 null 將導(dǎo)致包含所有行組
String orderBy) // 對(duì)行進(jìn)行排序,傳遞 null 將使用默認(rèn)排序順序,該順序可能是無(wú)序的第三個(gè)參數(shù)和第四個(gè)參數(shù)(selection 和 selectionArgs)會(huì)合并起來(lái)創(chuàng)建一個(gè) WHERE 子句。由于這兩個(gè)參數(shù)是與選擇查詢分開(kāi)提供的,因此它們?cè)诤喜⒅皶?huì)進(jìn)行轉(zhuǎn)義。這樣一來(lái),您的選擇語(yǔ)句就不受 SQL 注入的影響。
如需查看光標(biāo)中的某一行,請(qǐng)使用 Cursor move 方法之一,您必須始終在開(kāi)始讀取值之前調(diào)用該方法。由于光標(biāo)從位置 -1 開(kāi)始,因此調(diào)用 moveToNext() 會(huì)將“讀取位置”置于結(jié)果的第一個(gè)條目上,并返回光標(biāo)是否已經(jīng)過(guò)結(jié)果集中的最后一個(gè)條目。對(duì)于每一行,您都可以通過(guò)調(diào)用 Cursor get 方法之一(例如 getString() 或 getLong())讀取列的值。對(duì)于每個(gè) get 方法,您必須傳遞所需列的索引位置,您可以通過(guò)調(diào)用 getColumnIndex() 或 getColumnIndexOrThrow() 獲取該位置。遍歷結(jié)果之后,請(qǐng)對(duì)光標(biāo)調(diào)用 close() 以釋放其資源。例如,以下代碼展示了如何獲取存儲(chǔ)在光標(biāo)中的所有項(xiàng)目 ID 并將其添加到列表中:
List itemIds = new ArrayList<>();
while(cursor.moveToNext()) {
long itemId = cursor.getLong(
cursor.getColumnIndexOrThrow(FeedEntry._ID));
itemIds.add(itemId);
}
cursor.close();從數(shù)據(jù)庫(kù)中刪除信息
如需從表中刪除行,您需要提供選擇條件,以標(biāo)識(shí) delete() 方法的目標(biāo)行。該機(jī)制與 query() 方法的目標(biāo)選擇參數(shù)的工作方式相同。它將選擇規(guī)范劃分為選擇子句和選擇參數(shù)。子句定義要查看的列,并允許您合并列測(cè)試。參數(shù)是要測(cè)試的值,這些值綁定到子句中。由于結(jié)果的處理方式與常規(guī) SQL 語(yǔ)句的處理方式不同,因此不受 SQL 注入的影響。
// Define 'where' part of query.
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = { "MyTitle" };
// Issue SQL statement.
int deletedRows = db.delete(FeedEntry.TABLE_NAME, selection, selectionArgs);delete() 方法的返回值表示從數(shù)據(jù)庫(kù)中刪除的行數(shù)。
delete()函數(shù)介紹
// return: 如果傳入 whereClause,則影響的行數(shù),否則為 0。要?jiǎng)h除所有行并獲取計(jì)數(shù),請(qǐng)傳遞“1”作為 whereClause。
public int delete (String table, // 要操作的表名,不能為空
String whereClause, // 刪除時(shí)應(yīng)用的可選 WHERE 子句。傳遞 null 將刪除所有行。
String[] whereArgs) // 可以在 where 子句中包含 ?s,它將被 whereArgs 中的值替換。這些值將綁定為字符串。如果 whereClause 為 null 或不包含 ?s,則 whereArgs 可能為 null。更新數(shù)據(jù)庫(kù)
如果您需要修改數(shù)據(jù)庫(kù)值的子集,請(qǐng)使用 update() 方法。
SQLiteDatabase db = dbHelper.getWritableDatabase();
// New value for one column
String title = "MyNewTitle";
ContentValues values = new ContentValues();
values.put(FeedEntry.COLUMN_NAME_TITLE, title);
// Which row to update, based on the title
String selection = FeedEntry.COLUMN_NAME_TITLE + " LIKE ?";
String[] selectionArgs = { "MyOldTitle" };
int count = db.update(
FeedReaderDbHelper.FeedEntry.TABLE_NAME,
values,
selection,
selectionArgs);update() 方法的返回值是數(shù)據(jù)庫(kù)中受影響的行數(shù)。
update()函數(shù)介紹
public int update (String table, // 要更新的表名,不能為null
ContentValues values, // 從列名到新列值的映射。 null 是一個(gè)有效值,將被轉(zhuǎn)換為 NULL。
String whereClause, // 更新時(shí)應(yīng)用的可選 WHERE 子句。傳遞 null 將更新所有行。
String[] whereArgs) // 可以在 where 子句中包含 ?s,它將被 whereArgs 中的值替換。這些值將綁定為字符串。如果 whereClause 為 null 或不包含 ?s,則 whereArgs 可能為 null。保留數(shù)據(jù)庫(kù)連接
由于在數(shù)據(jù)庫(kù)關(guān)閉時(shí),調(diào)用 getWritableDatabase() 和 getReadableDatabase() 的成本比較高,因此只要您有可能需要訪問(wèn)數(shù)據(jù)庫(kù),就應(yīng)保持?jǐn)?shù)據(jù)庫(kù)連接處于打開(kāi)狀態(tài)。通常情況下,最好在發(fā)出調(diào)用的 Activity 的 onDestroy() 中關(guān)閉數(shù)據(jù)庫(kù)。
@Override
protected void onDestroy() {
dbHelper.close();
super.onDestroy();
}調(diào)試數(shù)據(jù)庫(kù)
Android SDK 提供一個(gè) sqlite3 shell 工具,您可以使用該工具瀏覽表內(nèi)容、運(yùn)行 SQL 命令以及在 SQLite 數(shù)據(jù)庫(kù)中執(zhí)行其他實(shí)用操作。
sqlite3 可啟動(dòng)用于檢查 SQLite 數(shù)據(jù)庫(kù)的 sqlite 命令行程序。它包含用于輸出表格內(nèi)容的 .dump 以及用于輸出現(xiàn)有表格的 SQL CREATE 語(yǔ)句的 .schema 等命令。您也可以從命令行執(zhí)行 SQLite 命令,如下所示:
$ adb -s emulator-5554 shell $ sqlite3 /data/data/com.example.app/databases/rssitems.db SQLite version 3.3.12 Enter ".help" for instructions
注意:您必須擁有對(duì)文件系統(tǒng)的 root 權(quán)限(例如在模擬器上),才能訪問(wèn) SQLite 數(shù)據(jù)庫(kù)。
如需了解詳情,請(qǐng)參閱 sqlite3 命令行文檔。
到此這篇關(guān)于Android 使用SQLite保存數(shù)據(jù)的文章就介紹到這了,更多相關(guān)Android SQLite保存數(shù)據(jù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android SQLite基本用法詳解
- android中SQLite使用及特點(diǎn)
- Android SQLite數(shù)據(jù)庫(kù)連接實(shí)現(xiàn)登錄功能
- Android使用gradle讀取并保存數(shù)據(jù)到BuildConfg流程詳解
- Android通過(guò)ViewModel保存數(shù)據(jù)實(shí)現(xiàn)多頁(yè)面的數(shù)據(jù)共享功能
- Android學(xué)習(xí)筆記-保存數(shù)據(jù)到SQL數(shù)據(jù)庫(kù)中(Saving Data in SQL Databases)
- Android 實(shí)現(xiàn)永久保存數(shù)據(jù)的方法詳解
相關(guān)文章
Android 側(cè)邊滑動(dòng)關(guān)閉Activity的示例代碼
這篇文章主要介紹了Android 側(cè)邊滑動(dòng)關(guān)閉Activity的方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05
Android EditText輸入框?qū)崿F(xiàn)下拉且保存最近5個(gè)歷史記錄思路詳解
今天給大家介紹Android EditText輸入框?qū)崿F(xiàn)下拉且保存最近5個(gè)歷史記錄功能,android實(shí)現(xiàn)文本框下拉利用sharedpreferences來(lái)保存每次app啟動(dòng)和關(guān)閉時(shí)已經(jīng)填寫(xiě)的數(shù)值,具體代碼跟隨小編一起看看吧2021-07-07
Android實(shí)現(xiàn)簡(jiǎn)易版打地鼠
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)易版打地鼠,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
android打開(kāi)應(yīng)用所在的市場(chǎng)頁(yè)面進(jìn)行評(píng)分操作的方法
這篇文章主要介紹了android打開(kāi)應(yīng)用所在的市場(chǎng)頁(yè)面進(jìn)行評(píng)分操作的方法,涉及Android操作市場(chǎng)頁(yè)面評(píng)分效果的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-04-04
Android查看電池電量的方法(基于BroadcastReceiver)
這篇文章主要介紹了Android查看電池電量的方法,結(jié)合實(shí)例分析了Android使用BroadcastReceiver實(shí)現(xiàn)針對(duì)電池電量的查詢技巧,需要的朋友可以參考下2016-01-01
Android媒體開(kāi)發(fā)之音樂(lè)播放器
這篇文章主要為大家詳細(xì)介紹了Android媒體開(kāi)發(fā)之音樂(lè)播放器,播放SD卡中的音樂(lè),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12

