SharedPreference儲存精煉詳解
-
前期基礎知識儲備
之前做應用時碰到一個問題:在使用者初次進入某個介面時,需要彈出一個提示,或者在EditText上顯示一個hint;而使用者之後進入就不再彈出提示了。思考之後就選擇了SharedPreferences這個輕型的資料儲存方式。儲存一個布林型值作為key,根據這個key作為判斷是否是第一次進入此介面。
protected void onCreate(@Nullable Bundle savedInstanceState) { ... ... preferences = PreferenceManager.getDefaultSharedPreferences(this); boolean isStarted = preferences.getBoolean(STARTED, false); if ((!isStarted) || ConfigApp.DEBUG) { if (isDataNull) { mInputFormula.setText("x^2+2x+1=9"); firstStarted(); } } } //應用第一次啟動時呼叫該方法 用於改變“value”值 private void firstStarted() { final SharedPreferences.Editor editor = preferences.edit(); editor.putBoolean(STARTED, true); editor.apply(); }
SharedPreferences的本質是基於XML檔案儲存key-value鍵值對資料,通常用來儲存一些簡單的配置資訊,用Sqlite資料庫來存放並不划算,因為資料庫連線跟操作等耗時大大影響了程式的效率。其儲存位置在/data/data/<包名>/shared_prefs目錄下。
“SharedPreference”是Android的四大資料持久化技術之一,其他三個分別是SQLite、Content Provider 和 File。
儲存特點:
①儲存基本的資料型別:int、long、boolean、String、Float、Set和Map這些資料型別,不支援自定義資料型別;
②4.2之後只能用於應用內部資料儲存和共享,4.2之前支援跨應用共享;
③key-value鍵值對資料,更新時,如果已經插入的key存在,那麼將更新原來的key;那麼將更新原key所對應的value;
④使用者應用程式一旦解除安裝,存在應用資料目錄下的shared_prefs檔案也會被刪除。
-
上程式碼,具體實現
1.存放資料資訊,步驟如下:
①開啟Preferences,名稱為user,如果存在則開啟它,否則建立新的Preferences;
建立Preference例項物件的方法有三種:
SharedPreference preference=getSharedPreferences("user", Context.MODE_PRIVATE); SharedPreference preference=PreferenceManager.getDefaultSharedPreferences(context); SharedPreference preference=context.getPreference;
a.Context.getSharedPreferences(檔名稱,操作模式)
檔名稱不存在就會建立一個,操作模式有四種:
MODE_PRIVATE:預設操作模式,直接在把第二個引數寫0就是預設使用這種操作模式,這種模式表示只有當前的應用程式才可以對當前這個SharedPreferences檔案進行讀寫。
MODE_MULTI_PRIVATE:追加模式,用於當前應用程式內部多個執行緒共同操作一個SharedPreferences檔案。
MODE_WORLD_READABLE:可讀模式,用於應用間實現資料共享,即創建出來的檔案可被其他應用讀取;
MODE_WORLD_WRITEABLE:可寫模式,用於應用間實現資料共享,即建立的檔案允許其他應用對其進行寫入;
注:MODE_WORLD_READABLE和MODE_WORLD_WRITEABLE這兩種模式已經在android 4.2版本以後廢棄了。
b.PreferenceManager.getDefaultSharedPreferences(Context context)
使用這個方法會自動使用當前程式的包名作為字首來命名SharedPreferences檔案;注意這裡需要傳入上下文Context作為引數,如果是在Activity內部使用,可直接傳入context;如果是在其他類內部使用,需傳入的應用程式級別的上下文。
c.Activity.getPreferences(操作模式)
使用這個方法會自動將當前活動的類名作為SharedPreferences的檔名,呼叫Activity物件的getPreferences()方法獲得的SharedPreferences物件只能在該Activity中使用。而第一種呼叫Context物件的getSharedPreferences()方法獲得的SharedPreferences物件可以被同一應用程式下的其他元件共享。所以第一種獲取Preference例項的方法使用最多。
②讓user處於被編輯狀態
Editor editor=preferences.edit();
③存放資料
String name="xixi";
String age="22";
editor.putString("name", name);
editor.putString("age", age);
④提交
editor.commit();
2.讀取資料資訊,步驟如下:
①獲取Preferences
SharedPreferences preferences=getSharedPreferences("user", Context.MODE_PRIVATE);
②取出資料
String name=preferences.getString("name", "null");
String age=preferences.getString("age", "0");
3.SharedPreference其他操作
①清除特定資料
SharedPreferences.Editor editor = user.edit();
editor.remove("KEY");
editor.commit();
②清除所有資料
SharedPreferences.Editor editor = user.edit();
editor.clear();
editor.commit();
③檢索特定資料
SharedPreference preference=getSharedPreferences("user", Context.MODE_PRIVATE);
boolean isContains=preference.contains("key");
-
SharedPreference儲存使用者設定例項
-
protected void onCreate(@Nullable Bundle savedInstanceState) { ... ... wSharedPreferences = getSharedPreferences("window", Context.MODE_PRIVATE);; isWindowSharedOpen = wSharedPreferences.getBoolean("isWindowOpen", false); if (isWindowSharedOpen) { mWCheckBox.setChecked(true); } vSharedPreferences = getSharedPreferences("vibration",Context.MODE_PRIVATE); isVibrationShareOpen = vSharedPreferences.getBoolean("isVibrationOpen",false); if (isVibrationShareOpen){ mVCheckBox.setChecked(true); } } private boolean isWindowSharedOpen,isVibrationShareOpen; private boolean b = false; private boolean a = false; private SharedPreferences wSharedPreferences,vSharedPreferences; private CompoundButton.OnCheckedChangeListener listener = new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { switch (buttonView.getId()) { case R.id.window_check_box: if (!a) { wSharedPreferences.edit().putBoolean("isWindowOpen", true).commit(); Toast.makeText(BasicCalculatorActivity.this, "懸浮窗開", Toast.LENGTH_SHORT).show(); //開啟懸浮窗 askForPermission(); a = true; } else if (a) { wSharedPreferences.edit().putBoolean("isWindowOpen", false).commit(); Toast.makeText(BasicCalculatorActivity.this, "懸浮窗關", Toast.LENGTH_SHORT).show(); //關閉懸浮窗 stopService(floatWinIntent); a = false; } break; case R.id.vibration_check_box: if (!b) { vSharedPreferences.edit().putBoolean("isVibrationOpen", true).commit(); Toast.makeText(BasicCalculatorActivity.this, "振動開", Toast.LENGTH_SHORT).show(); //開啟振動 所有的鍵盤都在碎片裡 要傳送廣播 sendBroadcast(new Intent(BasboardFragment.OPENVIBRATION)); sendBroadcast(new Intent(KeyboardFragment.OPENVIBRATION)); b = true; } else if (b) { vSharedPreferences.edit().putBoolean("isVibrationOpen", false).commit(); Toast.makeText(BasicCalculatorActivity.this, "振動關", Toast.LENGTH_SHORT).show(); //關閉振動 sendBroadcast(new Intent(BasboardFragment.CLOSEVIBRATION)); sendBroadcast(new Intent(KeyboardFragment.CLOSEVIBRATION)); b = false; } break; } } };
上面是CheckBox的點選儲存事件,接下來是RadioButton的點選儲存事件,注意兩個控制元件的點選按鈕邏輯處理不同:
-
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private PopupWindow popupWindow; private View popupView; private Button testBtn; private RadioButton choice_1, choice_2, result_1, result_2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initPopoupWindow(); testBtn = (Button) findViewById(R.id.test_btn); testBtn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showPopupWindow(); } }); result_1 = (RadioButton) findViewById(R.id.result_1); result_2 = (RadioButton) findViewById(R.id.result_2); /*處理第二個問題,儲存SharedPreference儲存的動作 * 第三個問題擺在眼前,處理popupwindow本身的儲存邏輯 不能放在應用初始化時,否則報空 * */ choice_1Preference = getSharedPreferences("choice_1", 0); isShared1 = choice_1Preference.getBoolean("choice_1_key", false); if (isShared1) { result_1.setChecked(true); choice_1.setChecked(true); } choice_2Preference = getSharedPreferences("choice_2", 0); isShared2 = choice_2Preference.getBoolean("choice_2_key", false); if (isShared2) { result_2.setChecked(true); choice_2.setChecked(true); } } /* * 處理第三個問題 popupwindow自身的儲存 將其變數做成成員變數 待處理的邏輯還有兩個:①進入應用時pop的儲存還原;②點選pop時的儲存還原 * */ private void initPopoupWindow(){ popupView = LayoutInflater.from(MainActivity.this).inflate(R.layout.popupview, null); popupWindow = new PopupWindow(popupView, ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, true); choice_1 = (RadioButton) popupView.findViewById(R.id.choice_1); choice_1.setOnClickListener(this); choice_2 = (RadioButton) popupView.findViewById(R.id.choice_2); choice_2.setOnClickListener(this); } private void showPopupWindow() { popupWindow.setContentView(popupView); popupWindow.setOutsideTouchable(true); popupWindow.showAsDropDown(testBtn); popupWindow.setAnimationStyle(); } private SharedPreferences choice_1Preference, choice_2Preference; private boolean isShared1, isShared2; /* * 處理第一個問題:單選框和複選框自帶邏輯本身就不一樣,所以對判斷的處理也是不一樣 * */ @Override public void onClick(View v) { switch (v.getId()) { case R.id.choice_1: choice_1Preference.edit().putBoolean("choice_1_key", true).commit(); choice_2Preference.edit().putBoolean("choice_2_key", false).commit(); result_1.setChecked(true); break; case R.id.choice_2: choice_2Preference.edit().putBoolean("choice_2_key", true).commit(); choice_1Preference.edit().putBoolean("choice_1_key", false).commit(); result_2.setChecked(true); } } }
-
延伸知識
1)File類的四種構造方法:
public File(File dir,String name)
Public File(String path)
public File(String dirPath,String name)
public File(URI uri)
2)檔案儲存之獲取 — 應用資料目錄/手機外部儲存根目錄
①應用資料目錄:Context.getExternalFilesDir("file1");
返回路徑:-/storage/sdcard0/Android/data/com.srain.cube.sample/files/file1
用於儲存應用內部資料,該目錄下的檔案會隨著應用刪除而一起刪除,比如應用內部下載的圖示、臨時檔案等等。
②手機外部儲存根目錄: Environment.getExternalStoragePublicDirectory("");
返回的路徑名:- /storage/sdcard0
該目錄用於儲存應用產生的對使用者有價值的資料,不隨著應用刪除而刪除,比如照片、歌曲,視訊等檔案等等
3)例項 應用資料目錄中儲存和刪除圖片
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private Button saveBtn, deleteBtn;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (MarshmallowPermissions.checkPermissionForCamera(MainActivity.this)) {
MarshmallowPermissions.requestPermissionForCamera(MainActivity.this);
}
saveBtn = findViewById(R.id.save);
saveBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
saveImageToGallery(MainActivity.this, "SI1");
}
});
deleteBtn = findViewById(R.id.delete);
deleteBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (appDir != null) {
deleteDirWihtFile(appDir);
}
}
});
}
private File file;
private File appDir;
public void saveImageToGallery(Context context, String fileName) {
// 儲存圖片
appDir = new File(context.getExternalFilesDir(""), "測試資料夾");
if (!appDir.exists()) {
appDir.mkdir();
}
file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
Bitmap btImage = BitmapFactory.decodeResource(getResources(), R.drawable.filter_lut_portrait_m10);
btImage.compress(Bitmap.CompressFormat.JPEG, 100, fos);
Log.d(TAG, "saveImageToGallery_Path: " + file.getAbsolutePath());
Toast.makeText(context, "save success", Toast.LENGTH_SHORT).show();
fos.flush();
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private void deleteDirWihtFile(File dir) {
Log.d(TAG, "deleteDirWihtFile: " + dir.getPath());
if (dir == null || !dir.exists() || !dir.isDirectory())
return;
for (File file : dir.listFiles()) {
if (file.getName().equals("SI1"))
file.delete(); // 刪除所有檔案
Toast.makeText(this, "刪除成功", Toast.LENGTH_SHORT).show();
}
}
}
path —— /storage/emulated/0/Android/data/com.example.recycler_date/files/測試資料夾