Android 數據存儲
一、文件存儲
文件存儲是Android中最基本的一種數據存儲方式,它不對存儲的內容進行任何的格式化處理,所有數據都是原封不動地保存到文件當中的,因而它比較適合用於存儲一些簡單的文本數據或二進制數據。如果你想使用文件存儲的方式來保存一些較爲複雜的文本數據,就需要定義一套自己的格式規範,這樣方便於之後將數據從文件中重新解析出來。
1.將數據存儲到文件中
Context類的openFileOutput ()方法,可以用於將數據存儲到指定的文件中。openFileOutput ()方法返回的是一個FileOutputStream對象,得到了這個對象之後就可以使用Java流的方式將數據寫入到文件中了。
第一個參數是文件名,不包含路徑,默認存儲到/data/data/<package name>/files/目錄下。
第二個參數是文件的操作模式。
MODE_PRIVATE是默認的操作模式,表示當指定同樣文件名的時候,所寫入的內容將會覆蓋原文件中的內容。
MODE_APPEND則表示如果該文件已存在就往文件裏面追加內容,不存在就創建新文件。
例子:
public void save() {
String data = "Data to save";
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out = openFileOutput("data", Context.MODE_PRIVATE);
writer = new BufferedWriter(new OutputStreamWriter(out));
writer.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.從文件中讀取數據
Context類的openFileInput()方法,只接受一個文件名參數,系統會自動到/data/data/<package name>/files/目錄下去加載這個文件,並返回一個FileInputStream對象。
例子:
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data");
reader = new BufferedReader(new InputStreamReader(in));
String line = "";
while ((line = reader.readLine()) != null) {
content.append(line);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
細節:
edit.setSelection(inputText.length()); //將輸入光標移動到文本的末尾位置以便於繼續輸入。
TextUtils.isEmpty()方法可以一次性判斷傳入的字符串等於null或者等於空字符串,返回true。
二、SharedPreferences存儲
使用鍵值對來存儲數據,支持不同的數據類型存儲。使用XML格式來對數據進行管理。
1.將數據存儲到SharedPreferences中
首先要獲取到SharedPreferences對象。
(1)Context類中的getSharedPreferences()方法
第一個參數指定文件名稱,若不存在則創建,,SharedPreferences文件都是存放在/data/data/<package name>/shared_prefs/目錄下的。第二個指定操作模式。MODE_PRIVATE默認,表示只有當前的應用程序纔可以對這個SharedPreferences文件進行讀寫。MODE_MULTI_PROCESS用於會有多個進程中對同一個SharedPreferences文件進行讀寫的情況。
(2)Activity類中的getPreferences()方法
只接受模式參數。自動將當前活動的類名作爲SharedPreferences的文件名。
(3)PreferenceManager類中的getDefaultSharedPreferences()方法
靜態方法,接受context參數,並自動使用當前應用程序的包名作爲前綴來命名SharedPreferences文件。
得到SharedPreferences對象後,就可以向SharedPreferences文件中存儲數據了。
(1)調用SharedPreferences對象的edit()方法獲取一個SharedPreferences.Editor對象
(2)向SharedPreferences.Editor對象中添加數據。例如putString()等。
(3)調用commit()將數據提交。
例子:
SharedPreferences.Editor editor = getSharedPreferences("data",
MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.commit();
2.從SharedPreferences中讀取數據
每種類型SharedPreferences.Editor都有對應的get方法,第一個參數是鍵,第二個是默認值,表示當鍵找不到值時以什麼默認值返回。
例子:
SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
String name = pref.getString("name", "");
int age = pref.getInt("age", 0);
boolean married = pref.getBoolean("married", false);
記住密碼的例子(修改強制下線項目的代碼):
boolean isRemember = pref.getBoolean("remember_password", false);
if (isRemember) {
// 將賬號和密碼都設置到文本框中
String account = pref.getString("account", "");
String password = pref.getString("password", "");
accountEdit.setText(account);
passwordEdit.setText(password);
rememberPass.setChecked(true);
}
login的點擊事件中:
editor = pref.edit();
if (rememberPass.isChecked()) { // 檢查複選框是否被選中
editor.putBoolean("remember_password", true);
editor.putString("account", account);
editor.putString("password", password);
} else {
editor.clear();
}
editor.commit();
三、SQLite數據庫存儲
SQLite是一款輕量級的關係型數據庫,它的運算速度非常快,佔用資源很少。SQLite不僅支持標準的SQL語法,還遵循了數據庫的ACID事務。integer表示整型,real表示浮點型,text表示文本類型,blob表示二進制類型。
1.創建數據庫
藉助SQLiteOpenHelper抽象幫助類就可以非常簡單地對數據庫進行創建和升級。
必須創建一個幫助類繼承SQLiteOpenHelper類,並重寫onCreate()和onUpgrade()方法。構造方法第一個參數是context,第二個是數據庫名,第三個允許在查詢的時候返回一個自定義的cursor,一般是null,第四個是當前數據庫的版本號,用於對數據庫進行升級操作。
構建出SQLiteOpenHelper實例後再調用getReadableDatabase()或getWritableDatabase()方法就能夠創建數據庫了,數據庫文件會存放在/data/data/<package name>/databases/目錄下。此時,重寫的onCreate()方法也會得到執行,所以通常會在這裏去處理一些創建表的邏輯。
注:getReadableDatabase()和getWritableDatabase()。這兩個方法都可以創建或打開一個現有的數據庫(如果數據庫已存在則直接打開,否則創建一個新的數據庫),並返回一個可對數據庫進行讀寫操作的對象。不同的是,當數據庫不可寫入的時候(如磁盤空間已滿)getReadableDatabase()方法返回的對象將以只讀的方式去打開數據庫,而getWritableDatabase()方法則將出現異常。
例子:
public class MyDatabaseHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table book ("
+ "id integer primary key autoincrement, "
+ "author text, "
+ "price real, "
+ "pages integer, "
+ "name text)";
private Context mContext;
public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) {
super(context, name, factory, version);
mContext = context;
}
@Override
public void onCreate(SQLiteDatabase db) {
db.execSQL(CREATE_BOOK);
Toast.makeText(mContext, "Create succeeded", Toast.LENGTH_SHORT).show();
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
MainActivity中:private MyDatabaseHelper dbHelper;
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
點擊事件中:
dbHelper.getWritableDatabase();
2.升級數據庫
例子:若要新增表Category。
public static final String CREATE_CATEGORY = "create table Category ("
+ "id integer primary key autoincrement, "
+ "category_name text, "
+ "category_code integer)";
在onCreate()方法中增加:
db.execSQL(CREATE_CATEGORY);
在onUpgrade()方法中增加:
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category");
onCreate(db);
MainActivity中修改:
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
3.添加數據
調用SQLiteOpenHelper的getReadableDatabase()或getWritableDatabase()方法是可以用於創建和升級數據庫的,不僅如此,這兩個方法還都會返回一個SQLiteDatabase對象,藉助這個對象就可以對數據進行CRUD操作了。
SQLiteDatabase中提供了一個insert()方法,這個方法就是專門用於添加數據的。它接收三個參數,第一個參數是表名,第二個參數用於在未指定添加數據的情況下給某些可爲空的列自動賦值NULL,一般直接傳入null即可。第三個參數是一個ContentValues對象,它提供了一系列的put()方法重載,用於向ContentValues中添加數據,只需要將表中的每個列名以及相應的待添加數據傳入即可。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
// 開始組裝第一條數據
values.put("name", "The Da Vinci Code");
values.put("author", "Dan Brown");
values.put("pages", 454);
values.put("price", 16.96);
db.insert("Book", null, values); // 插入第一條數據
values.clear();
// 開始組裝第二條數據
values.put("name", "The Lost Symbol");
values.put("author", "Dan Brown");
values.put("pages", 510);
values.put("price", 19.95);
db.insert("Book", null, values); // 插入第二條數據
4.更新數據
update()方法用於對數據進行更新,這個方法接收四個參數,第一個參數是表名,第二個參數是ContentValues對象,要把更新數據在這裏組裝進去。第三個參數對應的是SQL語句的where部分,表示去更新所有name等於?的行,而?是一個佔位符,可以通過第四個參數提供的一個字符串數組爲第三個參數中的每個佔位符指定相應的內容。不指定第三、四個參數的話默認就是更新所有行。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" });
5.刪除數據
delete()方法專門用於刪除數據,這個方法接收三個參數,第一個參數是表名,第二、第三個參數用於去約束刪除某一行或某幾行的數據,不指定的話默認就是刪除所有行。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.delete("Book", "pages > ?", new String[] { "500" });
6.查詢數據
query()方法第一個參數是表名,第二個參數用於指定去查詢哪幾列,如果不指定則默認查詢所有列。第三、第四個參數用於去約束查詢某一行或某幾行的數據,不指定則默認是查詢所有行的數據。第五個參數用於指定需要去group by的列,不指定則表示不對查詢結果進行group by操作。第六個參數用於對group by之後的數據進行進一步的過濾,不指定則表示不進行過濾。第七個參數用於指定查詢結果的排序方式,不指定則表示使用默認的排序方式。
query()方法參數 |
對應SQL部分 |
描述 |
table |
from table_name |
指定查詢的表名 |
columns |
select column1, column2 |
指定查詢的列名 |
selection |
where column = value |
指定where的約束條件 |
selectionArgs |
- |
爲where中的佔位符提供具體的值 |
groupBy |
group by column |
指定需要group by的列 |
having |
having column = value |
對group by後的結果進一步約束 |
orderBy |
order by column1, column2 |
指定查詢結果的排序方式 |
多數情況下只需要傳入少數幾個參數就可以完成查詢操作了。調用query()方法後會返回一個Cursor對象,查詢到的所有數據都將從這個對象中取出。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 查詢Book表中所有的數據
Cursor cursor = db.query("Book", null, null, null, null, null, null);
if (cursor.moveToFirst()) {
do {
// 遍歷Cursor對象,取出數據並打印
String name = cursor.getString(cursor. getColumnIndex("name"));
String author = cursor.getString(cursor. getColumnIndex("author"));
int pages = cursor.getInt(cursor.getColumnIndex ("pages"));
double price = cursor.getDouble(cursor. getColumnIndex("price"));
} while (cursor.moveToNext());
}
cursor.close();
7.用SQL語句操作數據庫
添加數據的方法如下:
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Da Vinci Code", "Dan Brown", "454", "16.96" });
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] { "The Lost Symbol", "Dan Brown", "510", "19.95" });
更新數據的方法如下:
db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99", "The Da Vinci Code" });
刪除數據的方法如下:
db.execSQL("delete from Book where pages > ?", new String[] { "500" });
查詢數據的方法如下:
db.rawQuery("select * from Book", null);
四、SQLite數據庫實踐
1.使用事務
SQLite數據庫是支持事務的,事務的特性可以保證讓某一系列的操作要麼全部完成,要麼一個都不會完成。
例子:
SQLiteDatabase db = dbHelper.getWritableDatabase();
db.beginTransaction(); // 開啓事務
try {
db.delete("Book", null, null);
ContentValues values = new ContentValues();
values.put("name", "Game of Thrones");
values.put("author", "George Martin");
values.put("pages", 720);
values.put("price", 20.85);
db.insert("Book", null, values);
db.setTransactionSuccessful(); // 事務已經執行成功
} catch (Exception e) {
e.printStackTrace();
} finally {
db.endTransaction(); // 結束事務
}
}
});
2.升級數據庫最佳寫法
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
switch (oldVersion) {
case 1:
db.execSQL(CREATE_CATEGORY);
case 2:
db.execSQL("alter table Book add column category_id integer");
default:
}
將每一版本的更新放在switch語句中。
switch中每一個case的最後都是沒有使用break的,爲了保證在跨版本升級的時候,每一次的數據庫修改都能被全部執行到。比如用戶當前是從第二版程序升級到第三版程序的,那麼case 2中的邏輯就會執行。而如果用戶是直接從第一版程序升級到第三版程序的,那麼case 1和case 2中的邏輯都會執行。使用這種方式來維護數據庫的升級,不管版本怎樣更新,都可以保證數據庫的表結構是最新的,而且表中的數據也完全不會丟失了。