SharedPreference、文件存儲 (FIleInputStream/FileOutputStream).SQLite

對於的數據進行保存方式,在Android中常用的有四種保存方式,這裏Himi先給大家統一先簡單的介紹下:

1.  SharedPreference

此保存方式試用於簡單數據的保存,文如其名屬於配置性質的保存,不適合數據比較大的保存方式;

2. 文件存儲 (FIleInputStream/FileOutputStream)

此保存方式比較適合遊戲的保存和使用,可以保存較大的數據,因爲相對於SQLite來說更容易讓童鞋們接受,此方式不僅能把數據存儲在系統中也能將數據保存到SDcard中;

3.SQLite 

此保存方式比較適合遊戲的保存和使用,可以保存較大的數據,並且可以將自己的數據存儲到文件系統或者數據庫當中,也可以將自己的數據存儲到SQLite數據庫當中,也能將數據保存到SDcard中;

4.ContentProvider (不推薦用於遊戲保存)

此保存方式不推薦用於遊戲保存,因爲此方式不僅能存儲較大數據,還支持多個程序之間就的數據進行交換!!! 但是由於遊戲中基本就不可能去訪問外部應用的數據,所以對於此方式我不予講解, 有興趣的可以去自行百度 google 學習;

以上簡單的對幾種常用的保存方式進行的概述,那麼,下面會詳細的去分析每個的優缺點以及每種保存的實現和需要注意的地方!

下面我首先向大家介紹第一種保存方式:

  保存方式之:  《SharedPreference》

優點: 簡單、方便、適合簡單數據的快速保存

缺點:1.存數的文件只能在同一包內使用,不能在不同包之間使用!

2.默認將數據存放在系統路徑下 /data/data/com.himi/  ,沒有找到放SD卡上的方法。

總結:其實本保存方式如同它的名字一樣是個配置保存,雖然方便,但只適合存儲比較簡單的數據!

main.xml  :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <TextView android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:text="保存數據練習!"
        android:textSize="20sp" android:textColor="#ff0000" android:id="@+id/tv_title" />
    <TextView android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:text="請輸入帳號" />
    <EditText android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:id="@+id/editText_Login"
        android:text=""></EditText>
    <TextView android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:text="請輸入密碼" />
    <EditText android:layout_width="fill_parent"
        android:layout_height="wrap_content" android:id="@+id/editText_Password"
        android:text=""></EditText>
    <Button android:id="@+id/button_save" android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="保存"></Button>
    <Button android:id="@+id/button_load" android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:text="取出數據"
        android:visibility="invisible"></Button>
</LinearLayout>

先把xml文件放上來的原因是因爲我在此篇中介紹的 SharedPreference 和 文件存儲 (FIleInputStream/FileOutputStream),都共用此xml,很簡單,兩個textview 兩個 editview 以及兩個button,這裏就不多說了;

下面是SharedPreference 的代碼實現和詳細講解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/**
 * @author Himi
 * @保存方式:SharedPreference
 * @注意:SharedPreference 可以跨程序包使用,多謝二樓童鞋提醒!
 * @操作模式: Context.MODE_PRIVATE:新內容覆蓋原內容
 *            Context.MODE_APPEND:新內容追加到原內容後
 *            Context.MODE_WORLD_READABLE:允許其他應用程序讀取
 *            Context.MODE_WORLD_WRITEABLE:允許其他應用程序寫入,會覆蓋原數據。
 */
public class MainActivity extends Activity implements OnClickListener {
    private EditText et_login, et_password;
    private Button btn_save;
    private TextView tv_title;
    private SharedPreferences sp;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.main);
        btn_save = (Button) findViewById(R.id.button_save);
        btn_save.setOnClickListener(this);
        et_login = (EditText) findViewById(R.id.editText_Login);
        et_password = (EditText) findViewById(R.id.editText_Password);
        tv_title = (TextView) findViewById(R.id.tv_title);
        // 這裏我們先調用 getSharedPreferences()來實例化一個SharedPreferences,
        // 第二個參數是指:操作模式(上面對各種操作模式已有解釋)
        sp = getSharedPreferences("Setting_himi", MODE_PRIVATE);
        /*
         * 下面代碼是我們要在程序剛啓動的時候我們來讀取之前的數據,
         * 當然我們還沒有保存任何數據所以肯定找不到!!
         * 如果找不到也沒關係會默認返回一個參數值,看下面的方法含義便知!
         */
        sp.getString("login", "");
        // getString()類似哈希表,一個key 一個volue ,
        // 這個方法如果找不到對應的第一個參數(key),那麼將以第二個參數作爲此key的返回值
        et_login.setText(sp.getString("login", ""));
        et_password.setText(sp.getString("password", ""));
    }
    @Override
    public void onClick(View v) {
        if (v == btn_save) {
            if (et_login.getText().toString().equals(""))
                tv_title.setText("請輸入帳號!");
            else if (et_password.getText().toString().equals(""))
                tv_title.setText("請輸入密碼!");
            else {
                sp.edit()
                  .putString("login", et_login.getText().toString())
                  .putString("password", et_password.getText().toString())
                  .commit();
                // 從sp.edit()開始進入編輯狀態,直到commit()提交!
                tv_title.setText("保存成功!可重新打開此程序,測試是否已經保存數據!" +
                    "/n(或者在'File Explorer'窗口下-data-data-com.himi路徑下" +
                    "是否存在" +"了'Setting_himi.xml')");
            }
        }
    }
}

代碼中的註釋的很清楚了,比較簡單,不多說了。

             保存方式之:  《文件存儲 OutputStream/InputStream》

優點: 1.適合遊戲存儲,能存儲較大數據;

2.不僅能存儲到系統中,也能存儲到SD卡中!

總結:如果童鞋們對SQL不太熟習的話那麼選擇此種方式最爲合適的啦、嘿嘿

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
 * @author Himi
 * @保存方式:Stream 數據流方式
 * @注意1:默認情況下,使用openFileOutput 方法創建的文件只能被其調用的應用使用,
 *         其他應用無法讀取這個文件,如果需要在不同的應用中共享數據;
 *
 * @注意2:因爲android  os內部閃存有限,所以適合保存較少的數據,當然我們也有解決的方法,
 *         就是把數據保存在SD開中,這樣就可以了,後面我也會向大家講解   !
 *
 * @提醒1 調用FileOutputStream 時指定的文件不存在,Android 會自動創建它。
 *        另外,在默認情況下,寫入的時候會覆蓋原 文件內容,如果想把新寫入的內
 *        容附加到原文件內容後,則可以指定其mode爲Context.MODE_APPEND。
 *
 * @提醒2 啓動程序就初始化的時候一定要注意處理!代碼中有註釋!一定要仔細看!
 *
 * @提醒3 這裏我給大家講兩種方式,一種是原生態file流來寫入/讀入,
 *        另外一種是用Data流包裝file流進行寫入/讀入 其實用data流來包裝進行操作;
 *        原因是:包裝後支持了更多的寫入/讀入操作,比如:file流寫入不支持
 *        writeUTF(String str); 但是用Data包裝後就會支持。
 *
 * @操作模式: Context.MODE_PRIVATE:新內容覆蓋原內容
 *            Context.MODE_APPEND:新內容追加到原內容後
 *            Context.MODE_WORLD_READABLE:允許其他應用程序讀取
 *            Context.MODE_WORLD_WRITEABLE:允許其他應用程序寫入,會覆蓋原數據。
 */
public class MainActivity extends Activity implements OnClickListener {
    private EditText et_login, et_password;
    private Button btn_save;
    private TextView tv_title;
    private FileOutputStream fos;
    private FileInputStream fis;
    private DataOutputStream dos;
    private DataInputStream dis;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        String temp = null;
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        this.requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.main);
        btn_save = (Button) findViewById(R.id.button_save);
        btn_save.setOnClickListener(this);
        et_login = (EditText) findViewById(R.id.editText_Login);
        et_password = (EditText) findViewById(R.id.editText_Password);
        tv_title = (TextView) findViewById(R.id.tv_title);
        try {
            // openFileInput 不像 sharedPreference 中
            // getSharedPreferences的方法那樣找不到會返回默認值,
            // 這裏找不到數據文件就會報異常,所以finally裏關閉流尤爲重要!!!
            if (this.openFileInput("save.himi") != null) {
                // --------------單純用file來讀入的方式-----------------
                // fis = this.openFileInput("save.himi");
                // ByteArrayOutputStream byteArray = new
                // ByteArrayOutputStream();
                // byte[] buffer = new byte[1024];
                // int len = 0;
                // while ((len = fis.read(buffer)) > 0) {
                // byteArray.write(buffer, 0, len);
                // }
                // temp = byteArray.toString();
                // -------------- 用data流包裝後的讀入的方式------------
                fis = this.openFileInput("save.himi");//備註1
                dis = new DataInputStream(fis);
                et_login.setText(dis.readUTF());
                et_password.setText(dis.readUTF());
                // 這裏也是在剛啓動程序的時候去讀入存儲的數據
                // 讀的時候要注意順序; 例如我們寫入數據的時候
                //先寫的字符串類型,我們也要先讀取字符串類型,一一對應!
            }
        } catch (FileNotFoundException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            // 在finally中關閉流!因爲如果找不到數據就會異常我們也能對其進行關閉操作 ;
            try {
                if (this.openFileInput("save.himi") != null) {
                    // 這裏也要判斷,因爲找不到的情況下,兩種流也不會實例化。
                    // 既然沒有實例化,還去調用close關閉它,肯定"空指針"異常!!!
                    fis.close();
                }
            } catch (FileNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    @Override
    public void onClick(View v) {
        if (Environment.getExternalStorageState() != null) {
            // 這個方法在試探終端是否有sdcard!
            Log.v("Himi", "有SD卡");
        }
        if (v == btn_save) {
            if (et_login.getText().toString().equals(""))
                tv_title.setText("請輸入帳號!");
            else if (et_password.getText().toString().equals(""))
                tv_title.setText("請輸入密碼!");
            else {
                try {
                    // ------單純用file來寫入的方式--------------
                    //fos = new FileOutputStream(f);
                    // fos.write(et_login.getText().toString().getBytes());
                    // fos.write(et_password.getText().toString().getBytes());
                    // ------data包裝後來寫入的方式--------------
                    fos = this.openFileOutput("save.himi", MODE_PRIVATE);//備註2
                    dos = new DataOutputStream(fos);
                    dos.writeUTF(et_login.getText().toString());
                    dos.writeUTF(et_password.getText().toString());
                    tv_title.setText("保存成功!可重新打開此程序,測試是" +
                            "否已經保存數據!/n(或者在'File Explorer'" +
                            "窗口下-data-data-com.himi-files路徑下" +
                            "是否存在了'save.himi')");
                } catch (FileNotFoundException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } finally {
                    // 在finally中關閉流 這樣即使try中有異常我們也能對其進行關閉操作 ;
                    try {
                        dos.close();
                        fos.close();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

以上代碼中實現了兩種流形式來完成寫入和讀入,這裏我們爲什麼要使用Data流來包裝,其實不光是獲得更多的操作方式,最主要的是方便快捷,你比如用file來讀入的時候,明顯的複雜了一些不說,它還一次性把所有數據都取出來了,不便於對數據的處理!

 

強調的有幾點:

1: 在一開始對數據的訪問再次提醒童鞋們,這個跟sharedPreference的獲取方式不一樣,sharedPreference 的獲取方式可以得到一個默認的值,但是你用咱們獲取的是個文件 而且直接就去open這個文件,一旦不存在必定異常,所以這一塊的異常處理,以及finally的處理一定要處理得當。

2.其實在一開始用data包裝的時候發現寫入的字符串在讀入的時候發現字符亂碼了,查了api才發現,api規定當寫入字符串的時候必須寫入UTF-8格式的編碼,但是後來不知道怎麼了就沒事了。 – -、所以這裏如果童鞋們遇到此問題,我給出大家一個解決方法,就是在寫入的時候我們不要去DataOutputStream 來包裝而是用,OutputStreamWriter ,因爲在構造的可以設定編碼!

OutputStreamWriter osw = new OutputStreamWriter(fis,”UTF-8″);

String  content = EncodingUtils.getString(buffer, “UTF-8″);  這個也能把字符數組轉碼制!

這樣寫入的就肯定是UTF-8編碼的字符啦、

下面介紹如何把我們的數據通過 OutputStream/InputStream 存入SD卡中!

其實將我們的數據放入SD卡中,無疑就需要對代碼進行兩處的修改:

注意:一定要有SD卡!對於如何創建SD卡在前一篇文章中已經說了兩種方式,不會的童鞋可以去看下;

第一:檢查是否裝有SD卡;

第二: 修改讀入的地方(備註1)

fis = this.openFileInput(“save.himi”); //這裏沒有路徑,路徑是默認的 data-data-com.himi-files下

替換成我們的SD卡的路徑就可以了:

File path = new File(“/sdcard/himi/save.himi”);//這裏新建一個File目錄路徑

fis = new FileInputStream(path);傳入路徑

第三 : 修改寫入的地方(備註2)

fos = this.openFileOutput(“save.himi”, MODE_PRIVATE);這裏也是默認路徑,需要對其修改,

注意:這裏修改了,那麼在finally中的判定大家也要對應的適當修改;

注意:如果是系統路徑,當沒有此文件的時候,android 會默認創建一個!但是我們放入SD卡的時候要自己創建目錄路徑和文件!

1
2
3
4
5
6
7
8
9
10
11
12
if (Environment.getExternalStorageState() != null) {// 這個方法在試探終端是否有sdcard!
                Log.v("Himi", "有SD卡");
                File path = new File("/sdcard/himi");// 創建目錄
                File f = new File("/sdcard/himi/save.himi");// 創建文件
                if (!path.exists()) {// 目錄不存在返回false
                    path.mkdirs();// 創建一個目錄
                }
                if (!f.exists()) {// 文件不存在返回false
                    f.createNewFile();// 創建一個文件
                }
                fos = new FileOutputStream(f);// 將數據存入sd卡中
            }

 

第四: 因爲我們要在SD卡中進行寫入的操作,所以要在配置文件中聲明權限!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?xml version="1.0" encoding="utf-8"?>
      package="com.himi"
      android:versionCode="1"
      android:versionName="1.0">
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
    <uses-sdk android:minSdkVersion="4" />
</manifest>

這一句就是啦~

<uses-permission android:name=”android.permission.WRITE_EXTERNAL_STORAGE”/>

爲了讓大家看到所放的位置,所以把整個xml放出來供參考;

那麼當創建路徑和文件的時候,我們對其檢查SD卡中是否已經存在exists()方法 ,如果已經存在就不去創建,這樣避免下次再次寫入數據的時候又新建了文件和路徑、

其實我們在可以在啓動程序的時候判斷如果沒有此文件,我們可以直接緊接着創建一個文件,這些都屬於優化上的了,我主要是讓大家引入,學會,那麼其他的簡化啦,優化啦,其他方式去實現啦都留給各位同學自己了、

OK、今天就先介紹到這裏,後面會單獨剖析SQLite如何存入數據,以及對數據操作的! 希望大家繼續關注!

本篇效果圖:

 

本篇源碼: “Himi-SharedPreference與FIle兩種存儲源碼.rar”        下載地址:  http://vdisk.weibo.com/s/hq1vl

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章