这是我最近碰到过的一个问题,之前并没有一个清晰的认识!唯一的印象是,SharedPreferences 是可以用来存取数据。直到前些日子,在360漏洞扫描应用后,才意识到这个问题,如何正确的使用SharedPreferences呢?以下是360漏洞扫描的信息:
关于SharedPreferences顺便再复习一下:
SharedPreferences 是Android 中的内置API,它允许我们存取键值对形式的基础类型数据,像:boolean,float ,int , long , string 。这些数据将会持久化的存在,即使你的应用程序结束之后(注意:如果应用从手机上写在掉后,该程序对应的SharedPreferences将会消失,保存在路径:/data/data/<package_name>/shared_prefs
目录下)
注意:User Preferences 用户配置
严格来说,SharedPreferences最好不要用来保存“用户配置”,比如,用户选择了什么铃声,是否自动更新等等。如果要为应用创建用户配置,可以使用PreferenceActivity,它可以用来创建“用户配置”。(PreferenceActivity用来创建程序中的设置界面)
如何在应用程序中获取SharedPreferences呢?有两个方法:
getSharedPreferences(name , mode) - 如果你的应用需要多个preferences文件可以使用该方法。该方法的第一个参数,用来区别不同的preferences文件。
比如:
SharedPreferences sp1 = getSharedPreferences("sp_1" , Context.MODE_PRIVATE);
SharedPreferences sp2 = getSharedPreferences("sp_2" , Context.MODE_PRIVATE);
是同一个应用程序的两个不同的配置文件。
该方法的第二个参数支持如下几种值:
Context.MODE_PRIVATE:指定该SharedPreferences数据只能被本应用程序或者是拥有相同user ID的应用读、写。
Context.MODE_WORLD_READABLE :指定该SharedPreferences数据能被其它应用程序读,但不能写。
这个变量在API 17以后已经不建议使用了。创建全局可读文件是非常危险的事,这样会引起程序的安全漏洞。程序之间的交互应该更多的使用正规的途径,比如:ContentProvider, BroadcastReceiver, and Service.当程序通过备份和恢复后,并不能保证这种获取方式依然对该文件有效。文件创建模式:允许所有的其它程序来读取创建的文件。
Context.MODE_WORLD_WRITEABLE:指定该SharedPreferences数据能被其它应用程序读,写。
这个变量在API 17以后已经不建议使用了。创建全局可写文件是非常危险的事,这样会引起程序的安全漏洞.程序之间的交互应该更多的使用正规的途径,比如:ContentProvider, BroadcastReceiver, and Service.当程序通过备份和恢复后,并不能保证这种获取方式依然对文件有效。文件创建模式:允许所有的其它程序来修改创建的文件。
Context.MODE_MULTI_PROCESS
在API11中添加,SharedPreferences加载标记:被设置后,在硬盘上的文件在被修改是将会被检查如果当前的SharedPreferences实例已经被加载进进程后。这种情况通常发生在,当程序有多个进程在修改同一个SharedPreferences文件的时候。通常来说这种方式在进程之间交互表现是非常好的。
在API 2.3之前,发布release版本时,这个标记是默认存在的,当发布release版本时。对于使用2.3以上的sdk进行编译的时候,如果想使用那么
必须明确的设置。
getPreferences() - 如果你的Activity仅仅需要一个preferences文件可以使用这个方法。因为你的Activity只要一个preferences文件,所以不需要提供名字。
如何往SharedPreferences中写数据呢?
1、需要获取SharedPreferences的编辑对象即:SharedPreferences.Editor : 使用 edit()。
比如:SharedPreferences.Editor editor = sp1.edit();
2、使用putXXX()方法,向preferences写入数据:
比如:editor.putXXX(“key” , value);
3、向preferences文件中提交数据:
比如:editor.commit();
如何读取数据呢?
使用方法getXXX()等方法读取。
比如:
boolean silent = settings.getBoolean("silentMode", false);
下面是Google doc提供的一个demo:
public class Calc extends Activity { public static final String PREFS_NAME = "MyPrefsFile"; @Override protected void onCreate(Bundle state){ super.onCreate(state); . . . // Restore preferences SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); boolean silent = settings.getBoolean("silentMode", false); setSilent(silent); } @Override protected void onStop(){ super.onStop(); // We need an Editor object to make preference changes. // All objects are from android.context.Context SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0); SharedPreferences.Editor editor = settings.edit(); editor.putBoolean("silentMode", mSilentMode); // Commit the edits! editor.commit(); } }
接下来看Google doc 中关于sharedpreferences的文档。
SharedPreferences 本身是一个接口,用来获取和修改程序的配置数据,位于:android.content.SharedPreferences。程序无法直接创建SharedPreferences实例,只能通过conext的getSharedPreferences(String, int)的获取接口实例。
注意:当前类不支持跨进程调用,将会在之后的版本中支持。
它提供了如下方法来访问perferences数据:
boolean contains(String key) :判断当前preferences是否包含key所对应的数据。
abstract SharedPreferences.Editor edit():为preferences创建一个新的Editor,因此你可以修改preferences的数据并且提交给SharedPreferences对象。
abstract Map<String, ?> getAll() :获取preferences中所包含的所有数据。
abstract XXX getXXX(String key, boolean defValue):获取perferences里指定的key对应的value。如果该key不存在,则返回默认defValue .其中XXX是基础数据类型:boolean、float、int、long、String等。
SharedPreferences接口本身并没有提供写入数据的能力,而是通过SharedPreferences的内部接口,SharedPreferences调用edit()方法即可获取它所对应的Editor对象。
Editor提供了如下方法来写向perferences入数据:
abstract SharedPreferences.Editor clear()
在Editor中标记出:清除所有preferences保存的数据。(注意此方法并没有实质性的清除,只是在Editor上进行了标记,需要commit后才可以进行真正的清除)
abstract boolean commit():
将Editor对preferences的修改提交给SharedPreferences对象,完成对preferences中数据的修改。
abstract SharedPreferences.Editor putXXX(String key, boolean value)
向preferences中存入key及其所对应的数据 <key-value>。其中xxx可以是boolean、float、int、long、String等各种基本类型的数值。
abstract SharedPreferences.Editor remove(String key):
在Editor中标记出preference中以key以键的键值对应该被移除,而实际的移除动作将会在commit()执行之后才会作用于真正的preferences。
如何读取其它应用的SharedPreferences:
如上所述,如果想读、写其它应用的SharedPreferences,前提是创建该SharedPreferences的应用程序指定相应的访问权限,例如指定了MODE_WORLD_READABLE , 这表明该SharedPreferences可被其它应用程序读取。指定了MODE_WORLD_WRITEABLE,这表明该SharedPreferences可被其它程序写入。
为了读取其它程序对应的Context,可按照如下步骤进行:
1、需要创建其它程序对应的Context,例如如下代码:
Context userCount = createPackageContext(“com.sharedpreferencesdemo.zhanggeng” , Context.CONTEXT_IGNORE_SECURITY);
上面的程序中:com.sharedpreferencesdemo.zhanggeng 就是其他程序的包名——实际上Android系统就是用应用程序的报名来作为程序的标识的。
2、调用其它程序的conext的getSharedPreferences(String name , int mode) 即可获取相应的SharedPreferences对象。
3、调用SharedPreferences的edit()方法获取相应的Editor即可。
如下demo所示:
private void readOtherAppSp() { Context useCount = null; try { //获取其它程序对应的Context useCount = createPackageContext("com.sharedpreferencesdemo.zhanggeng" , Context.CONTEXT_IGNORE_SECURITY); } catch (PackageManager.NameNotFoundException e) { e.printStackTrace(); } //使用其它程序的Context获取对应的SharedPreferences SharedPreferences prefs = useCount.getSharedPreferences("count" , Context.MODE_WORLD_READABLE); //读取数据 int count = prefs.getInt("count" , 0); //后续处理..... }
那么回到最初的问题,改如何正确的使用SharedPreferences?
1、设置相应的权限:如果只供本程序或者有相同user id的程序使用 使用Context.MODE_PRIVATE权限;如果允许其它程序可读设置权限;如果允许其它程序可写设置相应权限;
2、SharedPreferences保存的数据最好是基础数据类型(当然也可以用来保存用户配置,官方推荐PreferenceActivtiy )。
3、如果想让程序有多个preferences文件使用getSharedPreferences(xxx , xxx);如果想让程序只有一个preferences使用getPreferences()。
4、SharedPreferences最好不要用来保存多个程序交互使用的共享数据,建议使用ContentProvider, BroadcastReceiver, and Service 来完成多个程序之间的数据共享。
参考文档:
1、http://developer.android.com/reference/android/content/SharedPreferences.html