Android資料儲存 SharedPreferences及SDCard
一、資料儲存選項:Data Storage ——Storage Options
1、Shared Preferences
Store private primitive data in key-value pairs.
儲存簡單的鍵值對資料。
2、Internal Storage
Store private data on the device memory.
在手機記憶體中儲存不對外共享的資訊。
3、External Storage
Store public data on the shared external storage.
在外部儲存裝置上儲存公共的資料資訊。主要指儲存在SDCard
4、SQLite Databases
Store structured data in a private database.
將結構化的資料儲存進資料庫。
5、Network Connection
Store data on the web with your own network server.
將資料儲存到自己的遠端伺服器上。
【備註:】
- 內部儲存空間十分有限,因而顯得可貴,另外,它也是系統本身和系統應用程式主要的資料儲存所在地,一旦內部儲存空間耗盡,手機也就無法使用了。
- 所以對於內部儲存空間,我們要儘量避免使用。Shared Preferences和SQLite資料庫都是儲存在內部儲存
- getFilesDir()獲取你app的內部儲存空間,相當於你的應用在內部儲存上的根目錄。
- 最容易混淆的是外部儲存,如果說pc上區分出外部儲存和內部儲存的話,那麼自帶的硬碟算是內部儲存,U盤或者行動硬碟算是外部儲存,因此我們很容易帶著這樣的理解去看待安卓手機,認為機身固有儲存是內部儲存,而擴充套件的SDCard卡是外部儲存。比如Nexus 4有16G的內部儲存,普通消費者可以這樣理解,但是安卓的程式設計中不能,這16GB仍然是外部儲存。
二、SharedPreferences:
(一)、概念:
SharedPreferences是Android系統提供的一個通用的資料持久化
SharedPreferences主要用於儲存系統的配置資訊。例如上次登入的使用者名稱,上次最後設定的配置資訊(如:是否開啟音效、是否使用振動,小遊戲的玩家積分等)。當再次啟動程式後依然保持原有設定。SharedPreferences用鍵值對方式儲存,方便寫入和讀取。
(二)、使用SharedPreferences的步驟:
1、獲取SharedPreferences物件;
SharedPreferences本身是一個介面,無法直接建立例項,通過Context的getSharedPreferences(String name, int mode)方法來獲取例項。
該方法的第二個引數有以下三個值:【檔案讀寫的操作模式】
- Context.MODE_PRIVATE: 指定該SharedPreferences的資料只能被本應用程式讀、寫;
- Context.MODE_APPEND:新內容追加到原內容後;
- Context.MODE_WORLD_READABLE: 指定 SharedPreferences資料能被其他應用程式讀,但是不支援寫;
- Context.MODE_WORLD_WRITEABLE: 指定 SharedPreferences資料能被其他應用程式讀、寫。會覆蓋原資料。
- 可以使用 + 連線這些許可權。
2、呼叫edit()方法獲取SharedPreferences.Editor;
3、通過SharedPreferences.Editor介面提供的put()方法對SharedPreferences進行更新;
4、呼叫SharedPreferences.Editor的commit()方法,將更新提交到SharedPreferences中。
(三)、核心程式碼:
button_main_savedata.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
prefs = getSharedPreferences("myaccount", Context.MODE_PRIVATE);
SharedPreferences.Editor editor = prefs.edit();
editor.putInt("age", 38);
editor.putString("username", "wangxiangjun");
editor.putString("pwd", "123456");
editor.putString("username", "xiangjun");
editor.putString("age", "I'm 40 years old!");
editor.commit();
}
});
button_main_readdata.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
prefs = getSharedPreferences("myaccount", Context.MODE_PRIVATE);
String name = prefs.getString("username", "wxj");
String pwd = prefs.getString("pwd", "000");
int age = prefs.getInt("age", 20);
System.out.println("====>" + name + ":" + pwd + ":" + age);
}
});
(四)、儲存之後的SharedPreferences資料檔案:
SharedPreferences資料總是以xml格式儲存在:/data/data/包名/shared_prefs目錄下;
例如:
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="pwd">123456</string>
<string name="username">xiangjun</string>
<int name="age">20</int>
</map>
(五)、SharedPreferences的設定Setting功能:
1、引入:
手機中常有這樣的設定頁面,如果做這樣的頁面呢?是不是需要寫一個複雜的佈局檔案,再寫一堆事件監聽來完成呢?
2、PreferenceActivity的簡單用法:
1)、步驟:
- 將setting.xml檔案放到res的xml目錄下;
- 將arrays.xml檔案放到values目錄下;
- 寫一個頁面SettingActivity。
2)、目錄結構:
3)、核心程式碼:
//在SettingActivity中。不再需要setContentView(R.layout.activity_main)方法來載入佈局了。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
addPreferencesFromResource(R.xml.setting);
//備註:This method was deprecated in API level 11. This function is not relevant for a modern fragment-based PreferenceActivity.這個方法在11版本以上就已經不推薦使用了。
}
(六)、藉助SharedPreferences實現黑名單管理:
1、示例程式碼: publicclass MainActivity extends Activity { private ListView listView_main_blockList; private EditText editText_main_number; private TextView textView_main_emptyinfo; private SharedPreferences prefs = null; private Editor editor = null; private ArrayAdapter<String> adapter = null; @Override protectedvoid onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); editText_main_number = (EditText) findViewById(R.id.editText_main_number); listView_main_blockList = (ListView) findViewById(R.id.listView_main_blocklist); textView_main_emptyinfo = (TextView) findViewById(R.id.text_main_emptyinfo); prefs = getSharedPreferences("blocklist", Context.MODE_PRIVATE); editor = prefs.edit(); List<String> list = getBlocklist(); adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, list); // 注意setEmptyView()的用法。當介面卡為空的時候,設定ListView中的展示內容。 listView_main_blockList.setEmptyView(textView_main_emptyinfo); listView_main_blockList.setAdapter(adapter); } publicvoid clickButton(View view) { switch (view.getId()) { case R.id.button_main_add: String mpnumber = editText_main_number.getText().toString(); editor.putString(mpnumber, mpnumber); editor.commit(); fillListView(); break; case R.id.button_main_clear: editor.clear(); editor.commit(); fillListView(); break; default: break; } } /* * 獲取SharedPreferences中的全部資料,放到List集合中。形成介面卡的資料來源 */ private List<String> getBlocklist() { List<String> list = new ArrayList<String>(); try { Map<String, ?> map = prefs.getAll(); // 增強for迴圈,實現對Map集合的遍歷 for (Map.Entry<String, ?> entry : map.entrySet()) { list.add(entry.getKey()); } return list; } catch (Exception e) { returnnull; } } /* * 填充ListView控制元件,實現重新整理顯示資料的效果 */ privatevoid fillListView() { adapter.clear(); adapter.addAll(getBlocklist()); } @Override publicboolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.main, menu); returntrue; } }
三、External Storage之SDCard操作:
(一)、引入:Android中提供了特有的兩個方法來進行IO操作(openFileInput()和openFileOutput() ),但是畢竟手機內建儲存空間很有限,為了更好地儲存應用程式的大檔案資料,需要讀寫SD卡上的檔案。SD卡大大擴充了手機的儲存能力。
(二)、讀寫SD卡的步驟:
1、先判斷手機是否有sd卡;
呼叫Environment的getExternalStorageState()方法判斷手機是否插上sdcard。
2、獲取sdcard的路徑;
呼叫Environment的getExternalStorageDirectory()方法來獲取外部儲存器的目錄。
3、此外還可以獲取SDCard可用磁碟空間的大小(藉助StatFs類來實現);
4、清單檔案中設定讀寫sdcard的許可權;
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 在sdcard中建立與刪除檔案的許可權
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> 向sdcard寫入許可權
5、執行讀寫操作(基本IO流操作)。
【備註:】
Environment.getExternalStorageDirectory().getPath()來獲取sdcard路徑,如果您需要往sdcard中儲存特定型別的內容,可以考慮使用Environment.getExternalStoragePublicDirectory(String type)方法,該方法可以返回特定型別的目錄,目前支援如下型別:
- DIRECTORY_ALARMS //警報的鈴聲
- DIRECTORY_DCIM //相機拍攝的圖片和視訊儲存的位置
- DIRECTORY_DOWNLOADS //下載檔案儲存的位置
- DIRECTORY_MOVIES //電影儲存的位置, 比如 通過google play下載的電影
- DIRECTORY_MUSIC //音樂儲存的位置
- DIRECTORY_NOTIFICATIONS //通知音儲存的位置
- DIRECTORY_PICTURES //下載的圖片儲存的位置
- DIRECTORY_PODCASTS //用於儲存podcast(部落格)的音訊檔案
- DIRECTORY_RINGTONES //儲存鈴聲的位置
【備註:】
應用程式在執行的過程中如果需要向手機上儲存資料,一般是把資料儲存在SDcard中的。大部分應用是直接在SDCard的根目錄下建立一個資料夾,然後把資料儲存在該資料夾中。這樣當該應用被解除安裝後,這些資料還保留在SDCard中,留下了垃圾資料。如果你想讓你的應用被解除安裝後,與該應用相關的資料也清除掉,該怎麼辦呢?
- 通過Context.getExternalFilesDir()方法可以獲取到 SDCard/Android/data/應用的包名/files/ 目錄,一般放一些長時間儲存的資料 【設定->應用->應用詳情裡面的”清除資料 Clear Data“】
- 通過Context.getExternalCacheDir()方法可以獲取到 SDCard/Android/data/應用包名/cache/目錄,一般存放臨時快取資料 【設定->應用->應用詳情裡面的”清除快取“ Clear Cache】
- 如果使用上面的方法,當你的應用在被使用者解除安裝後,SDCard/Android/data/你的應用的包名/ 這個目錄下的所有檔案都會被刪除,不會留下垃圾資訊。
而且上面二個目錄分別對應 設定->應用->應用詳情裡面的”清除資料“與”清除快取“選項。當然如果要儲存下載的內容,就不要放在以上目錄下。
(三)、SDCard私有檔案目錄:
1、私有目錄的files目錄下有分為以下7種(無DIRECTORY_DCIM和DIRECTORY_DOWNLOADS):
- DIRECTORY_ALARMS
- DIRECTORY_MOVIES
- DIRECTORY_MUSIC
- DIRECTORY_NOTIFICATIONS
- DIRECTORY_PICTURES
- DIRECTORY_PODCASTS
- DIRECTORY_RINGTONES
2、私有目錄的cache目錄:
(三)、封裝SDCard的工具類:SDCardHelper類
package com.steven.sdcardhelper;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import andoid.ontent.Context;
import android.os.Environment;
import android.os.StatFs;
public class SDCardHelper {
// 判斷SD卡是否被掛載
public static boolean isSDCardMounted() {
// return Environment.getExternalStorageState().equals("mounted");
return Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED);
}
// 獲取SD卡的根目錄
public static String getSDCardBaseDir() {
if (isSDCardMounted()) {
return Environment.getExternalStorageDirectory().getAbsolutePath();
}
return null;
}
// 獲取SD卡的完整空間大小,返回MB
public static long getSDCardSize() {
if (isSDCardMounted()) {
StatFs fs = new StatFs(getSDCardBaseDir());
int count = fs.getBlockCount();
int size = fs.getBlockSize();
return count * size / 1024 / 1024;
}
return 0;
}
// 獲取SD卡的剩餘空間大小
public static long getSDCardFreeSize() {
if (isSDCardMounted()) {
StatFs fs = new StatFs(getSDCardBaseDir());
int count = fs.getFreeBlocks();
int size = fs.getBlockSize();
return count * size / 1024 / 1024;
}
return 0;
}
// 獲取SD卡的可用空間大小
public static long getSDCardAvailableSize() {
if (isSDCardMounted()) {
StatFs fs = new StatFs(getSDCardBaseDir());
int count = fs.getAvailableBlocks();
int size = fs.getBlockSize();
return count * size / 1024 / 1024;
}
return 0;
}
// 往SD卡的公有目錄下儲存檔案
public static boolean saveFileToSDCardPublicDir(byte[] data, String type,
String fileName) {
BufferedOutputStream bos = null;
if (isSDCardMounted()) {
File file = Environment.getExternalStoragePublicDirectory(type);
try {
bos = new BufferedOutputStream(new FileOutputStream(new File(
file, fileName)));
bos.write(data);
bos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return false;
}
// 往SD卡的自定義目錄下儲存檔案
public static boolean saveFileToSDCardCustomDir(byte[] data, String dir,
String fileName) {
BufferedOutputStream bos = null;
if (isSDCardMounted()) {
File file = new File(getSDCardBaseDir() + File.separator + dir);
if (!file.exists()) {
file.mkdirs();// 遞迴建立自定義目錄
}
try {
bos = new BufferedOutputStream(new FileOutputStream(new File(
file, fileName)));
bos.write(data);
bos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return false;
}
// 往SD卡的私有Files目錄下儲存檔案
public static boolean saveFileToSDCardPrivateFilesDir(byte[] data,
String type, String fileName, Context context) {
BufferedOutputStream bos = null;
if (isSDCardMounted()) {
File file = context.getExternalFilesDir(type);
try {
bos = new BufferedOutputStream(new FileOutputStream(new File(
file, fileName)));
bos.write(data);
bos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return false;
}
// 往SD卡的私有Cache目錄下儲存檔案
public static boolean saveFileToSDCardPrivateCacheDir(byte[] data,
String fileName, Context context) {
BufferedOutputStream bos = null;
if (isSDCardMounted()) {
File file = context.getExternalCacheDir();
try {
bos = new BufferedOutputStream(new FileOutputStream(new File(
file, fileName)));
bos.write(data);
bos.flush();
return true;
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
return false;
}
// 從SD卡獲取檔案
public static byte[] loadFileFromSDCard(String fileDir) {
BufferedInputStream bis = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
bis = new BufferedInputStream(
new FileInputStream(new File(fileDir)));
byte[] buffer = new byte[8 * 1024];
int c = 0;
while ((c = bis.read(buffer)) != -1) {
baos.write(buffer, 0, c);
baos.flush();
}
return baos.toByteArray();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
baos.close();
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
// 獲取SD卡公有目錄的路徑
public static String getSDCardPublicDir(String type) {
return Environment.getExternalStoragePublicDirectory(type).toString();
}
// 獲取SD卡私有Cache目錄的路徑
public static String getSDCardPrivateCacheDir(Context context) {
return context.getExternalCacheDir().getAbsolutePath();
}
// 獲取SD卡私有Files目錄的路徑
public static String getSDCardPrivateFilesDir(Context context, String type) {
return context.getExternalFilesDir(type).getAbsolutePath();
}
}
(四)、案例:
1、功能:點選按鈕,實現從網路上訪問圖片,將圖片儲存進SDCard中。點選另外一按鈕,可以獲取到剛才儲存進SDCard中的圖片,將其載入的頁面中的ImageView控制元件中。
2、示例程式碼:、示例程式碼:
publicclass MainActivity extends Activity {
private ImageView imageView_main_img;
private String urlString = "http://t2.baidu.com/it/u=2,1891512358&fm=19&gp=0.jpg";
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView_main_img = (ImageView) findViewById(R.id.imageView_main_img);
}
publicvoid clickButton(View view) {
switch (view.getId()) {
case R.id.button_main_save:
new MyTask(this).execute(urlString);
break;
case R.id.button_main_show:
String filepath = SDCardHelper.getSDCardPath() + File.separator
+ "mydir" + File.separator + "firstimg.jpg";
byte[] data = SDCardHelper.loadFileFromSDCard(filepath);
if (data != null) {
Bitmap bm = BitmapFactory.decodeByteArray(data, 0, data.length);
imageView_main_img.setImageBitmap(bm);
} else {
Toast.makeText(this, "沒有該圖片!", Toast.LENGTH_LONG).show();
}
break;
default:
break;
}
}
class MyTask extends AsyncTask<String, Void, byte[]> {
private Context context;
private ProgressDialog pDialog;
public MyTask(Context context) {
this.context = context;
pDialog = new ProgressDialog(context);
pDialog.setIcon(R.drawable.ic_launcher);
pDialog.setMessage("圖片載入中...");
}
@Override
protectedvoid onPreExecute() {
super.onPreExecute();
pDialog.show();
}
@Override
protectedbyte[] doInBackground(String... params) {
BufferedInputStream bis = null;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
URL url = new URL(params[0]);
HttpURLConnection httpConn = (HttpURLConnection) url
.openConnection();
httpConn.setDoInput(true);
httpConn.connect();
if (httpConn.getResponseCode() == 200) {
bis = new BufferedInputStream(httpConn.getInputStream());
byte[] buffer = newbyte[1024 * 8];
int c = 0;
while ((c = bis.read(buffer)) != -1) {
baos.write(buffer, 0, c);
baos.flush();
}
return baos.toByteArray();
}
} catch (Exception e) {
e.printStackTrace();
}
returnnull;
}
@Override
protectedvoid onPostExecute(byte[] result) {
super.onPostExecute(result);
if (result == null) {
Toast.makeText(context, "圖片載入失敗!", Toast.LENGTH_LONG).show();
} else {
// 將位元組陣列轉成Bitmap,然後將bitmap載入的imageview控制元件中
// Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0,
// result.length);
// imageView_main_img.setImageBitmap(bitmap);
if (SDCardHelper.saveFileToSDCard(result, "mydir",
"firstimg.jpg")) {
Toast.makeText(context, "圖片儲存OK!", Toast.LENGTH_LONG)
.show();
} else {
Toast.makeText(context, "圖片儲存失敗!", Toast.LENGTH_LONG)
.show();
}
}
pDialog.dismiss();
}
}
@Override
publicboolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
returntrue;
}
}
(五)、案例:SDCard檔案瀏覽器
2、原理:利用File物件的listFile()方法獲得File[]陣列。將陣列產生的資訊填充在listview中。
核心程式碼中的重要方法:
- listFiles()
- isFile()
- isDirectory()
- getAbsolutePath()
- getParentFile()
3、核心示例程式碼:
publicclass MainActivity extends Activity {
private TextView textView_main_currentpath;
private ListView listView_main_fileList;
private File currentFile = null;
private File[] arrCurrentFiles = null;
@Override
protectedvoid onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView_main_currentpath = (TextView) findViewById(R.id.text_main_currentpath);
listView_main_fileList = (ListView) findViewById(R.id.listView_main_filelist);
if (SDCardHelper.isSDCardMounted()) {
currentFile = new File(SDCardHelper.getSDCardPath());
fillListView(currentFile);
} else {
Toast.makeText(MainActivity.this, "SDCARD不存在!", Toast.LENGTH_LONG)
.show();
}
listView_main_fileList
.setOnItemClickListener(new OnItemClickListener() {
@Override
publicvoid onItemClick(AdapterView<?> parent, View view,
int position, long id) {
if (arrCurrentFiles[position].isDirectory()) {
File[] arrSubFiles = arrCurrentFiles[position]
.listFiles();
if (arrSubFiles.length == 0) {
Toast.makeText(MainActivity.this, "您點選的是空目錄!",
2000).show();
} else {
fillListView(arrCurrentFiles[position]);
}
} else {
Toast.makeText(MainActivity.this, "您點選的不是目錄!",
Toast.LENGTH_LONG).show();
}
}
});
}
publicvoid clickButton(View view) {
switch (view.getId()) {
case R.id.imageView_main_back:
if (!currentFile.getAbsolutePath().equals(
SDCardHelper.getSDCardPath())) {
fillListView(currentFile.getParentFile());
}
break;
default:
break;
}
}
publicvoid fillListView(File file) {
currentFile = file;
arrCurrentFiles = currentFile.listFiles();
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>();
for (int i = 0; i < arrCurrentFiles.length; i++) {
Map<String, Object> map = new HashMap<String, Object>();
if (arrCurrentFiles[i].isDirectory()) {
map.put("imgId", R.drawable.folder);
} else {
map.put("imgId", R.drawable.file);
}
map.put("filename", arrCurrentFiles[i].getName());
list.add(map);
}
SimpleAdapter adapter = new SimpleAdapter(MainActivity.this, list,
R.layout.item_listview_main,
new String[] { "imgId", "filename" }, newint[] {
R.id.imageView_item_listview_type,
R.id.text_item_listview_filename });
listView_main_fileList.setAdapter(adapter);
textView_main_currentpath.setText(currentFile.getAbsolutePath());
}
}
三、Android資料持久化總結:
檔案儲存:資料從持久化
SharedPreferences
內部儲存
外部儲存
資料庫儲存
路徑
1、data/data/包名【內部儲存目錄】
1.1 data/data/包名/shared_prefs
1.2 data/data/包名/databases
1.3 data/data/包名/files
context.getFilesDir()
1.4 data/data/包名/cache
context.getCacheDir()
2、data/app/apk
3、mnt【外部儲存】
mnt/sdcard
4、storage【外部儲存】
4.1 storage/sdcard
Environment.getExternalStorageDir()
公有目錄(九大公有目錄)
Environment.getExternalStoragePublicDir(String type)
該type引數是Environment中的常量
4.2 私有目錄
storage/sdcard/data/包名/files
context.getExternalFilesDir()
storage/sdcard/data/包名/cache
context.getExternalCacheDir()