一、SharedPreferences简介
SharedPreferences是基础key-value(键值对)来存储一些轻量数据的存储方式,特别适用于保存软件配置参数。使用SharedPreferences 保存数据,其背后是用xml文件存放数据,文件存放在/data/data/<
package name>
/shared_prefs。
SharedPreferences主要特点:
- 适合于少量数据的保存 ;
- 保存的数据只支持Java基本数据类型,不支持自定义数据类型;
- 使用简单。
二、使用方法
1)获取SharedPreferences实例
SharedPreferences本身是一个接口,程序无法直接创建SharedPreferences实例,只能通过Context提供的getSharedPreferences(String name,int mode)方法来获取SharedPreferences实例。例如:
// 获取只能被本应用程序读、写的SharedPreferences对象
SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);
第一个参数是生成xxx.xml文件的文件名字,第二个参数是文件的访问权限以及文件数据写入方式。
简单说明一下几种方式:
Context.MODE_PRIVATE: //指定该SharedPreferences数据只能被本应用程序读写。
Context.MODE_WORLD_READABLE: //指定该SharedPreferences数据能被其他应用程序读,但不能写。
Context.MODE_WORLD_WRITEABLE: //指定该SharedPreferences数据能被其他应用程序读写。
注意:从Android4.2开始, Android不再推荐使用 MODE_WORLD_READABLE、MODE_WORLD_WRITEABLE这两种模式, 因为这两种模式允许其他应用程序来读或写本应用创建的数据, 因此容易导致安全漏洞 。 如果应用程序确实需要把内部数据暴露出来供其他应用访司, 则应该使用 ContentProvider。
2)写入数据
方法一:
//获取SharedPreferences实例
SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);
//实例化SharedPreferences.Editor对象
editor = preferences.edit();
// 存入用户姓名
editor.putString("name", "yourname");
//存入用户年龄
editor.putInt("age",18);
// 提交所有存入的数据
editor.commit();
方法二:
SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);
preferences.edit()
.putString("name", "yourname")
.putInt("age", 18)
.commit();
切记:不要写成下面的形式,会导致数据无法存储。
SharedPreferences sp = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);
sp.edit().putString("name", "yourname");
sp.edit().putInt("age", 18);
sp.edit().commit();
为什么这种方式无法存储? 因为sp.edit()每次都会返回一个新的Editor对象,Editor的实现类EditorImpl里面会有一个缓存的Map,最后commit的时候先将缓存里面的Map写入内存中的Map,然后将内存中的Map写进XML文件中。使用上面的方式commit,由于sp.edit()又重新返回了一个新的Editor对象,缓存中的Map是空的,所以导致数据无法被存储。
3)读取数据
//获取SharedPreferences实例
SharedPreferences preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);
// 读取字符串数据
String name = preferences.getString("name", "");
// 读取int类型的数据
int age = preferences.getInt("age", 0);
getSharedPreferences的具体实现是frameworks/base/core/java/android/app/ContextImpl.java,代码如下:
@Override
public SharedPreferences getSharedPreferences(String name, int mode) {
SharedPreferencesImpl sp;
synchronized (ContextImpl.class) {
......
final String packageName = getPackageName();
ArrayMap<String, SharedPreferencesImpl> packagePrefs = sSharedPrefs.get(packageName);
if (packagePrefs == null) {
packagePrefs = new ArrayMap<String, SharedPreferencesImpl>();
sSharedPrefs.put(packageName, packagePrefs);
}
......
sp = packagePrefs.get(name);
if (sp == null) {
File prefsFile = getSharedPrefsFile(name);
sp = new SharedPreferencesImpl(prefsFile, mode);
packagePrefs.put(name, sp);
return sp;
}
}
......
return sp;
}
SharedPreferencesImpl是SharedPreferences接口的具体实现类,一个name对应一个SharedPreferencesImpl,一个应用程序中根据name的不同会有多个SharedPreferencesImpl。
SharedPreferencesImpl的具体实现是在frameworks/base/core/java/android/app/SharedPreferencesImpl.java,我们可以通过getSharedPreferences获得SharedPreferences的实例,当我们调用sp.getString等get方法取数据时,实际上是直接从内存中的Map里面去取,get方法传入的第一个参数正好是Map的key,第二个参数是当Map中没有这个key对应值的时候,返回的默认值。
4)监听数据变化
SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
}
};
preferences.registerOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);//注册数据变化监听
preferences.unregisterOnSharedPreferenceChangeListener(onSharedPreferenceChangeListener);//解注册数据变化监听
5)具体写入文件的存储结构
存储在文件中就是这样的,纯明文存储没有一点安全性可言。即使设置了Activity.MODE_PRIVATE权限在ROOT权限面前也是不堪一击的。所以我们在使用SharedPreferences的时候尽量不要存储一些有关用户信息的数据,如果要存储那该怎么补救呢?我们可以把key和value的值采用加密算法加密一下。
三、使用示例
简单设计一个应用,让用户输入姓名和年龄,然后将数据保存到SharedPreferences,之后读取SharedPreferences数据并在界面上显示出来。当用户修改数据重新保存以后,提示SharedPreferences数据发生变化。界面如下图:
首先是布局文件,
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="50dp"
android:paddingLeft="100dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/name"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<EditText
android:id="@+id/etWriteName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/write_name"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/age"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<EditText
android:id="@+id/etWriteAge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/write_age"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
<Button
android:id="@+id/write"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/write"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<Button
android:id="@+id/read"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/read"
android:textAppearance="?android:attr/textAppearanceLarge"/>
<TextView
android:id="@+id/tvReadData"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"/>
</LinearLayout>
然后是主程序代码,
package cl.fjnu.edu.cn.sharedpreferencestest;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity
{
SharedPreferences preferences;
SharedPreferences.Editor editor;
private EditText etWriteName;
private EditText etWriteAge;
private TextView tvRead;
private Button read;
private Button write;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
etWriteName = (EditText) findViewById(R.id.etWriteName);
etWriteAge = (EditText) findViewById(R.id.etWriteAge);
tvRead = (TextView) findViewById(R.id.tvReadData);
write = (Button) findViewById(R.id.write);
read = (Button) findViewById(R.id.read);
//获取只能被本应用程序读写的SharedPreferences对象,
//保存的文件名为sharedPrefFile.xml
preferences = getSharedPreferences("sharedPrefFile", Context.MODE_PRIVATE);
//实例化SharedPreferences.Editor对象
editor = preferences.edit();
//注册数据变化监听
preferences.registerOnSharedPreferenceChangeListener(
onSharedPreferenceChangeListener);
write.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
// 存入用户姓名
editor.putString("name", etWriteName.getText().toString());
//存入用户年龄
editor.putInt("age",Integer.parseInt(etWriteAge.getText().toString()));
// 提交所有存入的数据
editor.commit();
}
});
read.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View arg0)
{
// 读取字符串数据
String name = preferences.getString("name", "");
// 读取int类型的数据
int age = preferences.getInt("age", 0);
//显示读取出来的数据
tvRead.setText("姓名: "+name+"\n年龄: "+age);
}
});
}
//数据变化监听器
SharedPreferences.OnSharedPreferenceChangeListener onSharedPreferenceChangeListener
= new SharedPreferences.OnSharedPreferenceChangeListener() {
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
Toast.makeText(MainActivity.this,"sharedPrefTest文件数据发生了变化!",Toast.LENGTH_SHORT).show();
}
};
}
运行效果:
在姓名和年龄输入框分别输入数据,然后点击保存数据,再读取数据,可以看到下方显示出用户保存的SharedPreferences数据。如下图,
修改输入数据,点击保存数据重新保存,则弹出Toast消息,提示SharedPreferences数据已发生变化。如下图所示,
此时再点击读取数据,则会显示出更新过的SharedPreferences数据。如下图,