本篇主要涉及Android中的數據持久化技術。
一、 文件存儲
不對存儲內容進行任何格式化處理,原封不動的保存到文件中。適合存儲一些簡單的文本數據或二進制數據。
(一) 存儲
1. 獲取FileOutputStream對象:使用Context類中的 openFileOutput()
方法,接收兩個參數:(文件名, 操作模式)。
- 文件默認存儲到
/data/data/<package name>/files/
目錄下; - 兩種操作模式可選:MODE_PRIVATE(默認)和MODE_APPEND 。
2. 構建出OutputStreamWriter對象,再借此構建出BufferedWriter對象,調用它的 write
方法將數據寫入文件。
public void save(String inputText) {
FileOutputStream out = null;
BufferedWriter writer = null;
try {
out =openFileOutput("data", Context.MODE_PRIVATE); // 獲取FileOutputStream對象
writer = new BufferedWriter(new OutputStreamWriter(out); // 構建BufferedWriter對象
writer.write(inputText);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (writer != null) {
writer.close()
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
(二) 讀取
1. 獲取FileInputStream對象:使用Context類中的 openFileInput(文件名)
方法。
2. 構建出InputStreamReader,再借此構建出BufferedReader對象,讀取到StringBuilder中,最後返回。
public String load() {
FileInputStream in = null;
BufferedReader reader = null;
StringBuilder content = new StringBuilder();
try {
in = openFileInput("data"); // 獲取FileInputStream對象
reader = new BufferedReader(new InputStreamReader(in)); // 構建BufferedReader對象
String line = "";
while ((line = reader.readLine()) != null) {
content.append(line); // 逐行讀取,若內容不爲空添加到StringBuilder中
}
} catch (IOException e) {
e.printStackTrace;
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return content.toString();
}
二、 SharedPreference存儲
採用鍵值對的方式存儲數據,支持多種不同的數據類型存儲,使用XML格式來管理數據的。
(一) 存儲
1. 獲取SharedPreferences對象,有三種方法:
- Context類中的
getSharePregerences(文件名, 操作模式)
方法。
- SharedPregerences文件都存放在
/data/data/<package name>/shared_prefs/
目錄下 - 操作模式只有MODE_PRIVATE可選,和傳入0效果一樣
- SharedPregerences文件都存放在
- Activity類中的
getPreferences(操作模式)
方法。
- 只接受一個操作模式參數,自動將當前活動類名作爲文件名
- PreferenceManager類中的
getDeafaultSharedPreferences(context)
方法。
- 是一個靜態方法,接收一個context參數,使用當前應用程序包名作爲前綴來命名文件
2. 向SharedPreferences文件中存儲數據
(1) 調用SharedPreferences對象的 edit()
方法來獲取一個SharedPreferences.Editor對象;
(2) 向Editor對象中添加數據,根據數據類型調用 putString()
、putBoolean()
等方法;
(3) 調用 apply()
方法提交數據。
SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
editor.putString("name", "Tom");
editor.putInt("age", 28);
editor.putBoolean("married", false);
editor.apply();
(二) 讀取
使用 getSring()
、getBoolean()
等方法,接收兩個參數:(鍵, 默認值)。
SharedPreferences pref = getSharedPreferences("data", MODE_PRIVATE);
String name = pref.getString("name", "");
int age = pref.getInt("age", 0);
boolean married = pref.getBoolean("married", false);
Log.d("MainActivity", "name is " + name);
Log.d("MainActivity", "age is " + age);
Log.d("MainActivity", "married is " + married);
三、 SQLite數據庫存儲
適合存儲大量複雜的關係型數據。
(一) 創建數據庫
- 藉助抽象類SQLiteOpenHelper,繼承此類後重寫
onCreate()
、onUpgrade()
方法來創建和升級數據庫。 - SQLiteOpenHelper的構造方法接收4個參數:(Context, 數據庫名, null, 版本號)。第三個參數允許在查詢數據時返回一個自定義Cursor,一般傳入null。
- 兩個實例方法
getReadableDatabase()
、getWritableDatabase()
用於創建或打開現有數據庫,並返回一個可進行讀寫操作的對象。 - 數據庫文件存放在
/data/data/<package name>/databases/
目錄下。
public class MyDatabaseOpenHelper extends SQLiteOpenHelper {
public static final String CREATE_BOOK = "create table BOOK ("
+ "id integer primary key antoincrement, " // 設置爲主鍵,自增長
+ "author text" // text表示文本類型
+ "price real, " // real表示浮點型
+ "pages integer, " // integer表示整型
+ "name text)";
private Context mContext;
public MyDataBaseHelper(Context context, String name, SQLiteDatabase.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) {
}
}
public class MainActivity extends AppCompatActivity {
private MyDatabaseHelper dbHelper;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
Button createDatabase = (Button) findViewById(R.id.create_database);
createDatabase.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dbHelper.getWritableDatabase();
}
});
}
}
(二) 升級數據庫
1. 將建表語句添加到MyDatabaseHelper中;
2. onUpgrade()
中執行DROP語句,再調用 onCreate()
方法重新創建;
3. 修改MainActivity中構造方法的版本號,使 onUpgrade()
能夠執行。
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("drop table if exists Book");
db.execSQL("drop table if exists Category"); // 若創建表時發現表已存在,需要刪除掉,否則會報錯
onCreate(db);
}
(三) 添加數據
使用SQLiteDatabase的 insert()
方法,接收三個參數:(要添加數據的表名, null, ContentValues對象)
- 第二個參數用於在未指定數據的情況下給某些爲空的列自動賦值NULL,一般用不到直接傳入null;
- 第三個參數的ContentValues對象提供一系列
put()
方法重載用於向其中添加數據,傳入 (列名, 待添加數據) 即可。
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();
// 開始組裝第二條數據
...
db.insert("Book", null, valuse); // 插入第二條數據
(四) 更新數據
使用 update()
方法,接收四個參數:(表名, ContentValues對象, 約束語句, 字符串數組)。
- 第二個參數中把要更新的數據組裝進來;
- 第三個參數用於約束更新某一行或某幾行的數據,對應SQL語句的where部分,包含佔位符,例如”name = ?”;
- 第四個參數用一個字符串數組指定佔位符的內容,如
new String[] {"The Da Vinci Code"}
。
SQLiteDatabase db = dbHelper.getWritaberDatabase();
ContentValues values = new ContentValues();
values.put("price", 10.99);
db.update("Book", values, "name = ?", new String[] {"The Da Vinci Code"}); // 更新書名爲"The Da Vinci Code"的數據
(五) 刪除數據
使用 delete()
方法,接收三個參數:(表名, 約束語句, 字符串數組)。
- 和insert類似,第二三個參數用於約束刪除某一行或某幾行數據。
db.delete("Book", "pages > ?", new String[] {"500"}); // 刪除頁數超過500的書
(六) 查詢數據
1. 使用 query()
方法,最短的方法重載接收七個參數;
2. 查詢完得到一個Cursor對象,調用 moveToFirst()
將數據指針移動到第一行位置(返回True表示移動成功數據不爲空);
3. 通過一個循環遍歷查詢到的每一行數據,循環中可以通過Cursor的 getColumnIndex()
方法獲取到某一列在表中對應的位置索引,將其傳入對應取值方法中就可以讀取到數據了;
4. 最後調用 colse()
方法關閉Cursor。
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 | 指定查詢結果的排序方式 |
// 查詢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"));
Log.d("MainActivity", "book name is " + name);
Log.d("MainActivity", "book author is " + author);
Log.d("MainActivity", "book pages is " + pages);
Log.d("MainActivity", "book price is " + price);
} while (cursor.moveToNext());
}
cursor.close();
(七) 使用SQL操作數據庫
- 添加數據
db.execSQL("insert into Book (name, author, pages, price) values(?, ?, ?, ?)",
new String[] {"The Da Vinci Code", "Dan Brown", "454", "16.96"});
- 更新數據
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);
四、 使用LitePal操作數據庫
LitePal是一款開源的Android數據庫框架,它採用了對象關係映射(ORM)的模式,並對常用數據庫功能進行了封裝。
(一) 配置LitePal
- 在app/build.grale文件中聲明開源庫的引用:
compile 'org.litepal.android:core:1.4.1'
- 配置litepal.xml。在app/src/main下創建一個assets目錄,在其中創建一個litepal.xml文件,編輯其中內容:
<?xml version = "1.0" encoding="utf-8"?>
<litepal>
<!-- 數據庫名 -->
<dbname value="BookStore" ></dbname>
<!-- 數據庫版本號 -->
<version value="1" ></version>
<!-- 指定所有映射模型 -->
<list>
</list>
</litepal>
- AndroidManifest中配置LitePalApplication。
<application
android:name="org.litepal.LitePalApplication"
...>
...
</application>
(二) 創建和升級數據庫
1.創建數據庫
(1) 根據要創建的表定義一個類,定義相應字段並生成對應的getter和setter方法;
(2) 將創建的類添加到映射模型列表中,使用<mapping>
標籤;
(3) 調用 Connector.getDatabase()
創建數據庫。
/** 根據Book表的內容定義Book類 */
public class Book{
private int id;
private String author;
private double price;
private int pages;
private String name;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
... //生成各個字段對應的getter和setter方法
}
<!-- 添加到映射模型列表中 -->
<list>
<mapping class="com.example.litepaltest.Book"></mapping>
</list>
@Override
public void onClick(View v) {
Connector.getDatabase(); // 創建數據庫
}
2. 升級數據庫
更改想要修改的任何內容,然後將版本號加1即可。升級會保留之前表中的所有數據。
(三) 添加數據
創建出模型實例(要進行CRUD操作模型類必須繼承字DataSupport類),調用set方法將所有要存儲的數據設置好,最後調用 save()
方法即可。
public class Book extends DataSupport {
...
}
Book book = new Book();
book.setName("The Da Vinci Code");
book.setAuthor("Dan Brown");
book.setPages(454);
book.setPrice(16.96);
book.setPress("UnKnow");
book.save():
(四) 更新數據
- new出模型實例,調用set方法設置要更新的數據,最後調用
updateAll()
方法執行更新操作(updateAll中可以指定約束條件)。 - 注意:要更新爲默認值,使用
setToDefault()
方法。
/** 將書名爲The Lost Symbol並且作者是Dan Brown的書價格更新爲14.95,出版社更新爲Anchor */
Bool book = new Book();
book.setPrice(14.95);
book.setPress("Anchor");
book.updateAll("name = ? and author = ?", "The Lost Symbol", "Dan Brown");
/** 將所有書頁更新爲0(默認值) */
Book book = new book;
book.setToDefault("pages");
book.updateAll();
(五) 刪除數據
- 調用已存儲對象(調用過save方法或通過LitePal提供的查詢API查出來的對象)的
delete()
方法。 - 調用
DataSupport.deleteAll()
方法,接收三個參數:(要刪除數據的表名, 約束條件, 佔位符內容)
/** 刪除Book表中價格低於15的書 */
DataSupport.deleteAll(Book.class, "price < ?", "15");
(六) 查詢數據
- 調用
findAll()
方法,返回值爲對應類型的List集合。
List<Book> books = DataSupport.finAll(Book.class);
for (Book book : books) {
Log.d("MainActivity", "book name is " + book.getName());
Log.d("MainActivity", "book author is " + book.getAuthor());
Log.d("MainActivity", "book pages is " + book.getPages());
Log.d("MainActivity", "book price is " + book.getPrice());
Log.d("MainActivity", "book press is " + book.getPress());
}
findFirst()
、findLast()
分別查詢第一條和最後一條數據.- 通過連綴查詢定製更多查詢功能:
方法 | 對應SQL部分 | 描述 |
---|---|---|
select() | select | 指定查詢哪幾列數據 |
where() | where | 指定查詢的約束條件 |
order() | order by | 指定結果的排序方式(desc降序,asc或不寫爲升序) |
limit() | limit | 指定查詢結果的數量 |
offset() | limit | 指定查詢結果的偏移量 |
/** 查詢Book表中第11~20條頁數大於400的name、author、pages這三列數據,並將結果按頁數升序排列 */
List<Book> books = DataSupport.select("name", "author", "pages")
.where("pages > ?", "400")
.order("pages") // 默認爲asc升序
.limit(10) // 只查前10條
.offset(10) // 偏移10個位置
.find(Book.class);
- 也支持使用原生SQL進行查詢:調用
DataSupport.findBySQL(SQL語句, 佔位符的值)
,返回一個Cursor對象。
Cursor c = DataSupport.findBySQL("select * from Book where pages > ? and price < ?", "400", "20");