[ Android 五种数据存储方式之一 ] —— SharedPreferences存储数据
SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。
主要是保存一些常用的配置比如窗口状态,一般在Activity中 重载窗口状态onSaveInstanceState保存一般使用SharedPreferences完成,它提供了Android平台常规的Long长 整形、Int整形、String字符串型的保存。
SharedPreferences保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<package name>/shared_prefs目录下:
分析以下几个方法:
Android中得到SharedPreference的方式有四种:
- ContextWrapper.getSharedPreferences(String name, int mode)
- Activity.getPreferences(int mode)
- PreferenceManager.getSharedPreferences()
- PreferenceManager.getDefaultSharedPreferences(Context context)
下面我们一起来跟踪下它们的源码:
1.ContextWrapper.getSharedPreferences(String name,int mode)
可以看出,我们可以自己设置SharedPreference的名字与模式
一、getSharedPreferences(name,mode)
方法的第一个参数用于指定该文件的名称,名称不用带后缀,后缀会由Android自动加上;
方法的第二个参数指定文件的操作模式,共有四种操作模式。
四种操作模式分别为:
1. MODE_APPEND: 追加方式存储
2. MODE_PRIVATE: 私有方式存储,其他应用无法访问
3. MODE_WORLD_READABLE: 表示当前文件可以被其他应用读取
4. MODE_WORLD_WRITEABLE: 表示当前文件可以被其他应用写入
二、edit()方法获取editor对象
Editor editor = sharedPreferences.edit();
editor存储对象采用key-value键值对进行存放,editor.putString("name", "wujaycode");
通过commit()方法提交数据
与之对应的获取数据的方法:
SharedPreferences share=getSharedPreferences("Acitivity",Activity.MODE_WORLD_READABLE);
int i=share.getInt("i",0);
String str=share.getString("str","");
boolean flag=share.getBoolean("flag",false);
getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值
如果你想要删除通过SharedPreferences产生的文件,可以通过以下方法:
File file= new File("/data/data/"+getPackageName().toString()+"/shared_prefs","Activity.xml");
if(file.exists()){
file.delete();
Toast.makeText(TestActivity.this, "删除成功", Toast.LENGTH_LONG).show(); }
三、访问其他应用中的Preference
如果要访问其他应用中的Preference,必须满足的条件是,要访问的应用的Preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。
举例,假如有个<package name>为com.wujay.action下面的应用使用了下面语句创建了Preference,getSharedPreferences("wujay", Context.MODE_WORLD_READABLE),
现在要访问该Preferences:
首先,需要创建上面的Context,然后通过Context访问Preferences,访问preference时会在应用所在包下的shared_prefs目录找到preference:
Context otherAppsContext = createPackageContext("com.wujay.action", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("wujay", Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
如果不通过创建Context访问其他应用的preference,可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:
File xmlFile = new File(“/data/data/<package name>/shared_prefs/itcast.xml”);//<package name>应替换成应用的包名。
2.Activity.getPreferences(int mode)
这种方式下,name是通过getLocalClassName()得到的,不能让我们自己设置,而且最终会调到ContextWrapper的getSharedPreferences(),getLocalClassName()定义如下:
3.PreferenceManager.getSharedPreferences()
这种方式我们是让PreferenceManager给我们维护一个SharedPreference,当然我们可以调用PreferenceManager的API来设置name和mode,并且最终也是调用到ContextWrapper的getSharedPreferences
4.PreferenceManager.getDefaultSharedPreferences(Context context)
这种方式得到的SharedPreference是某个包名下共享的,并且是私有的,不能让其他的包访问,而且Name和mode不能设置。最终也会调用到ContextWrapper的getSharedPreferences
总结:
使用方法
1、存数据
1 SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE); 2 sp.edit().putString("name", "小张").putInt("age", 11).commit();
或者下面的写法也可以
1 SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE); 2 Editor editor = sp.edit(); 3 editor.putString("name", "小张"); 4 editor.putInt("age", 11); 5 editor.commit();
切记不要写成下面的形式,会导致数据无法存储
1 SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE); 2 sp.edit().putString("name", "小张"); 3 sp.edit().putInt("age", 11); 4 sp.edit().commit();
为什么这种方式无法存储,因为sp.edit()每次都会返回一个新的Editor对象,Editor的实现类EditorImpl里面会有一个缓存的Map,最后commit的时候先将缓存里面的Map写入内存中的Map,然后将内存中的Map写进XML文件中。使用上面的方式commit,由于sp.edit()又重新返回了一个新的Editor对象,缓存中的Map是空的,所以导致数据无法被存储。
2、取数据
1 SharedPreferences sp = getSharedPreferences("sp_demo", Context.MODE_PRIVATE); 2 String name = sp.getString("name", null); 3 int age = sp.getInt("age", 0);
getSharedPreferences的具体实现是在frameworks/base/core/java/android/app/ContextImpl.java,代码如下:
1 @Override 2 public SharedPreferences getSharedPreferences(String name, int mode) { 3 SharedPreferencesImpl sp; 4 synchronized (ContextImpl.class) { 5 ...... 6 final String packageName = getPackageName(); 7 ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName); 8 if (packagePrefs == null) { 9 packagePrefs = new ArrayMap<String, SharedPreferencesImpl>(); 10 sSharedPrefs.put(packageName, packagePrefs); 11 } 12 13 ...... 14 sp = packagePrefs.get(name); 15 if (sp == null) { 16 File prefsFile = getSharedPrefsFile(name); 17 sp = new SharedPreferencesImpl(prefsFile, mode); 18 packagePrefs.put(name, sp); 19 return sp; 20 } 21 } 22 ...... 23 return sp; 24 }
SharedPreferencesImpl是SharedPreferences接口的具体实现类,一个name对应一个SharedPreferencesImpl,一个应用程序中根据name的不同会有多个SharedPreferencesImpl。
SharedPreferencesImpl的具体实现是在frameworks/base/core/java/android/app/SharedPreferencesImpl.java,我们可以通过getSharedPreferences获得
SharedPreferences sharedPreferences = getSharedPreferences("ljq", Context.MODE_PRIVATE);
//getString()第二个参数为缺省值,如果preference中不存在该key,将返回缺省值
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 1);
如果访问其他应用中的Preference,前提条件是:该preference创建时指定了Context.MODE_WORLD_READABLE或者Context.MODE_WORLD_WRITEABLE权限。
如:有个<package name>为com.ljq.action的应用使用下面语句创建了preference。
getSharedPreferences("ljq", Context.MODE_WORLD_READABLE);
其他应用要访问上面应用的preference,首先需要创建上面应用的Context,然后通过Context 访问preference ,访问preference时会在应用所在包下的shared_prefs目录找到preference :
Context otherAppsContext = createPackageContext("com.ljq.action", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = otherAppsContext.getSharedPreferences("ljq", Context.MODE_WORLD_READABLE);
String name = sharedPreferences.getString("name", "");
int age = sharedPreferences.getInt("age", 0);
如果不通过创建Context访问其他应用的preference,也可以以读取xml文件方式直接访问其他应用preference对应的xml文件,如:
File xmlFile = new File("/data/data/<package name>/shared_prefs/itcast.xml");//<package name>应替换成应用的包名
案例:
string.xml文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <resources> 3 <string name="hello">Hello World, SpActivity!</string> 4 <string name="app_name">软件配置参数</string> 5 <string name="name">姓名</string> 6 <string name="age">年龄</string> 7 <string name="button">保存设置</string> 8 <string name="showButton">显示</string> 9 </resources>
main.xml布局文件
1 <?xml version="1.0" encoding="utf-8"?> 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:orientation="vertical" 4 android:layout_width="fill_parent" 5 android:layout_height="fill_parent"> 6 <RelativeLayout 7 xmlns:android="http://schemas.android.com/apk/res/android" 8 android:layout_width="wrap_content" 9 android:layout_height="wrap_content"> 10 <TextView android:layout_width="wrap_content" 11 android:layout_height="wrap_content" 12 android:text="@string/name" 13 android:textSize="20px" 14 android:id="@+id/nameLable" /> 15 <EditText android:layout_width="80px" 16 android:layout_height="wrap_content" 17 android:layout_toRightOf="@id/nameLable" 18 android:layout_alignTop="@id/nameLable" 19 android:layout_marginLeft="10px" 20 android:id="@+id/name" /> 21 </RelativeLayout> 22 <RelativeLayout 23 xmlns:android="http://schemas.android.com/apk/res/android" 24 android:layout_width="wrap_content" 25 android:layout_height="wrap_content"> 26 <TextView android:layout_width="wrap_content" 27 android:layout_height="wrap_content" 28 android:textSize="20px" 29 android:text="@string/age" 30 android:id="@+id/ageLable" /> 31 <EditText android:layout_width="80px" 32 android:layout_height="wrap_content" 33 android:layout_toRightOf="@id/ageLable" 34 android:layout_alignTop="@id/ageLable" 35 android:layout_marginLeft="10px" 36 android:id="@+id/age" /> 37 </RelativeLayout> 38 <RelativeLayout 39 xmlns:android="http://schemas.android.com/apk/res/android" 40 android:layout_width="wrap_content" 41 android:layout_height="wrap_content"> 42 <Button android:layout_width="wrap_content" 43 android:layout_height="wrap_content" 44 android:text="@string/button" 45 android:id="@+id/button" /> 46 <Button android:layout_width="wrap_content" 47 android:layout_height="wrap_content" 48 android:text="@string/showButton" 49 android:layout_toRightOf="@id/button" 50 android:layout_alignTop="@id/button" 51 android:id="@+id/showButton" /> 52 </RelativeLayout> 53 <TextView android:layout_width="fill_parent" 54 android:layout_height="wrap_content" 55 android:textSize="20px" 56 android:id="@+id/showText" /> 57 </LinearLayout>
1 package com.ljq.activity; 2 3 import android.app.Activity; 4 import android.content.Context; 5 import android.content.SharedPreferences; 6 import android.content.SharedPreferences.Editor; 7 import android.os.Bundle; 8 import android.view.View; 9 import android.widget.Button; 10 import android.widget.EditText; 11 import android.widget.TextView; 12 import android.widget.Toast; 13 14 public class SpActivity extends Activity { 15 private EditText nameText; 16 private EditText ageText; 17 private TextView resultText; 18 @Override 19 public void onCreate(Bundle savedInstanceState) { 20 super.onCreate(savedInstanceState); 21 setContentView(R.layout.main); 22 23 nameText = (EditText)this.findViewById(R.id.name); 24 ageText = (EditText)this.findViewById(R.id.age); 25 resultText = (TextView)this.findViewById(R.id.showText); 26 27 Button button = (Button)this.findViewById(R.id.button); 28 Button showButton = (Button)this.findViewById(R.id.showButton); 29 button.setOnClickListener(listener); 30 showButton.setOnClickListener(listener); 31 32 // 回显 33 SharedPreferences sharedPreferences=getSharedPreferences("ljq123", 34 Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE); 35 String nameValue = sharedPreferences.getString("name", ""); 36 int ageValue = sharedPreferences.getInt("age", 1); 37 nameText.setText(nameValue); 38 ageText.setText(String.valueOf(ageValue)); 39 } 40 41 private View.OnClickListener listener = new View.OnClickListener(){ 42 public void onClick(View v) { 43 Button button = (Button)v; 44 //ljq123文件存放在/data/data/<package name>/shared_prefs目录下 45 SharedPreferences sharedPreferences=getSharedPreferences("ljq123", 46 Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE); 47 switch (button.getId()) { 48 case R.id.button: 49 String name = nameText.getText().toString(); 50 int age = Integer.parseInt(ageText.getText().toString()); 51 Editor editor = sharedPreferences.edit(); //获取编辑器 52 editor.putString("name", name); 53 editor.putInt("age", age); 54 editor.commit();//提交修改 55 Toast.makeText(SpActivity.this, "保存成功", Toast.LENGTH_LONG).show(); 56 break; 57 case R.id.showButton: 58 String nameValue = sharedPreferences.getString("name", ""); 59 int ageValue = sharedPreferences.getInt("age", 1); 60 resultText.setText("姓名:" + nameValue + ",年龄:" + ageValue); 61 break; 62 } 63 } 64 }; 65 }
运行结果
如何访问其他应用中的Preference?
1 package com.ljq.sp; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 6 import android.content.Context; 7 import android.content.SharedPreferences; 8 import android.test.AndroidTestCase; 9 import android.util.Log; 10 11 public class AccessSharePreferenceTest extends AndroidTestCase{ 12 private static final String TAG = "AccessSharePreferenceTest"; 13 14 /** 15 * 访问SharePreference的方式一,注:权限要足够 16 * @throws Exception 17 */ 18 public void testAccessPreference() throws Exception{ 19 String path = "/data/data/com.ljq.activity/shared_prefs/ljq123.xml"; 20 File file = new File(path); 21 FileInputStream inputStream = new FileInputStream(file); 22 //获取的是一个xml字符串 23 String data = new FileService().read(inputStream); 24 Log.i(TAG, data); 25 } 26 27 /** 28 * 访问SharePreference的方式二,注:权限要足够 29 * @throws Exception 30 */ 31 public void testAccessPreference2() throws Exception{ 32 Context context = this.getContext().createPackageContext("com.ljq.activity", 33 Context.CONTEXT_IGNORE_SECURITY); 34 SharedPreferences sharedPreferences = context.getSharedPreferences("ljq123", 35 Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE); 36 String name = sharedPreferences.getString("name", ""); 37 int age = sharedPreferences.getInt("age", 1); 38 Log.i(TAG, name + " : " +age); 39 } 40 }