1,概念

SharedPreferences是Android平台上一个轻量级的存储类,用来保存应用的一些常用配置,以XML文件的形式存储的键值对(key-value)数据的数据存储方式保存在 /data/data/PACKAGE_NAME/shared_prefs目录下。

2,场景

一般用于储存应用的配置等信息。
注意:SharedPreferences文件并不安全,手机root后即可查看其信息,请勿存储用户信息、密码等敏感信息。(可以不存放密码,存放用户名,登录时检测有用户名则直接登录,可限定日期)

1)少量数据的保存
2)数据格式简单(基本数据类型)
如配置信息(是否打开音效、是否使用震动效果)、游戏的积分信息。
如各个界面需要相互传递的信息(用户名、用户头像修改、传值过于麻烦,直接存储在SharedPreferences中)
3)通过key-value的方式保存数据

3,实现

1)SharePreferences对象

通过Context上下文获取。

SharedPreferences preferences=getSharedPreferences("user",Context.MODE_PRIVATE);

--Use 0 or MODE_PRIVATE 默认设置,只能被本应用读写
--MODE_WORLD_READABLE   被其他应用程序读,不可写
--MODE_WORLD_WRITEABLE  被其他应用程序读写

2)Editor对象

调用SharePreferences的edit()方法返回一个Editor对象。
abstract SharedPreferences.Editor edit()

Editor editor=preferences.edit();

3)设置数据

通过Editor的putXXX(key , value)方法。

String name="xixi";
String age="22";
editor.putString("name", name);
editor.putString("age", age);

4)关闭对象

通过Editor的 commit(); 方法。

editor.commit();//返回boolean

5)其他

contains(String key)  判定SharedPreference是否包含key对应的值
Map<String, ?> getAll()  获取SharePreference里面全部的数据对
SharedPreferences.Editor remove(String key) 移除
SharedPreferences.Editor clear() 清除所有的数据

4,优缺点

1)优点

①可以全局共享访问
②对于常规的轻量级而言比SQLite好一点。
当存储量不大的时候可以考虑自己定义文件格式。

2)缺点

①保存为xml文件,整体效率来看不是特别的高,但却高于SQLit

xml处理时Dalvik会通过自带底层的本地XML Parser解析,比如XMLpull方式,这样对于内存资源占用比较好。

②安全性较差,请勿保存敏感信息

③对于较大的存储文件,会造成界面卡顿

SharedPreference在创建时会把整个文件全部加载进内存,如果文件较大,会出现以下问题:
i>第一次从sp中获取值的时候,有可能阻塞主线程,使界面卡顿、掉帧。
ii>解析sp的时候会产生大量的临时对象,导致频繁GC,引起界面卡顿。
iii>这些key和value会永远存在于内存之中,占用大量内存。

5,安全性

1)开发者在创建文件时务必选取合适的创建模式(MODE_PRIVATE、MODE_WORLD_READABLE以及MODE_WORLD_WRITEABLE)进行权限控制。
2)避免使用MODE_WORLD_WRITEABLE和MODE_WORLD_READABLE模式创建进程间通信的文件,此处即为Shared Preferences;如果需要与其他进程应用进行数据共享,请考虑使用content provider。
3)不要在使用“android:sharedUserId”属性的同时,对应用使用测试签名,否则其他应用拥有“android:sharedUserId”属性值和测试签名时,将会访问到内部存储文件数据。

6,优化

1)不要存放大的key和value,会引起界面卡、频繁GC、占用内存等等。
2)毫不相关的配置项就不要放在在一起,文件越大读取越慢。
3)读取频繁的key和不易变动的key尽量不要放在一起,影响速度,如果整个文件很小,那么忽略吧,为了这点性能添加维护成本得不偿失。
4)不要乱edit和apply,尽量批量修改一次提交,多次apply会阻塞主线程。
5)尽量不要存放JSON和HTML,这种场景请直接使用JSON。
6)SharedPreference无法进行跨进程通信,MODE_MULTI_PROCESS只是保证了在API 11以前的系统上,如果sp已经被读取进内存,再次获取这个SharedPreference的时候,如果有这个flag,会重新读一遍文件,仅此而已。

7,demo

1)简单用法

//FILE_BD  保存账户绑定信息
    SharedPreferences preferences_BD = null;
    String FILE_BD = "saveUserBind";//用于保存账户绑定信息
    ...
    //读入信息
    void PutScopeClose(){
        if (preferences_BD == null) {
            preferences_BD = mContext.getSharedPreferences(FILE_BD, MODE_PRIVATE);          
        }           
        Editor editPhone = preferences_BD.edit();
        switch (arr[1]) {
        case "onebind":
            editPhone.putString("Scope_Close1", arr[2]);
            break;
        case "twobind":
            editPhone.putString("Scope_Close2", arr[2]);
            break;
        }
        editPhone.commit();
    }
    //获取开启关闭安全距离信息
    String GetScopeClose(){
        String sendStr="";
        preferences_BD = mContext.getSharedPreferences(FILE_BD, MODE_PRIVATE);

        sendStr = preferences_BD.getString("Scope_Close1", "close") + "," + 
                preferences_BD.getString("Scope_Close2", "close") + "," + ":end" + ",";
        return sendStr;
    }

2)封装SharePreferenceUtil

public class SharePreferenceUtil {

    public static boolean isLogin(SharedPreferences sp){
        return sp.getBoolean("isLogin", false);
    }

    public static SharedPreferences getSharePerference(Context context){
        return context.getSharedPreferences(VariableUtil.APPLICATION_NAME, Context.MODE_PRIVATE);
    }

    public static void setStringSharedPerference(SharedPreferences sp,String key,String value){
        Editor editor=sp.edit();
        editor.putString(key, value);
        editor.commit();
    }
    public static void setStrsSharedPerference(SharedPreferences sp,String[] key,String[] value){
        Editor editor=sp.edit();
        for(int i=0;i<key.length;i++){
            editor.putString(key[i], value[i]);
            LogUtils.i(tag, " setStrsSharedPerference: " + key[i] + "," + value[i]);
        }
        editor.commit();
    }

    public static void removeStrsSharedPerference(SharedPreferences sp,String[] key){
        Editor editor=sp.edit();
        for(int i=0;i<key.length;i++){
            editor.remove(key[i]);
            LogUtils.i(tag, " removeStrsSharedPerference: " + key[i]);
        }
        editor.commit();
    }

    public static void setBooleanSharedPerference(SharedPreferences sp,String key,boolean value){
        Editor editor=sp.edit();
        editor.putBoolean(key, value);
        editor.commit();
    }
    /**
     * 删除缓存中的图片数据
     */
    public static void removeTempFromPref(Context context,String key) {
        SharedPreferences sp = context.getSharedPreferences(VariableUtil.APPLICATION_NAME, 0);
        sp.edit().remove(key).commit();
    }
}

3)封装SPUtils

SPUtils spUtils1 = SPUtils.getInstance(Config.USER_INFO);
spUtils1.put(Config.USER_ID, id);//Config为常量bean
import android.content.Context;
import android.content.SharedPreferences;
import android.support.annotation.NonNull;
public class SPUtils {

    private static Map<String, SPUtils> sSPMap = new HashMap<>();
    private SharedPreferences sp;

    /**
     * 获取SP实例
     *
     * @return {@link SPUtils}
     */
    public static SPUtils getInstance() {
        return getInstance("");
    }

    /**
     * 获取SP实例
     *
     * @param spName sp名
     * @return {@link SPUtils}
     */
    public static SPUtils getInstance(String spName) {
        if (isSpace(spName)) spName = "spUtils";
        SPUtils sp = sSPMap.get(spName);
        if (sp == null) {
            sp = new SPUtils(spName);
            sSPMap.put(spName, sp);
        }
        return sp;
    }

    private SPUtils(String spName) {
        sp = MyApplication.context.getSharedPreferences(spName, Context.MODE_PRIVATE);
    }

    /**
     * SP中写入String
     *
     * @param key   键
     * @param value 值
     */
    public void put(@NonNull String key, @NonNull String value) {
        sp.edit().putString(key, value).apply();
    }

    /**
     * SP中读取String
     *
     * @param key 键
     * @return 存在返回对应值,不存在返回默认值{@code null}
     */
    public String getString(@NonNull String key) {
        return getString(key, "");
    }

    /**
     * SP中读取String
     *
     * @param key          键
     * @param defaultValue 默认值
     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
     */
    public String getString(@NonNull String key, @NonNull String defaultValue) {
        return sp.getString(key, defaultValue);
    }

    /**
     * SP中写入int
     *
     * @param key   键
     * @param value 值
     */
    public void put(@NonNull String key, int value) {
        sp.edit().putInt(key, value).apply();
    }

    /**
     * SP中读取int
     *
     * @param key 键
     * @return 存在返回对应值,不存在返回默认值-1
     */
    public int getInt(@NonNull String key) {
        return getInt(key, -1);
    }

    /**
     * SP中读取int
     *
     * @param key          键
     * @param defaultValue 默认值
     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
     */
    public int getInt(@NonNull String key, int defaultValue) {
        return sp.getInt(key, defaultValue);
    }

    /**
     * SP中写入long
     *
     * @param key   键
     * @param value 值
     */
    public void put(@NonNull String key, long value) {
        sp.edit().putLong(key, value).apply();
    }

    /**
     * SP中读取long
     *
     * @param key 键
     * @return 存在返回对应值,不存在返回默认值-1
     */
    public long getLong(@NonNull String key) {
        return getLong(key, -1L);
    }

    /**
     * SP中读取long
     *
     * @param key          键
     * @param defaultValue 默认值
     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
     */
    public long getLong(@NonNull String key, long defaultValue) {
        return sp.getLong(key, defaultValue);
    }

    /**
     * SP中写入float
     *
     * @param key   键
     * @param value 值
     */
    public void put(@NonNull String key, float value) {
        sp.edit().putFloat(key, value).apply();
    }

    /**
     * SP中读取float
     *
     * @param key 键
     * @return 存在返回对应值,不存在返回默认值-1
     */
    public float getFloat(@NonNull String key) {
        return getFloat(key, -1f);
    }

    /**
     * SP中读取float
     *
     * @param key          键
     * @param defaultValue 默认值
     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
     */
    public float getFloat(@NonNull String key, float defaultValue) {
        return sp.getFloat(key, defaultValue);
    }

    /**
     * SP中写入boolean
     *
     * @param key   键
     * @param value 值
     */
    public void put(@NonNull String key, boolean value) {
        sp.edit().putBoolean(key, value).apply();
    }

    /**
     * SP中读取boolean
     *
     * @param key 键
     * @return 存在返回对应值,不存在返回默认值{@code false}
     */
    public boolean getBoolean(@NonNull String key) {
        return getBoolean(key, false);
    }

    /**
     * SP中读取boolean
     *
     * @param key          键
     * @param defaultValue 默认值
     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
     */
    public boolean getBoolean(@NonNull String key, boolean defaultValue) {
        return sp.getBoolean(key, defaultValue);
    }

    /**
     * SP中写入String集合
     *
     * @param key    键
     * @param values 值
     */
    public void put(@NonNull String key, @NonNull Set<String> values) {
        sp.edit().putStringSet(key, values).apply();
    }

    /**
     * SP中读取StringSet
     *
     * @param key 键
     * @return 存在返回对应值,不存在返回默认值{@code null}
     */
    public Set<String> getStringSet(@NonNull String key) {
        return getStringSet(key, Collections.<String>emptySet());
    }

    /**
     * SP中读取StringSet
     *
     * @param key          键
     * @param defaultValue 默认值
     * @return 存在返回对应值,不存在返回默认值{@code defaultValue}
     */
    public Set<String> getStringSet(@NonNull String key, @NonNull Set<String> defaultValue) {
        return sp.getStringSet(key, defaultValue);
    }

    /**
     * SP中获取所有键值对
     *
     * @return Map对象
     */
    public Map<String, ?> getAll() {
        return sp.getAll();
    }

    /**
     * SP中移除该key
     *
     * @param key 键
     */
    public void remove(@NonNull String key) {
        sp.edit().remove(key).apply();
    }

    /**
     * SP中是否存在该key
     *
     * @param key 键
     * @return {@code true}: 存在<br>{@code false}: 不存在
     */
    public boolean contains(@NonNull String key) {
        return sp.contains(key);
    }

    /**
     * SP中清除所有数据
     */
    public void clear() {
        sp.edit().clear().apply();
    }

    private static boolean isSpace(String s) {
        if (s == null) return true;
        for (int i = 0, len = s.length(); i < len; ++i) {
            if (!Character.isWhitespace(s.charAt(i))) {
                return false;
            }
        }
        return true;
    }
}