《第一行代碼》學(xué)習(xí)筆記 第 6 章

第 6 章 數(shù)據(jù)存儲(chǔ)全方案,詳解持久化技術(shù)

一:文件存儲(chǔ)

  • 將數(shù)據(jù)存儲(chǔ)到文件中(使用 Java 流的方式將數(shù)據(jù)寫入到文件中
    1. 這里通過(guò)openFileOutput()方法能夠得到一個(gè) FileOutputStream 對(duì)象
    2. 然后再借助它構(gòu)建出一個(gè) OutputStreamWriter 對(duì)象
    3. 接著再使用 OutputStreamWriter 構(gòu)建出一個(gè) BufferedWriter 對(duì)象
    4. 這樣你就可以通過(guò) BufferedWriter.write 來(lái)將文本內(nèi)容寫入到文件中了
    5. 關(guān)閉文件流
public void save() {
    String data = "Data to save";
    
    FileOutputStream out = null;
    BufferedWriter writer = null;
    
    try {
        out = openFileOutput("data", Context.MODE_PRIVATE);  // 1 FileOutputStream
        writer = new BufferedWriter(new OutputStreamWriter(out));  // 2 BufferedWriter葵腹、3 BufferedWriter
        writer.write(data);  // 4 
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        try {
            if (writer != null) {
                writer.close(); // 5
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • 從文件中讀取數(shù)據(jù)
    1. 首先通過(guò) openFileInput()方法獲取到了一個(gè) FileInputStream 對(duì)象忙菠,
    2. 然后借助它又構(gòu)建出了一個(gè) InputStreamReader 對(duì)象嗡午,
    3. 接著再使用 InputStreamReader 構(gòu)建出一個(gè) BufferedReader 對(duì)象坏瞄,
    4. 這樣我們就可以通過(guò) BufferedReader 進(jìn)行一行行地讀取仁堪,把文件中所有的文本內(nèi)容全部讀取出來(lái)并存放在一個(gè)StringBuilder對(duì)象中媳纬,
    5. 關(guān)閉文件流
    6. 最后將讀取到的內(nèi)容返回就可以了
public String load() {
    FileInputStream in = null;
    BufferedReader reader = null;
    StringBuilder content = new StringBuilder();
    
    try {
        in = openFileInput("data"); //1 FileInputStream
        reader = new BufferedReader(new InputStreamReader(in)); // 2、3
        String line = "";
        while ((line = reader.readLine()) != null) {    //4
            content.append(line);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (reader != null) {
            try {
                reader.close(); //  5
            } 
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    return content.toString();
}

二:SharedPreferences 存儲(chǔ)

  • 將數(shù)據(jù)存儲(chǔ)到 SharedPreferences 中

    1. 首先需要獲取到SharedPreferences對(duì)象(有三種方法可以獲任苹恰)
      • Context類中的 getSharedPreferences()方法
      • Activity類中的 getPreferences()方法(這個(gè)方法時(shí)會(huì)自動(dòng)將當(dāng)前活動(dòng)的類名作為 SharedPreferences的文件名)
      • PreferenceManager類中的 getDefaultSharedPreferences()方法(這是一個(gè)靜態(tài)方法帖烘,它接收一個(gè)Context參數(shù),并自動(dòng)使用當(dāng)前應(yīng)用程序的包名作為前綴來(lái)命名 SharedPreferences文件)
    2. 調(diào)用 SharedPreferences對(duì)象的 edit()方法來(lái)獲取一個(gè) SharedPreferences.Editor 對(duì)象橄杨。
    3. 向 SharedPreferences.Editor 對(duì)象中添加數(shù)據(jù)秘症,比如添加一個(gè)布爾型數(shù)據(jù)就使用putBoolean 方法
    4. 調(diào)用 commit() 或apply() 方法將添加的數(shù)據(jù)提交,從而完成數(shù)據(jù)存儲(chǔ)操作式矫。
  • 從 SharedPreferences 中讀取數(shù)據(jù)

    1. 首先通過(guò) getSharedPreferences()方法得到 SharedPreferences 對(duì)象乡摹,
    2. 然后分別調(diào)用它的 getString()、getInt()和getBoolean()方法去獲取前面所存儲(chǔ)的數(shù)據(jù)采转,如果沒(méi)有找到相應(yīng)的值就會(huì)使用方法中傳入的默認(rèn)值來(lái)代替

三:SQLite 數(shù)據(jù)庫(kù)存儲(chǔ)

  • 創(chuàng)建數(shù)據(jù)庫(kù)
    getReadableDatabase() 和getWritableDatabase()聪廉。這兩個(gè)方法都可以創(chuàng)建或打開一個(gè)現(xiàn)有的數(shù)據(jù)庫(kù)(如果數(shù)據(jù)庫(kù)已存在則直接打開,否則創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)) 氏义,并返回一個(gè)可對(duì)數(shù)據(jù)庫(kù)進(jìn)行讀寫操作的對(duì)象锄列。不同的是,當(dāng)數(shù)據(jù)庫(kù)不可寫入的時(shí)候(如磁盤空間已滿)getReadableDatabase()方法返回的對(duì)象將以只讀的方式去打開數(shù)據(jù)庫(kù)惯悠,而 getWritableDatabase()方法則將出現(xiàn)異常。
    1. 新建自定義的數(shù)據(jù)庫(kù)類 MyDatabaseHelper 繼承 SQLiteOpenHelper
public class MyDatabaseHelper extends SQLiteOpenHelper {
    private Context mContext;
    
    //使用了 primary key將 id 列設(shè)為主鍵竣况,并用 autoincrement關(guān)鍵字表示 id列是自增長(zhǎng)的克婶。
    public static final String CREATE_BOOK = "create table book ("
    + "id integer primary key autoincrement, "
    + "author text, "
    + "price real, "
    + "pages integer, "
    + "name text)";

    public MyDatabaseHelper(Context context, String name, CursorFactory factory, int version) {
        super(context, name, factory, version);
        mContext = context;
    }

    //  創(chuàng)建數(shù)據(jù)庫(kù)
    @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) {
    }
}
2. 在需要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù)時(shí)構(gòu)建一個(gè) MyDatabaseHelper對(duì)象,并且通過(guò)構(gòu)造函數(shù)的參數(shù)將數(shù)據(jù)庫(kù)名指定為 BookStore.db丹泉,版本號(hào)指定為1
dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 1);
3. 然后在 Create database 按鈕的點(diǎn)擊事件里調(diào)用了getWritableDatabase()方法情萤。
dbHelper.getWritableDatabase();

(這樣當(dāng)?shù)谝淮吸c(diǎn)擊 Create database按鈕時(shí),就會(huì)檢測(cè)到當(dāng)前程序中并沒(méi)有BookStore.db這個(gè)數(shù)據(jù)庫(kù)摹恨,于是會(huì)創(chuàng)建該數(shù)據(jù)庫(kù)并調(diào)用MyDatabaseHelper中的 onCreate()方法筋岛,這樣 Book表也就得到了創(chuàng)建,再次點(diǎn)擊 Create database按鈕時(shí)晒哄,會(huì)發(fā)現(xiàn)此時(shí)已經(jīng)存在BookStore.db數(shù)據(jù)庫(kù)了睁宰,因此不會(huì)再創(chuàng)建
:adb是 Android SDK中自帶的一個(gè)調(diào)試工具肪获,使用這個(gè)工具可以直接對(duì)連接在電腦上的手機(jī)或模擬器進(jìn)行調(diào)試操作。它存放在sdk的platform-tools 目錄下柒傻,如果想要在命令行中使用這個(gè)工具孝赫,就需要先把它的路徑配置到環(huán)境變量里。(輸入adbshell红符,就會(huì)進(jìn)入到設(shè)備的控制臺(tái)進(jìn)行數(shù)據(jù)庫(kù)操作)

  • 升級(jí)數(shù)據(jù)庫(kù)

    1. 數(shù)據(jù)庫(kù)類中定義新的表格
    public static final String CREATE_CATEGORY = "create table Category ("
        + "id integer primary key autoincrement, "
        + "category_name text, "
        + "category_code integer)";
    
    1. 在數(shù)據(jù)庫(kù)類的onCreate方法中添加新建數(shù)據(jù)表命令
        db.execSQL(CREATE_CATEGORY);
    
    1. 在數(shù)據(jù)庫(kù)類的onUpgrade方法中添加命令 (刪除已存在的表格青柄,再新建)
        db.execSQL("drop table if exists Book");
        db.execSQL("drop table if exists Category");
        onCreate(db);
    
    1. 在代碼中需要更新數(shù)據(jù)庫(kù)的地方調(diào)用以下代碼(記得升級(jí)版本號(hào))
        dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 2);
        dbHelper.getWritableDatabase();
    
  • 添加數(shù)據(jù)(getReadableDatabase()或getWritableDatabase()方法都會(huì)返回一個(gè)SQLiteDatabase對(duì)象,借助這個(gè)對(duì)象就可以對(duì)數(shù)據(jù)進(jìn)行添加预侯、刪除致开、更新等操作

    1. 我們先獲取到了 SQLiteDatabase 對(duì)象,
    2. 然后使用ContentValues來(lái)對(duì)要添加的數(shù)據(jù)進(jìn)行組裝
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    ContentValues values = new ContentValues();
    //  開始組裝第一條數(shù)據(jù)
    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); //  插入第一條數(shù)據(jù)
    values.clear();
    //  開始組裝第二條數(shù)據(jù)
    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); // 
  • 更新數(shù)據(jù)
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    
    ContentValues values = new ContentValues();
    values.put("price", 10.99);
    
    //第三個(gè)參數(shù)對(duì)應(yīng)的是 SQL 語(yǔ)句的where部分萎馅,表示去更新所有name等于?的行双戳,而?是一個(gè)占位符,可以通過(guò)第四個(gè)參數(shù)提供的一個(gè)字符串?dāng)?shù)組為第三個(gè)參數(shù)中的每個(gè)占位符指定相應(yīng)的內(nèi)容
    db.update("Book", values, "name = ?", new String[] { "The Da Vinci Code" }); //將名字是 The Da Vinci Code的這本書的價(jià)格改成 10.99
  • 刪除數(shù)據(jù)
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.delete("Book", "pages > ?", new String[] { "500" });
  • 查詢數(shù)據(jù)(Cursor對(duì)象
    SQLiteDatabase db = dbHelper.getWritableDatabase();
   
    Cursor cursor = db.query("Book", null, null, null, null, null, null);
    //查詢完之后就得到了一個(gè) Cursor對(duì)象校坑,接著我們調(diào)用它的moveToFirst()方法將數(shù)據(jù)的指針移動(dòng)到第一行的位置拣技,然后進(jìn)入了一個(gè)循環(huán)當(dāng)中,去遍歷查詢到的每一行數(shù)據(jù)
    if (cursor.moveToFirst()) {
        do {
            //  遍歷Cursor 對(duì)象耍目,取出數(shù)據(jù)并打印
            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 操作數(shù)據(jù)庫(kù)(直接使用sql語(yǔ)句實(shí)現(xiàn)以上操作)

    1. 添加數(shù)據(jù)的方法如下:
        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" });
    
    1. 更新數(shù)據(jù)的方法如下:
        db.execSQL("update Book set price = ? where name = ?", new String[] { "10.99","The Da Vinci Code" });
    
    1. 刪除數(shù)據(jù)的方法如下:
        db.execSQL("delete from Book where pages > ?", new String[] { "500" });
    
    1. 查詢數(shù)據(jù)的方法如下:
        db.rawQuery("select * from Book", null);
    

  • SQLite 數(shù)據(jù)庫(kù)的最佳實(shí)踐
    • 使用事務(wù)(事務(wù)的特性可以保證讓某一系列的操作要么全部完成膏斤,要么一個(gè)都不會(huì)完成。)
    SQLiteDatabase db = dbHelper.getWritableDatabase();
    db.beginTransaction(); //  開啟事務(wù)
    
    try {
    
        //刪除數(shù)據(jù)
        db.delete("Book", null, null);  
        
        if (true) {
            //  在這里手動(dòng)拋出一個(gè)異常邪驮,讓事務(wù)失敗莫辨,此時(shí)異常被catch捕獲,無(wú)法執(zhí)行之后的添加數(shù)據(jù)操作毅访,由于事務(wù)的存在沮榜,導(dǎo)致之前的刪除操作也會(huì)被還原,所以無(wú)法刪除喻粹。
            throw new NullPointerException();
        }
        
        //添加數(shù)據(jù)
        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(); //  事務(wù)已經(jīng)執(zhí)行成功
        
    } catch (Exception e) {
        e.printStackTrace();
    } finally {//是否成功都會(huì)執(zhí)行
        db.endTransaction(); //  結(jié)束事務(wù)
    }
  • 升級(jí)數(shù)據(jù)庫(kù)的最佳寫法(為每一個(gè)版本賦予它各自改變的內(nèi)容蟆融,然后在onUpgrade()方法中對(duì)當(dāng)前數(shù)據(jù)庫(kù)的版本號(hào)進(jìn)行判斷,再執(zhí)行相應(yīng)的改變)

//需求1:向數(shù)據(jù)庫(kù)中再添加一張 Category表

  1. 在 onCreate()方法里我們新增了一條建表語(yǔ)句守呜,
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
        db.execSQL(CREATE_CATEGORY);    //新增了一條建表語(yǔ)句
    }
  1. 然后又在 onUpgrade()方法中添加了一個(gè)switch判斷型酥,如果用戶當(dāng)前數(shù)據(jù)庫(kù)的版本號(hào)是1,就只會(huì)創(chuàng)建一張Category表查乒。這樣當(dāng)用戶是直接安裝的第二版的程序時(shí)弥喉,就會(huì)將兩張表一起創(chuàng)建。而當(dāng)用戶是使用第二版的程序覆蓋安裝第一版的程序時(shí)玛迄,就會(huì)進(jìn)入到升級(jí)數(shù)據(jù)庫(kù)的操作中由境,此時(shí)由于 Book 表已經(jīng)
    存在了,因此只需要?jiǎng)?chuàng)建一張 Category表即可蓖议。
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    switch (oldVersion) {
        case 1:
            db.execSQL(CREATE_CATEGORY);
        default:
    }
}

//需求2:需要在 Book表中添加一個(gè) category_id 的字段

  1. 修改原先的建表語(yǔ)句虏杰,添加一個(gè) category_id 的字段
    public static final String CREATE_BOOK = "create table Book ("
    + "id integer primary key autoincrement, "
    + "author text, "
    + "price real, "
    + "pages integer, "
    + "name text, "
    + "category_id integer)";
  1. 修改onUpgrade方法
    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:
        }
    }

注:

  1. switch 中每一個(gè) case 的最后都是沒(méi)有使用break的讥蟆,為什么要這么做呢?這是為了保證在跨版本升級(jí)的時(shí)候嘹屯,每一次的數(shù)據(jù)庫(kù)修改都能被全部執(zhí)行到攻询。
  2. 使用這種方式來(lái)維護(hù)數(shù)據(jù)庫(kù)的升級(jí),不管版本怎樣更新州弟,都可以保證數(shù)據(jù)庫(kù)的表結(jié)構(gòu)是最新的钧栖,而且表中的數(shù)據(jù)也完全不會(huì)丟失了。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末婆翔,一起剝皮案震驚了整個(gè)濱河市拯杠,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌啃奴,老刑警劉巖潭陪,帶你破解...
    沈念sama閱讀 212,816評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異最蕾,居然都是意外死亡依溯,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門瘟则,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)黎炉,“玉大人,你說(shuō)我怎么就攤上這事醋拧】妒龋” “怎么了?”我有些...
    開封第一講書人閱讀 158,300評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵丹壕,是天一觀的道長(zhǎng)庆械。 經(jīng)常有香客問(wèn)我,道長(zhǎng)菌赖,這世上最難降的妖魔是什么缭乘? 我笑而不...
    開封第一講書人閱讀 56,780評(píng)論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮琉用,結(jié)果婚禮上忿峻,老公的妹妹穿的比我還像新娘。我一直安慰自己辕羽,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評(píng)論 6 385
  • 文/花漫 我一把揭開白布垄惧。 她就那樣靜靜地躺著刁愿,像睡著了一般。 火紅的嫁衣襯著肌膚如雪到逊。 梳的紋絲不亂的頭發(fā)上铣口,一...
    開封第一講書人閱讀 50,084評(píng)論 1 291
  • 那天滤钱,我揣著相機(jī)與錄音,去河邊找鬼脑题。 笑死件缸,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的叔遂。 我是一名探鬼主播他炊,決...
    沈念sama閱讀 39,151評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼已艰!你這毒婦竟也來(lái)了痊末?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,912評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤哩掺,失蹤者是張志新(化名)和其女友劉穎凿叠,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嚼吞,經(jīng)...
    沈念sama閱讀 44,355評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡盒件,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了舱禽。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炒刁。...
    茶點(diǎn)故事閱讀 38,809評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖呢蔫,靈堂內(nèi)的尸體忽然破棺而出切心,到底是詐尸還是另有隱情,我是刑警寧澤片吊,帶...
    沈念sama閱讀 34,504評(píng)論 4 334
  • 正文 年R本政府宣布绽昏,位于F島的核電站,受9級(jí)特大地震影響俏脊,放射性物質(zhì)發(fā)生泄漏全谤。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評(píng)論 3 317
  • 文/蒙蒙 一爷贫、第九天 我趴在偏房一處隱蔽的房頂上張望认然。 院中可真熱鬧,春花似錦漫萄、人聲如沸卷员。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毕骡。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間未巫,已是汗流浹背窿撬。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叙凡,地道東北人劈伴。 一個(gè)月前我還...
    沈念sama閱讀 46,628評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像握爷,于是被迫代替她去往敵國(guó)和親跛璧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容