באנדרואיד קימות מספר אפשרויות לשמירת נתונים (data persistence):
- שמירת נתונים בקובץ. בזיכרון פנימי, או חיצוני- למשל SD Card.
- SQlite Database - שימוש בבסיס הנתונים הסטנדרטי - ראה פוסטים בנושא: מבוא ל-SQlite.
- Content Provider - האמצעי היחידי (נכון לעכשיו), שמאפשר שיתוף נתונים בין אפליקציות. מתוכנן פוסט בנושא.
- שמירה על שרת ברשת.
- Shared Preferences - המנגנון עליו נדון כאן.
- הראשון - הנוכחי - ידגים שמירת ושליפת נתונים מזיכרון.
- הפוסט השני בנושא יציג את ה-Preferences Framework - מנגנון שמשלב את ה-UI עם ה-Shared Preferences. הוא נותן widgets סטנדרטים לבחירה של העדפות משתמש. הגדרת ה-UI מתבצעת ע"י קובץ xml. עם view groups מיוחדים.
Shared Preferences הוא מנגנון יעיל ונוח לשמירת נתונים. הנתונים נשמרים בצמדים של key/value.
מגבלות:
- שמירה של נתונים "פרימיטיבים" בלבד: booleans, floats, ints, longs, strings.
- הנתונים נגישים רק בתוך האפליקציה - נכון לרגע זה. עקרונית ה-shared preferences אמורים להיות נגישים גם מתוך אפליקציות אחרות.
- (getSharedPreferences(String name, int mode: שיטה זו מאפשרת פתיחת קבצים רבים, כשכל קובץ מוגדר ע"י שם - String - ראו פרמטר ראשון של ה-API הנ"ל. לכל קובץ כזה ניתן לגשת מכל ה-Activities של האפליקציה. הפרמטר השני - security mode מגדיר מגבלות כתיבה וקריאה מתוך אפליקציות אחרות. כרגע לא רלוונטי, ואפשר לשים תמיד 0. אחזור לזה במהלך המעבר על הקוד.
- (getPreferences(int mode: ה-API הזה מגדיר קובץ יחיד, כך ש כאן התוכנית לא קובעת לקובץ שם. קובץ זה שייך ל-Activity בו הוא נוצר ורק ממנו היגשים לקובץ. בכל Activity אפשר להגדיר קובץ אחד כזה.
- ()getDefaultSharedPreferences: שימוש בקובץ יחיד. בשונה מה-getPreferences, בעזרת ה-API הזה ניתן לגשת לקובץ מכל Activity של האפליקציה.
ניגש לדוגמא:
- נדגים שמירה של נתונים. נשתמש ב-getSharedPreferences.
- נדגים גם שימוש ב- getDefaultSharedPreferences.
- (לא נדגים את getPreference, אבל הוא מקרה פרטי של getSharedPreference).
- נדגים גישה לנתונים מ-Activity אחר. המעבר בין ה-Activities בעזרת intents.
- הנתונים אותם נשמור, הם מונים (counter) שעוקבים אחרי ה-callbacks של ה-Activity Lifecycle.
- נשים מונה בשישה callbacksL:
- onCreate
- onStart
- onResume
- onPause
- onStop
- onDestroy
- אגב, זוהי הזדמנות לעקוב אחרי ה-life cycle של ה-Activity ע"י צפיה בהתקדמות המונים.
הנה התצוגה של ה-Activity הראשי:
על המסך מופיעים:
- כפתור למעבר ל-Activity השני. המעבר בין ה-Activities מפעיל את ששת ה-callbacks של lifecycle וכך המונים מתקדמים.
- כפתור לאיפוס ה-counters.
- תצוגה של ששת ה-lifecycle counters + תצוגה של מס הלחיצות על הכפתור. כל 7 המונים הנ"ל נשמרים בקובץ ה-preferences.
הקוד:
הקוד מורכב משני Activities:
- ה-Activity הראשי בו נשמרים ומוצגים ה-counters.
- ה-Activity השני שמבצע שליפה של הנתונים והצגתם.
הנה ה-Activity הראשי:
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class SimpleSharedPrefs extends Activity {
Integer createCnt;
Integer startCnt;
Integer resumeCnt;
Integer pauseCnt;
Integer stopCnt;
Integer destroyCnt;
Integer buttonPushCnt;
public static final String CREATE_CNT = "CREATE_CNT";
public static final String START_CNT = "START_CNT";
public static final String RESUME_CNT = "RESUME_CNT";
public static final String PAUSE_CNT = "PAUSE_CNT";
public static final String STOP_CNT = "STOP_CNT";
public static final String DESTROY_CNT = "DESTROY_CNT";
public static final String BUTTON_CNT = "BUTTON_CNT";
public static final String PREFS_NAME = "PREFS_NAME";
SharedPreferences prefs;
SharedPreferences prefsDefault;
TextView textViewLifecycleCnt;
TextView textViewButtonPushCnt;
Button button_push;
Intent intent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class SimpleSharedPrefs extends Activity {
Integer createCnt;
Integer startCnt;
Integer resumeCnt;
Integer pauseCnt;
Integer stopCnt;
Integer destroyCnt;
Integer buttonPushCnt;
public static final String CREATE_CNT = "CREATE_CNT";
public static final String START_CNT = "START_CNT";
public static final String RESUME_CNT = "RESUME_CNT";
public static final String PAUSE_CNT = "PAUSE_CNT";
public static final String STOP_CNT = "STOP_CNT";
public static final String DESTROY_CNT = "DESTROY_CNT";
public static final String BUTTON_CNT = "BUTTON_CNT";
public static final String PREFS_NAME = "PREFS_NAME";
SharedPreferences prefs;
SharedPreferences prefsDefault;
TextView textViewLifecycleCnt;
TextView textViewButtonPushCnt;
Button button_push;
Intent intent;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- textViewLifecycleCnt = (TextView)findViewById(R.id .textview_lifecycle_cnt);
- textViewButtonPushCnt = (TextView)findViewById(R.id .text_viewbutton_push_cnt);
- Button button_push = (Button)findViewById(R.id.button_push);
- Button buttonClean = (Button)findViewById(R.id.button_clean);
- restoreAll();
- createCnt+=1;
- buttonClean.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- createCnt = 0;
- startCnt = 0;
- resumeCnt = 0;
- pauseCnt = 0;
- stopCnt = 0;
- destroyCnt = 0;
- buttonPushCnt = 0;
- displayAll();
- }
- });
- ButtonListener buttonListener = new ButtonListener();
- button_push.setOnClickListener(buttonListener);
- intent = new Intent (this, SecondActivity.class);
- }
- @Override
- public void onStart() {
- super.onStart();
- startCnt++;
- }
- @Override
- public void onResume() {
- super.onResume();
- resumeCnt++;
- displayAll();
- }
- @Override
- public void onPause() {
- super.onPause();
- pauseCnt++;
- saveAll();
- }
- @Override
- public void onStop() {
- super.onStop();
- stopCnt++;
- saveAll();
- }
- @Override
- public void onDestroy() {
- destroyCnt++;
- saveAll();
- super.onDestroy();
- }
- /* Before onStop. No guarantee if before or after onPause */
- @Override
- public void onSaveInstanceState(Bundle saveInstanceState) {
- // saveInstanceState.putInt(CREATE_CNT, createCnt);
- }
- /* Invoked after onStart: */
- @Override
- public void onRestoreInstanceState(Bundle savedInstanceState) {
- if (savedInstanceState != null)
- if (savedInstanceState.containsKey(CREATE_CNT)){
- // createCnt = savedInstanceState.getInt(CREATE_CNT, 0);
- }
- }
- class ButtonListener implements View.OnClickListener{
- public void onClick(View view) {
- buttonPushCnt++;
- textViewButtonPushCnt.setText("Push Button Counter = "+buttonPushCnt);
- startActivity(intent);
- finish();
- }
- }
- private void saveAll(){
- // Demonstrate both API: getDefaultSharedPreferences and getSharedPreferences
- // (Each API could do the job by itself)
- // Demo API 1:
- Context context = getApplicationContext();
- prefsDefault = PreferenceManager.getDefaultSharedPreferences(context);
- Editor editor1 = prefs.edit();
- editor1.putInt(CREATE_CNT, createCnt);
- editor1.commit();
- // Demo API 2:
- prefs = getSharedPreferences(PREFS_NAME, MODE_WORLD_WRITEABLE);
- Editor editor = prefs.edit();
- editor.putInt(CREATE_CNT, createCnt);
- editor.putInt(START_CNT, startCnt);
- editor.putInt(RESUME_CNT, resumeCnt);
- editor.putInt(PAUSE_CNT, pauseCnt);
- editor.putInt(STOP_CNT, stopCnt);
- editor.putInt(DESTROY_CNT, destroyCnt);
- editor.putInt(BUTTON_CNT, buttonPushCnt);
- editor.commit();
- }
- private void restoreAll(){
- // Demo API 1:
- Context context = getApplicationContext();
- prefsDefault = PreferenceManager.getDefaultSharedPreferences(context);
- createCnt = prefsDefault.getInt(CREATE_CNT, 0);
- // Demo API 2:
- prefs = getSharedPreferences(PREFS_NAME, 0);
- startCnt = prefs.getInt(START_CNT, 0);
- resumeCnt = prefs.getInt(RESUME_CNT, 0);
- pauseCnt = prefs.getInt(PAUSE_CNT, 0);
- stopCnt = prefs.getInt(STOP_CNT, 0);
- destroyCnt = prefs.getInt(DESTROY_CNT, 0);
- buttonPushCnt = prefs.getInt(BUTTON_CNT, 0);
- }
- private void displayAll(){
- textViewLifecycleCnt.setText("Create Counter = "+createCnt
- +"\nStart Counter = "+startCnt
- +"\nResume Counter = "+resumeCnt
- +"\nPause Counter = "+pauseCnt
- +"\nStop Counter = "+stopCnt
- +"\nDestroy Counter = "+destroyCnt);
- textViewButtonPushCnt.setText("Push Button Counter = "+buttonPushCnt);
- }
- }
נתרכז בשתי המתודות שעוסקות ב-Shared Preferences:
- restoreAll
- saveAll
- private void restoreAll(){
- // Demo API 1:
- Context context = getApplicationContext();
- prefsDefault = PreferenceManager.getDefaultSharedPreferences(context);
- createCnt = prefsDefault.getInt(CREATE_CNT, 0);
- // Demo API 2:
- prefs = getSharedPreferences(PREFS_NAME, 0);
- startCnt = prefs.getInt(START_CNT, 0);
- resumeCnt = prefs.getInt(RESUME_CNT, 0);
- pauseCnt = prefs.getInt(PAUSE_CNT, 0);
- stopCnt = prefs.getInt(STOP_CNT, 0);
- destroyCnt = prefs.getInt(DESTROY_CNT, 0);
- buttonPushCnt = prefs.getInt(BUTTON_CNT, 0);
- }
כמו שצוין קודם, מודגמים שני API לשמירה ב-preferences.
- ה-API הראשון (שורות 2-5), מטפל (=שומר) במונה אחד.
- ה-API השני מטפל ביתר 7 המונים.
- ה-DefaultSharedPreferences משתמש בקובץ יחיד משותף לכל האפליקציה. מביאים אותו תוך שימוש ב-context.
- שורה 3: ה-key, במקרה הזה הוא CREATE_CNT, והוא מתייחס למונה של מתודת onCreate.
שורה 3: אם לא קיים בקובץ רקורד עם key=CREATE_CNT, הוא ייוצר כעת. הפרמטר השני של המתודה getInt, הוא הערך שיוחזר במקרה שהרקורד לא היה קיים - במקרה הזה 0.
- Context context = getApplicationContext();
- prefsDefault = PreferenceManager.getDefaultSharedPreferences(context);
- createCnt = prefsDefault.getInt(CREATE_CNT, 0);
ה-API השני מטפל ביתר ששת הפרמטרים:
- prefs = getSharedPreferences(PREFS_NAME, 0);
- startCnt = prefs.getInt(START_CNT, 0);
- resumeCnt = prefs.getInt(RESUME_CNT, 0);
- pauseCnt = prefs.getInt(PAUSE_CNT, 0);
- stopCnt = prefs.getInt(STOP_CNT, 0);
- destroyCnt = prefs.getInt(DESTROY_CNT, 0);
- buttonPushCnt = prefs.getInt(BUTTON_CNT, 0);
שורה מס 1: מביאה את הקובץ:
ה-API מקבל שני פרמטרים:
ה-API מקבל שני פרמטרים:
- שם הקובץ.
- ה-security mode, יכול - תאורטית - לקבל 3 ערכים:
- 0 או MODE PRIVATE
- MODE_WORLD_WRITEABLE
- MODE_WORL_READABLE
שורות 2-7 שולפות מהקובץ את ששת ה-counters שנישמרים בו.
- גם כאן, הגישה היא ע"י ה-key.
- אם אין רקורד מתאים, הוא ייוצר עם הפקודה - getInt (או getFloat, getString, getBoolean וכדומה). במקרה זה, ערך שיוחזר הוא הפרמטר השני במתודה get - אצלנו שמנו תמיד אפס.
saveAll
- private void saveAll(){
- // Demonstrate both API: getDefaultSharedPreferences and getSharedPreferences
- // (Each API could do the job by itself)
- // Demo API 1:
- Context context = getApplicationContext();
- prefsDefault = PreferenceManager.getDefaultSharedPreferences(context);
- Editor editor1 = prefs.edit();
- editor1.putInt(CREATE_CNT, createCnt);
- editor1.commit();
- // Demo API 2:
- prefs = getSharedPreferences(PREFS_NAME, MODE_WORLD_WRITEABLE);
- Editor editor = prefs.edit();
- editor.putInt(CREATE_CNT, createCnt);
- editor.putInt(START_CNT, startCnt);
- editor.putInt(RESUME_CNT, resumeCnt);
- editor.putInt(PAUSE_CNT, pauseCnt);
- editor.putInt(STOP_CNT, stopCnt);
- editor.putInt(DESTROY_CNT, destroyCnt);
- editor.putInt(BUTTON_CNT, buttonPushCnt);
- editor.commit();
- }
בהתאם למתודה הקודמת, restoreAll, שמירת הפרמטרים נעשית תוך הדגמת שתי השיטות:
- שורות 5-9: מונה ה-onCreate עם ה-getDefaultSharedPreferences
- שורות 11-20: יתר ששת המונים עם getSharedPreferences.
שורה 7\שורה 12: יצירת אובייקט edit
שורה 8\שורות 13-19: שמירת הפרמטרים.
שורה 9\שורה 20: הפעלת commit. בלי commit השמירה לא תתבצע.
לא נתעכב על ה-Activity השני, היות שהוא דומה מאד לראשון הנ"ל.
ה-UI שלו כולל:
- כפתור שלחיצה עליו תפעיל את ה-Activity הראשי. שוב, ע"י שליחת intent.
- תצוגה של ה-counters שנשלפים מתוך ה-preferences.
בכל זאת הנה ה-Activity:
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class SecondActivity extends Activity {
Intent intent;
Integer createCnt;
Integer startCnt;
Integer resumeCnt;
Integer pauseCnt;
Integer stopCnt;
Integer destroyCnt;
Integer buttonPushCnt;
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setContentView(R.layout.main2);
Button button = (Button)findViewById(R.id.button_push);
restoreAll();
displayAll();
intent = new Intent(this, SimpleSharedPrefs.class);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(intent);
}
});
}
private void restoreAll(){
// Demo API 1:
Context context = getApplicationContext();
SharedPreferences prefsDefault = PreferenceManager.getDefaultSharedPreferences(context);
createCnt = prefsDefault.getInt(SimpleSharedPrefs.CREATE_CNT, 0);
// Demo API 2:
SharedPreferences prefs = getSharedPreferences(SimpleSharedPrefs.PREFS_NAME, MODE_WORLD_WRITEABLE);
startCnt = prefs.getInt(SimpleSharedPrefs.START_CNT, 0);
resumeCnt = prefs.getInt(SimpleSharedPrefs.RESUME_CNT, 0);
pauseCnt = prefs.getInt(SimpleSharedPrefs.PAUSE_CNT, 0);
stopCnt = prefs.getInt(SimpleSharedPrefs.STOP_CNT, 0);
destroyCnt = prefs.getInt(SimpleSharedPrefs.DESTROY_CNT, 0);
buttonPushCnt = prefs.getInt(SimpleSharedPrefs.BUTTON_CNT, 0);
}
private void displayAll(){
TextView textView = (TextView)findViewById(R.id.text_view);
textView.setText("Create Counter = "+createCnt
+"\nStart Counter = "+startCnt
+"\nResume Counter = "+resumeCnt
+"\nPause Counter = "+pauseCnt
+"\nStop Counter = "+stopCnt
+"\nDestroy Counter = "+destroyCnt
+"\nPush=Button Counter = "+buttonPushCnt
+"\nPush Button Counter = "+buttonPushCnt);
}
}
אחי קודם כל תודה על כל ההשקעה זה באמת מדריך מושקע ומפורט...כל הכבוד!! דבר שני רציתי לשאול אותך איך אני יכול לדעת לאיזה ACTIVITY שייך כל קובץ JAVA זאת אומרת לאיזה XML אני רוצה לשמור נתונים מ DATAPICKER ו TIMEPICKER ולהציג אותם בACTIVITY חדש בטבלה יש לך מושג איל אני יכול לעשות את זה?? תודה איתן.
השבמחקלמרות שזה פוסט יישן תודה רבה :-) מעניין מאוד
השבמחק