Android开发7:简单的数据存储(使用SharedPreferences)和文件操作
前言
啦啦啦~大家好,又見面啦~
本篇博文講和大家一起完成一個需要注冊、登錄的備忘錄的,一起學習 SharedPreferences 的基本使用,學習 Android 中常見的文件操作方法,復習 Android 界面編程。
直接進入正題~
?
基礎(chǔ)知識
1.SharedPreferences 的使用
使用SharedPreferences儲存用戶名和密碼,SharedPreferences是直接處理xml文件,不需要做字符串分割,存儲效率會比使用內(nèi)部存儲,和外部存儲存儲用戶名和密碼高。
(1) SharedPreferences 的讀取
在 Android 中,用于獲取 SharedPreferences 的接?是 getSharedPreferences(String, int) 函數(shù)。 兩個參數(shù)的意義:
String: Desired preferences file. If a preferences file by this name does not exist, it will be created when you retrieve an editor.
int: Operating mode. Use 0 or MODE_PRIVATE for the default operation.
我們對 SharedPreferences 的讀取操作是通過 getSharedPreferences(String, int) 函數(shù)返回的 SharedPreferences 對象的方法來完成的。查閱文檔可以看到,SharedPreferences 支持如下幾種方法讀取之前存儲的數(shù)據(jù):
-
-
- abstract Map<String, ?> getAll()
- abstract boolean getBoolean(String key, boolean defValue)
- abstract float getFloat(String key, float defValue)
- abstract int getInt(String key, int defValue)
- abstract long getLong(String key, long defValue)
- abstract String getString(String key, String defValue)
- abstract Set<String> getStringSet(String key, Set<String> defValues)
-
所有方法都需要傳入一個 defValue 參數(shù),在給定的 key 不存在時,SharedPreferences 會直接返回 這個默認值。
(2)SharedPreferences 的寫入
所有對? SharedPreferences 的寫入操作,都必須通過? SharedPreferences.edit() 函數(shù)返回的 Editor對象來完成。
舉例:
Context context = getActivity(); SharedPreferences sharedPref = context.getSharedPreferences("MY_PREFERENCE", Context.MODE_PRIVATE); // Alternatively, if you need just one shared preference file for your activity, you can use the getPreferences() method: // SharedPreferences sharedPref =getActivity().getPreferences(Context.MODE_PRIVATE); SharedPreferences.Editor editor = sharedPref.edit(); editor.putInt("KEY_SCORE", newHighScore); editor.commit();使用SharedPreferences將用戶名和密碼保存在本地后,可以在\data\data\+包名+\shared_prefs目錄下找到一個info.xml文件
?
2.Android 中的文件操作
Android 中的存儲空間分為兩種:Internal Storage 和? External Storage.
(1)Internal Storage
默認情況下,保存在 Internal Storage 的文件只有應用程序可見,其他應用,以及用戶本?是無法訪問這些文件的。
向? Internal Storage 寫入文件的示例代碼如下:
try (FileOutputStream fileOutputStream = openFileOutput(FILE_NAME, MODE_PRIVATE)) {String str = "Hello, World!";fileOutputStream.write(str.getBytes());Log.i("TAG", "Successfully saved file."); } catch (IOException ex) {Log.e("TAG", "Fail to save file."); }若對應的文件不存在,openFileOutput(String, int) 函數(shù)會直接新建文件。注意傳入的文件名參數(shù)不能含有 path separators(即 '/'). 該函數(shù)返回一個 FileOutputStream 對象,可以調(diào)用?write() 方法寫入內(nèi)容。
相應地,文件的讀取可以使用 openFileInput(String) 來讀取文件。該函數(shù)返回一個 FileInput- Stream,調(diào)用? read() 方法讀取內(nèi)容。
try (FileInputStream fileInputStream =openFileInput(FILE_NAME)) {byte[] contents = new byte[fileInputStream.available()]; fileInputStream.read(contents); } catch (IOException ex) {Log.e("TAG", "Fail to read file."); }?
(2)External Storage
Android 支持使用? Java 的文件? API 來讀寫文件,但是關(guān)鍵的點在于要有一個合適的路徑。如果你要存儲一些公開的,體積較大的文件(如媒體文件),External Storage 就是一個比較合適的地方。如文檔中所說:
All Android devices have two file storage areas: “internal” and “external” storage. These names come from the early days of Android, when most devices offered built-in non-volatile memory (internal storage), plus a removable stor- age medium such as a micro SD card (external storage). Some devices divide? the permanent storage space into “internal” and “external” partitions, so even without a removable storage medium, there are always two storage spaces and the API behavior is the same whether the external storage is removable or not.
無論是否支持外置 SD 卡,所有的 Android 設(shè)備都會將存儲空間分為 internal 和 external? 兩部分。
要往? External Storage 寫入文件,需要在? AndroidManifest.xml 文件中聲明權(quán)限:
-
- <manifest ...> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> ... </manifest>
隨后調(diào)用 getExternalFilesDir(String type) 或 Environment.getExternalStoragePublicDirectory()來獲取? SD 卡路徑。兩者的區(qū)別在于:前者指向的目錄會在應用卸載時被刪除,而后者不會。
?
上面的兩個函數(shù)均返回一個 File 對象,代表一個目錄路徑,使用這個 File 對象,再結(jié)合文件名,即 可創(chuàng)建? FileInputStream 或? FileOutputStream 來進?文件讀寫。
舉例:
?
-
- void createExternalStoragePrivateFile() { // Create a path where we will place our private file on external // storage. File file = new File(getExternalFilesDir(null), "DemoFile.jpg"); try { // Very simple code to copy a picture from the application's // resource into the external file. Note that this code does // no error checking, and assumes the picture is small (does // not try to copy it in chunks). Note that if external storage is // not currently mounted this will silently fail. InputStream is =getResources().openRawResource(R.drawable.balloons); OutputStream os = new FileOutputStream(file); byte[] data = new byte[is.available()]; is.read(data); os.write(data); is.close(); os.close(); } catch (IOException e) { // Unable to create file, likely because external storage is // not currently mounted. Log.w("ExternalStorage", "Error writing " + file, e); }}
?
實驗內(nèi)容
? ? ? ? ??? ??? ??? ?
? ? ? ? ? ? ? ? ? ? ? ? ? ? 1 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 2 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?3 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 4
?
Figure 1: 首次進入, 呈現(xiàn)創(chuàng)建密碼界面
Figure 2: 若密碼不匹 配,彈出? Toast 提示
Figure 3: 若密碼為空, 彈出? Toast 提示
Figure 4: 退出后第二次進入呈現(xiàn)輸入密碼界面
?
? ? ? ? ?? ??? ??? ??
? ? ? ? ? ? ? ? ? ? ? ? ? ?5 6 ?7 ? 8
?
Figure 5: 若密碼不正 確,彈出? Toast 提示
Figure 6: 文件加載失 敗,彈出? Toast 提示
Figure 7: 成功導入文 件,彈出? Toast 提示
Figure 8: 成功保存文 件,彈出? Toast 提示?
?1.如? Figure 1??? Figure 8?所示,本次實驗演示應用包含兩個 Activity.
?2.首先是密碼輸入 Activity:
若應用首次啟動,則界面呈現(xiàn)出兩個輸入框,分別為新密碼輸入框和確認密碼輸入框。
輸入框下方有兩個按鈕:
– OK 按鈕點擊后:
*? 若? New Password 為空,則發(fā)出? Toast 提示。見 Figure 3。
*? 若 New Password 與 Confirm Password 不匹配,則發(fā)出 Toast 提示。見 Figure 2。
* 若兩密碼匹配,則保存此密碼,并進入文件編輯 Activity。
– CLEAR 按鈕點擊后:清除兩輸入框的內(nèi)容。
? 完成創(chuàng)建密碼后,退出應用再進入應用,則只呈現(xiàn)一個密碼輸入框。見 Figure 4。
– 點擊? OK 按鈕后,若輸入的密碼與之前的密碼不匹配,則彈出? Toast 提示。見 Figure 5.
– 點擊? CLEAR 按鈕后,清除密碼輸入框的內(nèi)容。
? 出于演示和學習的目的,本次實驗我們使用 SharedPreferences 來保存密碼。但實際應用中不會使用這種方式來存儲敏感信息,而是采用更安全的機制。
3.文件編輯 Activity:
? 界面底部有三個按鈕,?度一致,頂對齊,按鈕?平均勻分布。三個按鈕上方除 ActionBar 和 StatusBar 之外的全部空間由一個 ?EditText 占據(jù)(保留 ?margin)。EditText 內(nèi)的文字豎直方 向置頂,左對齊。
? 在編輯區(qū)域輸入任意內(nèi)容,點擊 SAVE 按鈕后能保存到指定文件(文件名隨意). 成功保存后,彈 出? Toast 提示。見 Figure 8。
? 點擊? CLEAR 按鈕,能清空編輯區(qū)域的內(nèi)容。
? 點擊? LOAD 按鈕,能夠從同一文件導入內(nèi)容,并顯示到編輯框中。若成功導入,則彈出? Toast。
提示。見?Figure 7。?若讀取文件過程中出現(xiàn)異常(如文件不存在),則彈出 ?Toast 提示。見Figure 6。
4. 特殊要求:進入文件編輯? Activity 后,若點擊返回按鈕,則直接返回 Home 界面,不再返回密碼輸入 Activity。
?
參考實現(xiàn)
1.如何使文件編輯? Activity 的? EditText 占據(jù)上方全部空間?
使用? LinearLayout 和? layout_weight 屬性。
?
if there are three text fields and two of them declare a weight of 1, while the other is given no weight, the third text field without weight will not grow and will only occupy the area required by its content. The other two will expand equally to fill the space remaining after all three fields are measured.
2.當 Activity 不可見時,如何將其從 activity stack 中除去(按返回鍵直接返回Home)?
AndroidManifest.xml 中設(shè)置? noHistory 屬性。
?
Whether or not the activity should be removed from the activity stack and finished (its finish() method called) when the user navigates away from it and?it’s no longer visible on screen —“true” if it should be finished, and “false”
if not. The default value is “false”.
3.如何根據(jù)需要隱藏/顯示特定的控件?
Set visibility: You can hide or show views using setVisibility(int).?
?
4.參考工程目錄結(jié)構(gòu)
?
實驗過程
本次實驗主要是實現(xiàn)一個備忘錄,實現(xiàn)數(shù)據(jù)存儲的功能。本次實驗主要涉及SharePreferences的使用(讀取和寫入)。
首先,寫好兩個界面的XML布局文件(這里注意使用LinearLayout和layout_weigh屬性,使得文件編輯Activity的EditText占據(jù)上方全部空間)。
接下來完成MainActivity.java類,在 Android 中,用于獲取 SharedPreferences 的接口是 getSharedPreferences(String, int) 函數(shù)。對 SharedPreferences 的讀取操作是通過 getSharedPreferences(String,int)函數(shù)返回的 SharedPreferences 對象的方法來完成的:
?
?
所有對 SharedPreferences 的寫入操作,都必須通過? SharedPreferences.edit()函數(shù)返回的Editor對象來完成:
?
在OK按鈕點擊事件中,設(shè)置若 New Password為空,則發(fā)出Toast 提示。若New Password 與 Confirm Password不匹配,則發(fā)出Toast 提示,若New Password與 Confirm Password 匹配,則跳轉(zhuǎn)到文件編輯界面:
?
獲取本地存儲的密碼,密碼不為空就隱藏其中一個密碼框(保證完成創(chuàng)建密碼后,退出應用再進入應用,則只呈現(xiàn)一個密碼輸入框):
然后在clear按鈕點擊事件中,通過設(shè)置tag來控制密碼輸入欄的清空事件(tag初始設(shè)為true,在創(chuàng)建密碼界面中點擊clear會清除兩個密碼輸入框中的所有內(nèi)容,在輸入密碼的登錄界面中點擊clear隱藏的密碼框中的內(nèi)容不會清除,通過與隱藏了的密碼框中的已存儲的密碼進行比較確認輸入的密碼是否正確):
?
通過在AndroidManifest.xml中設(shè)置noHistory屬性,使得當Activity不可見時,如何將其從activity stack中除去(按返回鍵直接返回Home):
?
在文件編輯的java類中,我們向Internal Storage 寫入文件。
默認情況下,保存在 Internal Storage 的文件只有應用程序可見,其他應用,以及用戶本身是無法 訪問這些文件的。
若對應的文件不存在,openFileOutput(String, int) 函數(shù)會直接新建文件。注意傳入的文件名參數(shù)不能含有 path separators(即 '/')。該函數(shù)返回一個 FileOutputStream 對象,可以調(diào)用 write() 方法寫入內(nèi)容(寫在save點擊事件中):
?
?
相應地,文件的讀取可以使用 openFileInput(String) 來讀取文件。該函數(shù)返回一個 FileInput- Stream,調(diào)用 read()方法讀取內(nèi)容(寫在load點擊事件中):
實現(xiàn)文件編輯中的文件存儲和文件加載的功能。
?
完成實驗~
?
實驗截圖
?
其他知識
1.Android為應用程序的存儲提供了五種方式:
Shared Preferences?
Internal Storage?
External Storage??
SQLite Database???
Network Connection
?
2.Internal Storage 和 External Storage 的區(qū)別:
Internal storage:總是可用的;這里的文件默認是只能被自己的app所訪問的;當用戶卸載app的時候,系統(tǒng)會把internal里面的相關(guān)文件都清除干凈;Internal是在你想確保不被用戶與其他app所訪問的最佳存儲區(qū)域。Internal storage 是屬于應用程序的,文件管理器看不見。
External storage:并不總是可用的,因為用戶可以選擇把這部分作為USB存儲模式,這樣就不可以訪問了;是大家都可以訪問的,因此保存到這里的文件是失去訪問控制權(quán)限的;當用戶卸載你的app時,系統(tǒng)僅僅會刪除external根目錄(getExternalFilesDir)下的相關(guān)文件;External是在你不需要嚴格的訪問權(quán)限并且你希望這些文件能夠被其他app所共享或者是允許用戶通過電腦訪問時的最佳存儲區(qū)域。External storage 在文件瀏覽器里是可以看見的/mnt。?
這兩個概念都是相對于應用來說的,應該理解為邏輯上的概念,不應理解為物理上的外部SD卡和手機或移動設(shè)備內(nèi)存。一個應用把數(shù)據(jù)存在external storage上時,那么數(shù)據(jù)成為共有的,所有人都可見的和可用的。存在internal storage上時,只有這個應用本身可以看到和使用。很多沒有插SD卡的設(shè)備,系統(tǒng)會虛擬出一部分存儲空間用來做公共存儲(主要是音樂,文檔之類的media)。
?
3.使用方法:
1)向內(nèi)部存儲器中創(chuàng)建一個私有文件并向其中寫入數(shù)據(jù),使用以下方法:
a.調(diào)用openFileOutput(String fileName, int mode)方法,
若fileName對應的文件存在,就打開該文件,若不存在,并以mode權(quán)限創(chuàng)建該文件并打開,該方法返回一個指向fileName對應文件的FileOutputStream,使用這個FileOutputStream可向文件中寫入數(shù)據(jù)。
b.調(diào)用FileOutputStream對象的write()方法向文件中寫入數(shù)據(jù)。
c.調(diào)用FileOutputStream對象的close()方法關(guān)閉文件寫入流。
例:向內(nèi)部存儲器中寫入一個名為"abc.txt"的文件后,會在內(nèi)部存儲器的/data/data/<package name>/files/目錄下生成"abc.txt"文件。
?
讀取內(nèi)部存儲器中私有文件的數(shù)據(jù),使用以下方法:
a.調(diào)用openFileInputStream(String fileName)方法打開內(nèi)部存儲器中fileName對應的文件,若該文件存在,該方法返回一個指向fileName文件的FileInputStream對象。
b.調(diào)用FileInputStream對象的read()方法讀取fileName文件中的內(nèi)容。
c.調(diào)用FileInputStream對象的close()方法關(guān)閉文件讀取流。
2)剛才保存到Internal中的時候什么都沒有配置,需要保存到外部的時候需要配置讀寫的權(quán)限,讀的權(quán)限READ_EXTERNAL_STORAGE,寫的權(quán)限:READ_EXTERNAL_STORAGE。
首先需要判斷一下SD卡是不是可用,因為external storage可能是不可用的比如SD卡被拔出,那么你應該在訪問之前去檢查是否可用。你可以通過執(zhí)行 getExternalStorageState()來查詢external storage的狀態(tài)。如果返回的狀態(tài)是MEDIA_MOUNTED, 那么你可以讀寫。看到這個獲取完之后跟上面保存在內(nèi)部存儲設(shè)備的過程一樣。是路徑是在/mnt/sdcard目錄下,如果是弄成私有文件,不允許外部訪問,目錄是在/mnt/sdcard/Android/data/包名 目錄下。
?
4.其它相關(guān):
如果想在編譯時就在應用程序中保存一個不允許修改的文件,就把這個文件保存在/res/raw/目錄下。
在程序中打開這個文件可以調(diào)用openRawResource(int id)方法, 里面的參數(shù)id表示R.raw.<file name>,
這個方法打開后會返回一個InputStream,使用它可以讀取這個文件。這個文件不能被執(zhí)行寫入操作。
?
如果有緩存文件需要保存,而這些文件并不需要永久保存,可以調(diào)用getCacheDir()方法,該方法執(zhí)行后會在內(nèi)部存儲器的/data/data/<package name>/目錄下創(chuàng)建一個名為cache/的空目錄(或打開cache/目錄),并返回一個File對象指向這個(空)文件夾。在這個cache/目錄下,可以保存緩存文件,當設(shè)備的內(nèi)部存儲器空間不夠用時,系統(tǒng)會自動刪除一部分cache/目錄下的緩存文件,但為了保證系統(tǒng)運行效率,應該手動對cache/目錄的大小進行控制,如控制它不能大于1M。當用戶卸載應用程序時,cache/目錄會連同一起被刪除。?
例:調(diào)用getCacheDir()方法,該方法執(zhí)行后會在內(nèi)部存儲器的/data/data/<package name>/目錄下創(chuàng)建一個名為cache/的空目錄。
?
源碼下載
源碼下載點擊這里~
?
?注
1、本實驗實驗環(huán)境:
操作系統(tǒng) Windows 10?
實驗軟件 Android Studio 2.2.1
虛擬設(shè)備:Nexus_5X
API:23
2、貼代碼的時候由于插入代碼框的大小問題,代碼格式不太嚴整,望見諒~
轉(zhuǎn)載于:https://www.cnblogs.com/yanglh6-jyx/p/Android_SharedPreferences_File.html
總結(jié)
以上是生活随笔為你收集整理的Android开发7:简单的数据存储(使用SharedPreferences)和文件操作的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 三星framebuffer驱动代码分析
- 下一篇: jieba初步了解