Android09-數(shù)據(jù)存儲的三種方式

  • 持久化技術(shù)

指將用戶產(chǎn)生的數(shù)據(jù)漠另,存儲到手機中,即使手機關(guān)機數(shù)據(jù)也不會丟失番挺。Android有文件存儲唠帝、ShardPreference存儲、數(shù)據(jù)庫存儲玄柏、或者SD卡存儲這幾種方式來實現(xiàn)數(shù)據(jù)持久化襟衰。但相對來說前三種方式會相對比較簡單和安全。

1. 文件存儲

Android最基本的數(shù)據(jù)存儲方式粪摘,不對存儲內(nèi)容進行任何處理瀑晒,所有數(shù)據(jù)原封不動的保存到文件當中,比較適合用于存儲一些簡單的文本或二進制數(shù)據(jù)徘意。

1.1 將數(shù)據(jù)存儲到文件中

使用Context類提供的openFileOutput()方法可以將數(shù)據(jù)存儲到指定文件中瑰妄。默認存儲到/data/data/<packagename>/files/目錄下
openFileOutput()參數(shù)說明:

  1. 第一個:文件名,文件創(chuàng)建時使用的名稱映砖,無需包含路徑间坐。
  2. 第二個:文件的操作模式,MODE_PRIVATE: 默認操作模式邑退,當指定的文件名已存在的時候竹宋,覆蓋已存在的內(nèi)容。MODE_APPEND: 當指定文件已存在的時候就忘里邊追加內(nèi)容地技。
  3. 該方法返回一個FileOutputStream對象蜈七,得到該對象之后就可以對文件進行保存了。
  • 以下展示了將一段文本內(nèi)容保存到文件中

    public void save() {
        String data = "我是要保存的文本";
        FileOutputStream out = null;
        BufferdWriter writer = null;
        try {
            out = openFileOutput("data", Context.MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.writer(data);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (writer != null) {
                    writer.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    

1.2 從文件中讀取數(shù)據(jù)

使用Context類提供的OpenFileInput()方法莫矗,用于從文件中讀取數(shù)據(jù)飒硅。
這個方法的使用只需傳入要讀取的文件名砂缩,然后就會從/data/data/<package name>/files目錄下去加載這個文件,返回一個FileInputStream()對象。

  • 示例代碼如下:
public String load() {
    FileInputStream in = null;
    BufferedReader reader = null;
    StringBuilder content = new StringBuilder();
    try {
        in = openFieInput("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();
}

2.SharedPreferences存儲

SharedPreferences是使用鍵值對的方式來存儲數(shù)據(jù)的,如果存儲的數(shù)據(jù)是整型德迹,讀出來的數(shù)據(jù)也是整形,所以使用SharedPreferences來進行數(shù)據(jù)存儲要比文件方便很多双吆。

2.1 將數(shù)據(jù)存儲到SharedPreferences中

  1. 獲取SharedPreferences對象(Android有三種方式可以得到SharedPreferences對象)
  2. Activity類中的getPreferences()方法
  3. PreferenceManager類中的getDefaultSharedPreferences()方法
  4. Context類中的getSharedPreferences()方法
       pref = PreferenceManager.getDefaultSharedPreferences(this);
        pref = getPreferences(MODE_PRIVATE);
        pref = getSharedPreferences("", MODE_PRIVATE);
  1. 調(diào)用SharedPreferences對象的edit()方法。
  2. 向SharedPreferences.Editor對象中添加數(shù)據(jù)会前。
  3. 調(diào)用apply()方法將添加的數(shù)據(jù)提交好乐。
Button button = (Button) findViewById(R.id.save_data);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //獲得SharedPreferences對象
                SharedPreferences.Editor editor = getSharedPreferences("data", MODE_PRIVATE).edit();
                //寫如要保存的數(shù)據(jù)
                editor.putString("name", "Tom");
                editor.putInt("age", 28);
                editor.putBoolean("married", false);
                //調(diào)用apply()方法
                editor.apply();
            }
        });

2.2 從SharedPrefrences中讀取數(shù)據(jù)

步驟跟存數(shù)據(jù)類似
需要注意的是,讀取數(shù)據(jù)不需要調(diào)用edit()方法瓦宜。

Button button1 = (Button)findViewById(R.id.restore_data);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
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);
    }
});

3.SQLite數(shù)據(jù)庫存儲

使用SQLiteOpenHelper類可以非常簡單的對數(shù)據(jù)庫進行創(chuàng)建和升級蔚万。

3.1 SQLiteOpenHelper類介紹

  1. SQLiteOpenHelper是一個抽象類,如果要使用它临庇,需要創(chuàng)建一個幫助類去繼承它反璃,然后重寫onCreate()和onUpgrade()這兩個抽象方法。
  2. getReadableDatabase()和getWritableDatabase()是兩個非常重要的實例方法苔巨,可以創(chuàng)建或打開一個現(xiàn)有的數(shù)據(jù)庫(如果數(shù)據(jù)庫已存在直接打開版扩,否則創(chuàng)建一個新的數(shù)據(jù)庫)废离,并返回一個可對數(shù)據(jù)庫進行讀寫操作的對象侄泽。注意:當磁盤空間已滿時,使用getReadableDatabase()方法返回的為只讀數(shù)據(jù)庫蜻韭,而getWritableDatabase()則將出現(xiàn)異常

3.2創(chuàng)建數(shù)據(jù)庫

SQLiteOpenHelper中有兩個構(gòu)造方法可供重寫悼尾,一般使用參數(shù)少一點方法即可
第一個參數(shù):Context
第二個參數(shù):數(shù)據(jù)庫名
第三個參數(shù):允許我們在查詢數(shù)據(jù)的時候返回一個自定義的Cursor,一般傳null
第四個參數(shù):當前數(shù)據(jù)庫的版本號肖方」胛海可用于對數(shù)據(jù)庫進行操作

class MyDatabaseHelper extends SQLiteOpenHelper {
//定義SQL的建表語句
    public static final String CREATE_BOOK2 = "create table Book("
            + "id integer primary key autoincrement, "
            + "author text, "
            + "price real, "
            + "pages integer, "
            + "name text)";
    private Context mContext;
    //重寫構(gòu)造方法
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
        Toast.makeText(context, "創(chuàng)建", Toast.LENGTH_SHORT).show();
        mContext = context;
    }
    //重寫onCreate方法
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK2);
        Toast.makeText(mContext, "創(chuàng)建成功", Toast.LENGTH_SHORT).show();
    }
    //重寫onUpgrade()方法
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    }
}

3.3 升級數(shù)據(jù)庫

升級數(shù)據(jù)庫:在原有的數(shù)據(jù)庫基礎(chǔ)上添加新表。
主要通過SQLiteOpenHelper的構(gòu)造方法的第四個參數(shù)(當前版本號)來實現(xiàn)俯画。當出入的版本好為新版本號時會執(zhí)行onUpgrade方法析桥,在這個方法里邊實現(xiàn)升級數(shù)據(jù)庫的操作。

//重寫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");
    //調(diào)用onCreate()方法艰垂,創(chuàng)建要創(chuàng)建的表
    onCreate(db);
}

3.4 添加數(shù)據(jù)

SQLiteDatabase中提供了insert()方法泡仗,專門用于添加數(shù)據(jù),它接受三個參數(shù)
第一個參數(shù):表名
第二個參數(shù):用于在未指定添加數(shù)據(jù)的情況下給某些可為空的列自動賦值NULL猜憎,一般這個參數(shù)傳null即可
第三個參數(shù):一個ContentValues對象娩怎,它提供了一系列的put()方法重載,用于向ContentValues中添加數(shù)據(jù)胰柑。只需要將每個列名以及相應(yīng)的待添加數(shù)據(jù)傳入即可截亦。

//添加數(shù)據(jù)
Button addData = (Button) findViewById(R.id.add_data);
        addData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                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.69);
                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ù)
            }
        });

3.5 更新數(shù)據(jù)

SQLiteDatabase中提供了update()方法用于對數(shù)據(jù)進行更新爬泥,這個方法接受四個參數(shù)
第一個參數(shù):表名,和insert()方法一樣
第二個參數(shù):ContentValues對象崩瓤,把要更新的數(shù)據(jù)在這里組裝進去
第三個參數(shù)第四個參數(shù)袍啡;用于約束更新某一行或某幾行數(shù)據(jù),不指定的話默認更新所有行

//更新數(shù)據(jù)
        Button updateData = (Button) findViewById(R.id.update_data);
        updateData.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                SQLiteDatabase db = dbHelper.getWritableDatabase();
                ContentValues values = new ContentValues();
                values.put("price", 1.99);
                //將名字是The Da Vinci Code的書價格改為1.99
                db.update("Book", values, "name = ?",new String[] {"The da Vinci Code"});
            }
        });

3.6 刪除數(shù)據(jù)

SQLiteDatabase提供了delete()方法用于刪除數(shù)據(jù)谷遂,該方法接收三個參數(shù)
第一個參數(shù):表名葬馋,和其他方法一樣
第二個第三個參數(shù)是用來約束刪除某一行或某幾行的,跟update()方法的第三第四個參數(shù)類似肾扰。

//刪除數(shù)據(jù)
Button deleteData = (Button) findViewById(R.id.delete_data);
deleteData.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      //刪除Book表中page大于500的數(shù)據(jù)
      db.delete("Book", "page > ?0", new String[] {"500"});
  }
});

3.7 查詢數(shù)據(jù)

使用SQLiteDatabase提供querry()方法除數(shù)據(jù)逆行查詢畴嘶。該方法較為復(fù)雜,最短的一個方法重載也要傳入七個參數(shù)集晚,并返回一個Cursor對象

  1. 第一個參數(shù):表名
  2. 第二個參數(shù):用于指定查詢哪幾列窗悯,如果不指定默認查詢所有列
  3. 第三第四個參數(shù):用于約束查詢某一行或者某幾行的數(shù)據(jù),不指定則默認查詢所有行
  4. 第五個參數(shù):用于指定需要去group by的列偷拔,不指定則表示不進行g(shù)roup by操作蒋院。
  5. 第六個參數(shù):用于對group by之后的數(shù)據(jù)進行進一步的過濾,不指定則表示不進行過濾莲绰。
  6. 第七個參數(shù):用于指定查詢結(jié)果的排序方式欺旧,不指定則表示使用默認的排序方式。
  7. 注:querry()方法會返回一個Cursor對象蛤签,查詢的所有數(shù)據(jù)都是從這個對象取出
2017-04-11_15-12-06.png
//查詢數(shù)據(jù)
Button querryData = (Button) findViewById(R.id.querry_data);
querryData.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
          SQLiteDatabase db = dbHelper.getWritableDatabase();
          //查詢Book表中的所有數(shù)據(jù)
          Cursor cursor = db.query("Book", null, null, null, null, null, null);
          //調(diào)用moveToFirst()方法將數(shù)據(jù)指針移動到第一行位置
          if (cursor.moveToFirst()) {
              do {
                  //遍歷Cursor對象辞友,取出數(shù)據(jù)并打印
                  String name = cursor.getString(cursor.getColumnIndex("name"));   //getColumnIndex()方法可以獲得某一列在表中對應(yīng)的位置索引
                  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 page is + " + pages);
                  Log.d("MainActivity", "book price is + " + price);
              } while (cursor.moveToNext());
          }
          cursor.close();
      }
    });
}

3.8 使用SQL語句操作數(shù)據(jù)庫

Android還可以不借助SQLiteDatabase的情況下進行數(shù)據(jù)庫的增刪查改功能,具體使用方法如下

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

3.9 SQLite使用事務(wù)

SQLite的特性是支持事務(wù)的震肮,事務(wù)的特性可以保證讓某一系列的操作要么全部一起完成称龙,要么一個都不完成。
比如有這么一個需求戳晌,需要將數(shù)據(jù)庫中的舊書替換成新書鲫尊,這時候就需要把舊書刪除,然后把新書添加進去沦偎,但是要保證舊書刪除之后新書必須添加成功疫向,這個時候就可以使用事務(wù)來實現(xiàn)了。

//替換數(shù)據(jù)
Button replaceData = (Button) findViewById(R.id.replace_data);
replaceData.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
      SQLiteDatabase db = dbHelper.getWritableDatabase();
      //開啟事務(wù)
      db.beginTransaction();
      try {
          db.delete("Book", null, null);
          if (true) {
              //在這里手動跑出一個異常豪嚎,演示當添加不成功時搔驼,事務(wù)失敗
              throw new NullPointerException();
          }
          ContentValues values = new ContentValues();
          values.put("name", "Game of Thrones");
          values.put("author", "me");
          values.put("pages", 750);
          values.put("price", 20.22);
          db.insert("Book", null, values);
          db.setTransactionSuccessful();//事務(wù)執(zhí)行成功
      } catch (Exception e) {
          e.printStackTrace();
      } finally {
          //結(jié)束事務(wù)
          db.endTransaction();
      }        
  }
});

3.10 升級數(shù)據(jù)庫的最佳寫法

在3.3的時候已經(jīng)有演示了升級數(shù)據(jù)庫的寫法,但是那種方法是通過把原有的數(shù)據(jù)庫刪除疙渣,然后重新創(chuàng)建一個的方式來進行升級的匙奴,這樣的話原有數(shù)據(jù)庫的信息也會隨著升級而丟失,所以有時候這種方法是不可取的妄荔。
如果想要進行數(shù)據(jù)庫升級泼菌,并保留原有的數(shù)據(jù)谍肤,需要為每個數(shù)據(jù)庫版本號賦予它各自改變的內(nèi)容,然后在onUpgrade()方法中去執(zhí)行更新操作哗伯。

//重寫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");
//        //調(diào)用onCreate()方法荒揣,創(chuàng)建要創(chuàng)建的表
//        onCreate(db);
   switch (oldVersion) {
       //對舊版本進行判斷,執(zhí)行新版本要更新的內(nèi)容焊刹。這里需要注意系任,case后面不加break,以保證用戶直接從第一版升級至第三版時case1和case2都會執(zhí)行虐块。
       case 1:
           db.execSQL(CREATE_GATEGORY);
       case 2:
           db.execSQL("alter table Book add colum category_id integer");
       default:
   }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俩滥,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子贺奠,更是在濱河造成了極大的恐慌霜旧,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件儡率,死亡現(xiàn)場離奇詭異挂据,居然都是意外死亡,警方通過查閱死者的電腦和手機儿普,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門崎逃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人眉孩,你說我怎么就攤上這事个绍。” “怎么了勺像?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵障贸,是天一觀的道長错森。 經(jīng)常有香客問我吟宦,道長,這世上最難降的妖魔是什么涩维? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任殃姓,我火速辦了婚禮,結(jié)果婚禮上瓦阐,老公的妹妹穿的比我還像新娘蜗侈。我一直安慰自己,他們只是感情好睡蟋,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布踏幻。 她就那樣靜靜地躺著,像睡著了一般戳杀。 火紅的嫁衣襯著肌膚如雪该面。 梳的紋絲不亂的頭發(fā)上夭苗,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天,我揣著相機與錄音隔缀,去河邊找鬼题造。 笑死,一個胖子當著我的面吹牛猾瘸,可吹牛的內(nèi)容都是我干的界赔。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼牵触,長吁一口氣:“原來是場噩夢啊……” “哼淮悼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起揽思,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤敛惊,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绰更,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞧挤,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年儡湾,在試婚紗的時候發(fā)現(xiàn)自己被綠了特恬。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡徐钠,死狀恐怖癌刽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情尝丐,我是刑警寧澤显拜,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站爹袁,受9級特大地震影響远荠,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜失息,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一譬淳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盹兢,春花似錦邻梆、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春剂娄,著一層夾襖步出監(jiān)牢的瞬間窘问,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工宜咒, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留惠赫,地道東北人。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓故黑,卻偏偏與公主長得像儿咱,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子场晶,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

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