Android開發基礎知識整理之數據存儲

本篇主要涉及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效果一樣
  • 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");
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章