Android 數據存儲

.1. **Android系統中的三種存儲方式 **

  • 文件存儲
  • SharedPreference存儲
  • SQLite存儲

.2. 文件存儲

文件存儲又可分為內部存儲(Internal storage)和外部存儲(External storage).

1.內部存儲

  • 總是可用的,程序默認將文件保存在這里
  • 當程序被卸載時,保存在這里的文件是默認全部被移除的

2.外部存儲

  • 保存的文件可以隨時讀取,并且所有程序都可以獲得這個文件的訪問權
  • 當程序被卸載時,系統會移除這些文件,但是如果你在getExternalFileDir() 方法獲得目錄下保存文件的話,它將不會被移除

3.兩種方法的比較

  • 如果不想要文件被用戶或者其他的app訪問,那么內部存儲是一個不錯的選擇
  • 如果是保存音樂,圖片或者視屏類的文件,我們通常不希望應用程序被卸載時文件也被移除,所以最好選用外部存儲的方式

4.使用內部存儲 存儲和讀取文件

Context類提供了一個openFileOutput()方法,用于將數據存儲到指定文件中,這個方法接受兩個參數,第一個參數是文件名,第二個是文件的操作模式,有MODE_PRIVATE和MODE_APPEND兩種方式,MODE_PRIVATE是系統默認的操作方式,當有相同文件名的文件時,這種方式會覆蓋原文件,MODE_APPEND表示當文件存在時,追加內容,不存在則創建文件,這個方法返回一個FileOutputStream對象。

Context 類還提供了一個openFileInput()方法,用于從文件中讀取數據,這個方法只接收一個參數,即要讀取的文件名,返回一個FileInputStream對象,得到這個對象后,可以通過Java流的方式獲得數據。

public void save(View view) {

        FileOutputStream out  = null;
        BufferedWriter writer = null;
        String data = editText.getText().toString();
        try{
            out = openFileOutput("data",MODE_PRIVATE);
            writer = new BufferedWriter(new OutputStreamWriter(out));
            writer.write(data);
        }catch (IOException e){
            e.printStackTrace();
        } finally {
            if (writer != null){
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
 public void load(View view) {

        FileInputStream in = null;
        BufferedReader reader = null;
        StringBuilder builder = new StringBuilder();

        try {
            in = openFileInput("data");
            reader = new BufferedReader(new InputStreamReader(in));
            String line = "";
            while((line = reader.readLine())!=null){
                builder.append(line);
            }
        }catch (IOException e){

        } finally {
            if (reader !=null){
                try {
                    reader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        editText.setText(builder.toString());
    }

Tips:

為了提升保存效率以及防止引起IOException異常,可以通過getFreeSpace()或者getTotalSpace()查看當前是否有足夠的內存去保存文件。

這種方式保存文件,文件默認保存在 /data/data/<package name>/files/目錄下,可以借助DDMS下的File Explorer找到該文件。

5.使用外部存儲保存文件

通過getExternalStorageState()方法查詢外部設備是否可用,當返回值為MEDIA_MOUNTED時,才可以讀取和寫入文件。

.3.SharedPreferences存儲

  • getSharedPreferences(String fileName, int mode)
    第一個參數為文件名稱,第二個參數為操作模式,有MODE_PRIVATE 和MODE_MULTI_PROCESS兩種。MODE_PRIVATE 仍然是系統默認的選擇,和傳入0效果相同,表示只有當前應用才可以對這個SharedPreferences文件進行讀取。MODE_MULTI_PROCESS一般是用于有多個進程對同一個SharedPreferences進行讀寫的情況。
    這種方法定義的文件,你可以通過文件名在app任何一個Context中訪問。

  • getPreferences(int mode)
    這種方式只接收一個參數,也即操作模式,使用這個方式將默認當前活動的類名為SharePreferences文件的名稱。這種方式定義的文件默認屬于這個Activity,不需要提供文件名稱。

  • getDefaultSharedPreferences(Context context)
    這個方法是PreferenceManager類中的一個靜態方法,接受一個Context參數,并自動使用當前應用程序的包名作為前綴命名SharedPreferences文件。

獲取到SharedPreferences對象后,就可以開始向SharedPreferences文件存儲數據了,一般可分為三步:

  1. 調用SharedPreferences的edit()方法,獲取一個SharedPreferences.Editor對象。
  2. 向SharedPreferences.Editor對象中添加數據。
  3. 調用commit()方法提交數據。
public void save(View view){
        SharedPreferences sharedPreferences = getSharedPreferences("data",MODE_PRIVATE);
        SharedPreferences.Editor editor = sharedPreferences.edit();
        
        editor.putString("name","value");
        editor.putInt("age",20);
        editor.putBoolean("alive",true);
        
        editor.commit();
    }
}


public void load(View view){
        SharedPreferences reader = getSharedPreferences("data",MODE_PRIVATE);
        
        String name = reader.getString("name", ""); //第二個參數為默認值
        int age = reader.getInt("age",0);
        Boolean alive = reader.getBoolean("alive",false);
    }

.4.SQLite數據庫存儲

為了方便的管理數據庫,Android系統專門提供了一個抽象幫助類SQLiteOpenHelper,借助這個類可以對數據庫進行創建和升級。SQLiteOpenHelper是抽象類,這意味著我們不能對它使用new方法,因此我們必須自定義一個類去繼承他,并實現內部的抽象方法。

SQLiteOpenHelper內部包含兩個抽象方法onCreate()和onUpgrade()方法,顧名思義,一個用來創建數據庫,一個用來升級數據庫。

SQLiteOpenHelper還有兩個重要的實例方法 :

  1. getReadableDatabase() : 創建或者打開一個現有的數據庫,如果數據庫不可寫入,將以只讀方式打開。
  2. getWritableDatabase() : 創建或者打開一個現有的數據庫,如果數據庫不可寫入,將會出現異常。

使用SQLite數據庫存儲文件通常需要三步 :

  1. 自定義類繼承SQLiteHelper并實現其內部抽象方法。
  2. 利用getWritableDatabase() 或者 getReadableDatabase() 獲得操作對象。
  3. 進行C(Create) R(Retrieve) U(Update) D(Delete)操作。

具體實現如下所示:

根據Google開發者文檔的建議,我們最好定義一個合約類去管理我們所有的建表語句,并將它們設置為全局變量,以便Activity統一訪問。

public final class TableManager {

    public TableManager(){}

    public static abstract  class PERSON_TABLE{
        public static final String ID = "id";
        public static final String PERSON_NAME = "name";
        public static final String PERSON_AGE = "age";
        public static final String PERSON_SEX = "sex";
        public static final String TABLE_NAME = "person";
        public static final String DATABASE_NAME = "db_person";

        public static final String CREATE_TABLE = "create table "+TABLE_NAME +"("
                + ID + " integer primary key autoincrement, "
                +PERSON_NAME + " text, "
                +PERSON_AGE + " integer, "
                +PERSON_SEX + " text)";

    }
}

新建MyDatabaseHelper繼承自SQLiteOpenHelper:

public class MyDatabaseHelper extends SQLiteOpenHelper{
    
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super(context, name, factory, version);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
            
        db.execSQL(TableManager.PERSON_TABLE.CREATE_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }
}

之后就可以在Activity中創建數據庫 :

public class MainActivity extends AppCompatActivity {
    
    private MyDatabaseHelper helper;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        helper = new MyDatabaseHelper(this,TableManager.PERSON_TABLE.DATABASE_NAME,null,1);
        Button button = (Button) findViewById(R.id.create);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                helper.getWritableDatabase();
            }
        });
        
    }
  }

這樣就已經創建好數據庫了,在File Explorer中的databases目錄下可以查找到,但是.db文件是無法通過File Explorer查看到的,因此可以通過adb shell 來查看。

接下來介紹數據庫的基本操作 :

  • 添加數據

    使用SQLiteDatabase的insert()方法可以往數據庫中添加數據,這個方法接收三個參數。第一個參數是要插入的表的名字,第二個參數用于在未指定添加數據的情況下給某些可為空的列自動賦值null,一般直接傳入null即可,第三個參數為ContentValues對象。

public void addData(View view) {

            SQLiteDatabase database = helper.getWritableDatabase();

            ContentValues values = new ContentValues();
            
            values.put(TableManager.PERSON_TABLE.PERSON_NAME,"author");
            values.put(TableManager.PERSON_TABLE.PERSON_AGE,20);
            values.put(TableManager.PERSON_TABLE.PERSON_SEX,"男");
            
            database.insert(TableManager.PERSON_TABLE.TABLE_NAME,null,values);
 }
  • 更新數據

    使用SQLiteDatabase中的update()方法可以對數據進行更新,這個方法接收四個參數。第一個參數還是表名,第二個參數為ContentValues對象,把要更新的信息裝入即可,第三四個參數用于約束更新某一行或者某幾行的數據,不指定的話就是默認更新所有行。

 public void updateData(View view){
            
         SQLiteDatabase database = helper.getWritableDatabase();
            
         ContentValues values = new ContentValues();
            
         values.put(TableManager.PERSON_TABLE.PERSON_AGE,100);
            
         database.update(TableManager.PERSON_TABLE.TABLE_NAME,
         values,
         TableManager.PERSON_TABLE.PERSON_NAME + " = ? ", 
         new String[]{"author"});
}
  • 刪除數據

    SQLiteDataBase提供了delete()方法,用于對數據的刪除,這個方法和update()方法類似。這個方法接受三個參數,第一個參數是表名,第二三參數用于約束刪除某一行和幾行的數據,不指定的話就是默認刪除所有行。

public void deleteData(View view){
            
            SQLiteDatabase database = helper.getWritableDatabase();
            
            database.delete(TableManager.PERSON_TABLE.TABLE_NAME,
                    TableManager.PERSON_TABLE.PERSON_NAME +" = ?",
                    new String[]{"author"});
}
  • 查詢數據

SQLiteDatabase提供了query()方法去查詢數據,不過query()方法接受的參數比較多,最短的重載方法也含有七個參數。

  1. table_name,指定查詢的表名
  2. select column,指定查詢的列名
  3. column selection,指定where的約束條件
  4. selectionArgs,為where 中的占位符提供具體的值
  5. groupBy,指定需要group by的列
  6. having, 對group by后的結果進行進一步的約束
  7. orderBy,指定查詢結果的排序方式
 public void queryData(View view){
        
        SQLiteDatabase database = helper.getWritableDatabase();

        Cursor cursor = database.query(TableManager.PERSON_TABLE.TABLE_NAME,null,null,null,null,null,null);
        if (cursor.moveToFirst()){
            do {
                String name = cursor.getString(cursor.getColumnIndex(TableManager.PERSON_TABLE.PERSON_NAME));
                String sex = cursor.getString(cursor.getColumnIndex(TableManager.PERSON_TABLE.PERSON_SEX));
                int age = cursor.getInt(cursor.getColumnIndex(TableManager.PERSON_TABLE.PERSON_AGE));
                
            }while (cursor.moveToNext());
        }
        cursor.close();
    }
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容

  • 之前閑著無聊,研究了下 Android 存儲方面的知識,順便翻譯了下官方文檔(雖然有已經被翻譯過...)。這里就算...
    koguma閱讀 1,000評論 0 3
  • 一、使用SharedPreferences存儲數據 默認存儲路徑:/data/data/ /shared_pref...
    zoky_閱讀 904評論 0 1
  • 譯自android.com 安卓為你是實現持久化數據提供了幾種選擇。具體的方案選擇取決于你的特定需要,比如存儲的數...
    gdut6049閱讀 640評論 1 5
  • 前面兩篇文章Android數據存儲(一)和Android數據存儲(二)分別使用文件存儲、SharedPrefere...
    前端develop閱讀 6,319評論 0 12
  • 那個人擺攤 賣上帝 標了價 還要贈送條香腸 在這大熱天。
    留子堯閱讀 252評論 3 5