Android -- SharedPreferences保存基本数据、序列化对象、List数据

前言

Android提供了五种数据存储方式:

  • 使用 SharedPreferences 存储数据
  • 文件存储数据
  • SQLite 数据库存储方式
  • 使用 ContentProvider 存储数据
  • 网络存储数据

在 Android 下做持久化的数据存储,大部分是用 sqlite 数据库或 sharepreference。为了少写 sql 语句,大部分都是用 ORM 形式的开源数据库框架,例如 greendao 和 cupboard 或者 dao4,但在一般小型存储系统中,还是用 sharepreference 存储比较方便。

1 SharedPreferences 存储

1.1 SharedPreferences 可存储的数据类型

SharedPreferences 是 Android 平台上一个轻量级的存储辅助类,用来保存应用的一些常用配置。
SharedPreferences 可存储的数据类型有:

  • 四种基本类型数据(int,float,long,boolean)+ String
  • List类型的数据:List< String>,List< JavaBean>
  • 序列化对象
  • Map<K, V>集合

最终数据是以xml形式进行存储。

SharedPreferences 文件都是存放在 /data/data//shared_prefs/目录下的,在模拟器上可以看见该文件;在真机上,需要对真机root后,才能看到该文件。

1.2 SharedPreferences 权限
  • MODE_PRIVATE:默认操作模式,和直接传0效果相同,表示只有当前应用程序才可以对这个 SharedPreferences 文件进行读写。
  • MODE_WORLD_READABLE:指定此 SharedPreferences 对其他程序只读且无法修改。
  • MODE_WORLD_WRITEABLE:指定此 SharedPreferences 能被其他程序读写。
  • MODE_MULTI_PROCESS:Android2.3之后已经弃之不用了。

下面总结 SharedPreferences 可存储的数据类型。

1.3 存储四种基本类型数据

SharedPreferences 可存储四种基本类型数据(int,float,long,boolean)+ String。

SPUtil

/**
     * 获取SharedPreference保存的value
     * 值的类型:(int,float,long,boolean)+String
     *
     * @param context 上下文
     * @param key     储存值的key
     * @param clazz   获取值的类型   int,float,long,boolean)+String
     * @param <T>     T
     * @return 值
     */
    public static <T> T getValue(Context context, String key, Class<T> clazz) {
        if (context == null) {
            throw new RuntimeException("context is null");
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        return getValue(key, getT(clazz), sp);
    }

    /**
     * 获取SharedPreference保存的value
     * 值的类型:(int,float,long,boolean)+String
     *
     * @param key   储存值的key
     * @param T 获取值的类型   int,float,long,boolean)+String
     * @param sp    SharedPreferences
     * @param <T>   T
     * @return 值
     */
    private static <T> T getValue(String key, T t, SharedPreferences sp) {
        if (t == null) {
            return null;
        }
        if (t instanceof Integer) {
            return (T) Integer.valueOf(sp.getInt(key, 0));
        } else if (t instanceof String) {
            return (T) sp.getString(key, "");
        } else if (t instanceof Boolean) {
            return (T) Boolean.valueOf(sp.getBoolean(key, false));
        } else if (t instanceof Long) {
            return (T) Long.valueOf(sp.getLong(key, 0L));
        } else if (t instanceof Float) {
            return (T) Float.valueOf(sp.getFloat(key, 0L));
        }
        Log.e(TAG, "无法找到" + key + "对应的值");
        return null;
    }

    /**
     * 通过反射创建类实例
     * 反射有两种方式创建类实例:
     * 1、Class.newInstance():只能调用无参的构造函数(默认构造函数)
     * 2、Constructor.newInstance():可以根据传入的参数,调用任意构造构造函数。
     * Integer、Boolean、Long、Float 没有默认构造函数,只能通过 Constructor.newInstance() 调用
     * String 是有默认构造函数的,两种方法都适用
     * @param clazz  String.class、Integer.class、Boolean.class、Long.class、Float.class
     * @param <T> T
     * @return T
     */
    private static <T> T getT(Class<T> clazz) {
        T t = null;
        String clazzName = clazz.getName();
        Log.e(TAG, "基本类型名字是[" + clazzName + "]");
        try {
            if ("java.lang.Integer".equals(clazzName)) {
                t = clazz.getConstructor(int.class).newInstance(1);
            } else if ("java.lang.String".equals(clazzName)) {
                t = clazz.newInstance();
            } else if ("java.lang.Boolean".equals(clazzName)) {
                t = clazz.getConstructor(boolean.class).newInstance(false);
            } else if ("java.lang.Long".equals(clazzName)) {
                t = clazz.getConstructor(long.class).newInstance(0L);
            } else if ("java.lang.Float".equals(clazzName)) {
                t = clazz.getConstructor(float.class).newInstance(0F);
            }
        } catch (InstantiationException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        }

        return t;
    }

    /**
     * 使用SharedPreference保存value
     *
     * @param context 上下文
     * @param key     储存值的key
     * @param value   值
     */
    public static void setValue(Context context, String key, Object value) {
        if (context == null) {
            throw new RuntimeException("context is null");
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        setValue(key, value, sp);
    }

    /**
     * 使用SharedPreference保存value
     *
     * @param key   储存值的key
     * @param value 值
     * @param sp    SharedPreferences
     */
    private static void setValue(String key, Object value, SharedPreferences sp) {
        SharedPreferences.Editor editor = sp.edit();
        if (value instanceof Integer) {
            editor.putInt(key, (Integer) value);
        } else if (value instanceof String) {
            editor.putString(key, (String) value);
        } else if (value instanceof Boolean) {
            editor.putBoolean(key, (Boolean) value);
        } else if (value instanceof Long) {
            editor.putLong(key, (Long) value);
        } else if (value instanceof Float) {
            editor.putFloat(key, (Float) value);
        }
        editor.commit();
    }

注:
通过反射创建类实例
反射有两种方式创建类实例:
1、Class.newInstance():只能调用无参的构造函数(默认构造函数)
2、Constructor.newInstance():可以根据传入的参数,调用任意构造构造函数
Integer、Boolean、Long、Float 没有默认构造函数,只能通过 Constructor.newInstance() 调用
String 是有默认构造函数的,两种方法都适用

在 SPTestActivity 中使用

               //存储
               String str = etContent.getText().toString().trim();
                if (TextUtils.isEmpty(str)) {
                    ToastUtils.toast(this, "请输入内容");
                    return;
                }
//                SPUtil.setValue(this, "base_value", str);
//                SPUtil.setValue(this, "base_value_int", 2);
//                SPUtil.setValue(this, "base_value_boolean", true);
//                SPUtil.setValue(this, "base_value_long", 3L);
                SPUtil.setValue(this, "base_value_float", 3.5F);
                ToastUtils.toast(this, "数据保存成功!");


                 //读取
                String value = SPUtil.getValue(this, "base_value",  String.class);
//                int value = SPUtil.getValue(this, "base_value_int",   Integer.class);
//                boolean value = SPUtil.getValue(this, "base_value_boolean", Boolean.class);
//                long value = SPUtil.getValue(this, "base_value_long",   Long.class);
                float value = SPUtil.getValue(this, "base_value_float",   Float.class);
                ToastUtils.toast(this, String.valueOf(value));
1.4 存储序列化对象

SharedPreferences 存储对象是将对象转化为字节流,然后写入本地xml文件中; 读取时,从xml文件中读取字节流然后转化为对象。

存储对象流程:

  1. 对象必须实现 Serializable 序列化,不能使用 Parcelable 序列化,因为 out.writeObject 无法写入 Parcelable 序列化的对象
  2. 将序列化的对象转为字节流
  3. 通过将字对象进行用Base64转码,写入sp中
  4. 通过key读取到编码后的字节流,并进行Base64解码
  5. 将解码后的 String 反序列化,返回对象

SPUtil

/**
     * 使用SharedPreference保存序列化对象
     * 用Base64.encode将字节文件转换成Base64编码保存在String中
     *
     * @param context 上下文
     * @param key     储存对象的key
     * @param object  object对象  对象必须实现Serializable序列化,否则会出问题,
     *                out.writeObject 无法写入 Parcelable 序列化的对象
     */
    public static void setObject(Context context, String key, Object object) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        //创建字节输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //创建字节对象输出流
        ObjectOutputStream out = null;
        try {
            //然后通过将字对象进行64转码,写入sp中
            out = new ObjectOutputStream(baos);
            out.writeObject(object);
            String objectValue = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
            SharedPreferences.Editor editor = sp.edit();
            editor.putString(key, objectValue);
            editor.commit();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (baos != null) {
                    baos.close();
                }

                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取SharedPreference保存的对象
     * 使用Base64解密String,返回Object对象
     *
     * @param context 上下文
     * @param key     储存对象的key
     * @param <T>     泛型
     * @return 返回保存的对象
     */
    public static <T> T getObject(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        if (sp.contains(key)) {
            String objectValue = sp.getString(key, null);
            byte[] buffer = Base64.decode(objectValue, Base64.DEFAULT);
            //一样通过读取字节流,创建字节流输入流,写入对象并作强制转换
            ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
            ObjectInputStream ois = null;
            try {
                ois = new ObjectInputStream(bais);
                T t = (T) ois.readObject();
                return t;
            } catch (StreamCorruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (bais != null) {
                        bais.close();
                    }

                    if (ois != null) {
                        ois.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

在 SPTestActivity 中使用

 //存储
UserModel userModel = new UserModel();
userModel.name = "张三";
userModel.age = 20;
SPUtil.setObject(this, "user_model", userModel);
ToastUtils.toast(this, "数据保存成功!");

//读取
UserModel user = SPUtil.getObject(this, "user_model");
ToastUtils.toast(this, "姓名:" + user.name + ",年龄:" + user.age);

UserModel 对象

public class UserModel implements Serializable {
    public int id;
    public String name;
    public int age;

}
1.5 存储List类型的数据

用于保存List数据,支持的类型有:

  • List< String>
  • List< JavaBean>

存储List类型的数据

/**
     * 使用SharedPreferences保存List
     * 支持类型:List<String>,List<JavaBean>
     *
     * @param context  上下文
     * @param key      储存的key
     * @param dataList 存储数据
     * @param <T>      泛型
     */
    public static <T> void setDataList(Context context, String key, List<T> dataList) {
        if (null == dataList || dataList.size() < 0) {
            return;
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        Gson gson = new Gson();
        //转换成json数据,再保存
        String strJson = gson.toJson(dataList);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString(key, strJson);
        editor.commit();
    }

读取List类型的数据

/**
     * 获取SharedPreferences保存的List
     *
     * @param context 上下文
     * @param key     储存的key
     * @param <T>     泛型
     * @return 存储List<T>数据
     */
    public static <T> List<T> getDataList(Context context, String key, Class<T> cls) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        List<T> dataList = new ArrayList<T>();
        String strJson = sp.getString(key, null);
        if (null == strJson) {
            return dataList;
        }

        Gson gson = new Gson();

        //使用泛型解析数据会出错,返回的数据类型是LinkedTreeMap
        dataList = gson.fromJson(strJson, new TypeToken<List<T>>() {
        }.getType());

        return dataList;
    }

在 SPTestActivity 中使用

//存储
List<UserModel> userList = new ArrayList<>();
for (int i = 0; i < 5; i ++) {
          UserModel userModel1 = new UserModel();
          userModel1.name = "名字:" + i;
          userList.add(userModel1);
}
SPUtil.setDataList(this, "user_list", userList);
ToastUtils.toast(this, "数据保存成功!");

//读取
List<UserModel> dataList = SPUtil.getDataList(this, "user_list");
StringBuffer buffer = new StringBuffer();
 for (UserModel user1 : dataList) {
        buffer.append(user1.name + ";");
 }
 ToastUtils.toast(this, buffer.toString());

运行后,会发现,在读取数据的时候,会抛出异常:
java.lang.ClassCastException: com.google.gson.internal.LinkedTreeMap cannot be cast to com.example.rs.recyclerviewdemo.model.UserModel
看错误信息,是 “类型” 转换问题。

断点调试下
用这种方式读取数据:

/**
    * 获取SharedPreferences保存的List
    *
    * @param context 上下文
    * @param key     储存的key
    * @param <T>     泛型
    * @return 存储List<T>数据
    */
   public static <T> List<T> getDataList(Context context, String key, Class<T> cls) {
       SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
       List<T> dataList = new ArrayList<T>();
       String strJson = sp.getString(key, null);
       if (null == strJson) {
           return dataList;
       }

       Gson gson = new Gson();

       //使用泛型解析数据会出错,返回的数据类型是LinkedTreeMap
       dataList = gson.fromJson(strJson, new TypeToken<List<T>>() {
       }.getType());

       return dataList;
   }

断点调试结果:
在这里插入图片描述
可看到,返回的类型是个LinkedTreeMap,所以当把解析后的集合遍历复制给 UserModel 的时候就会报 “类型” 转换错误的异常。

对从 SharedPreferences 读取数据那块代码进行改造:

public static <T> List<T> getDataList(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        List<T> dataList = new ArrayList<T>();
        String strJson = sp.getString(key, null);
        if (null == strJson) {
            return dataList;
        }

        Gson gson = new Gson();

        //使用泛型解析数据会出错,返回的数据类型是LinkedTreeMap
//        dataList = gson.fromJson(strJson, new TypeToken<List<T>>() {
//        }.getType());

        //这样写,太死
        dataList = gson.fromJson(strJson, new TypeToken<List<UserModel>>() {
        }.getType());
        return dataList;
    }

在解析时,将 T 泛型改为 UserModel 对象进行解析,再次运行,读取正常。
断点调试查看数据:
在这里插入图片描述
dataList 里存入的是 UserModel 对象,解析正常。
但是解析类型规定死,这样太死了,无法扩展。

再次进行改造:

/**
     * 获取SharedPreferences保存的List
     *
     * @param context 上下文
     * @param key     储存的key
     * @param <T>     泛型
     * @return 存储List<T>数据
     */
    public static <T> List<T> getDataList(Context context, String key, Class<T> cls) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        List<T> dataList = new ArrayList<T>();
        String strJson = sp.getString(key, null);
        if (null == strJson) {
            return dataList;
        }

        Gson gson = new Gson();

        //使用泛型解析数据会出错,返回的数据类型是LinkedTreeMap
//        dataList = gson.fromJson(strJson, new TypeToken<List<T>>() {
//        }.getType());

        //这样写,太死
//        dataList = gson.fromJson(strJson, new TypeToken<List<UserModel>>() {
//        }.getType());

        JsonArray arry = new JsonParser().parse(strJson).getAsJsonArray();
        for (JsonElement jsonElement : arry) {
            dataList.add(gson.fromJson(jsonElement, cls));
        }

        return dataList;
    }

调用:

List<UserModel> dataList = SPUtil.getDataList(this, "user_list",
                        UserModel.class);
StringBuffer buffer = new StringBuffer();
for (UserModel user1 : dataList) {
      buffer.append(user1.name + ";");
}
ToastUtils.toast(this, buffer.toString());

OK ! ! !

或者参考LinkedTreeMap的处理方法

1.6 存储 Map<K, V> 集合

SharedPreferences 存储 Map<K, V> 集合

    /**
     * 使用SharedPreferences保存集合
     * @param context  上下文
     * @param key  储存的key
     * @param map  map数据
     */
    public static <K, V> void setHashMapData(Context context, String key, Map<K, V> map){
        if (map == null) {
            return ;
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        Gson gson = new Gson();
        String strJson = gson.toJson(map);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString(key, strJson);
        editor.commit();
    }

    /**
     * 获取SharedPreferences保存的集合
     * @param context  上下文
     * @param key  储存的key
     * @param clsV  解析类型
     * @return  Map集合
     */
    public static <V> Map<String, V> getHashMapData(Context context, String key, Class<V> clsV){
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        Map<String, V> map = new HashMap<>();
        String strJson = sp.getString(key, "");
        Gson gson = new Gson();
        JsonObject obj =new JsonParser().parse(strJson).getAsJsonObject();
        Set<Map.Entry<String, JsonElement>> entrySet = obj.entrySet();
        for (Map.Entry<String, JsonElement> entry : entrySet) {
            String entryKey = entry.getKey();
            JsonObject value = (JsonObject) entry.getValue();
            map.put(entryKey, gson.fromJson(value, clsV));
        }

        return map;
    }

使用:

//存储
Map<String, UserModel> map = new HashMap<>();
for (int i = 0; i < 5; i++) {
      UserModel userModel1 = new UserModel();
      userModel1.name = "姓名:" + i;
      map.put(i +"", userModel1);
}

SPUtil.setHashMapData(this, "user_map", map);
ToastUtils.toast(this, "数据保存成功!");


//读取
Map<String, UserModel> userMap = SPUtil.getHashMapData(this, "user_map",  UserModel.class);
UserModel userModel1 = userMap.get("1");
ToastUtils.toast(this, userModel1.name);

2 SharedPreferences 工具类封装

SPUtil

package com.example.rs.recyclerviewdemo.util;

import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import android.util.Log;

import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.StreamCorruptedException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Created by sgll on 2019/1/15.
 * SharedPreferences工具类
 * 保存的数据类型有:
 * 1、四种基本类型数据(int,float,long,boolean)+ String
 * 2、List类型的数据
 * 3、存储序列化对象
 * 4、存储Map<K, V>集合
 */
public class SPUtil {
    private static String SP_NAME = "recyclerviewdemo_sp";
    private static String TAG = "SharedPreferenceUtil";

    /**
     * 获取SharedPreference保存的value
     * 值的类型:(int,float,long,boolean)+String
     *
     * @param context 上下文
     * @param key     储存值的key
     * @param clazz   获取值的类型   int,float,long,boolean)+String
     * @param <T>     T
     * @return 值
     */
    public static <T> T getValue(Context context, String key, Class<T> clazz) {
        if (context == null) {
            throw new RuntimeException("context is null");
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        return getValue(key, getT(clazz), sp);
    }

    /**
     * 获取SharedPreference保存的value
     * 值的类型:(int,float,long,boolean)+String
     *
     * @param key   储存值的key
     * @param T 获取值的类型   int,float,long,boolean)+String
     * @param sp    SharedPreferences
     * @param <T>   T
     * @return 值
     */
    private static <T> T getValue(String key, T t, SharedPreferences sp) {
        if (t == null) {
            return null;
        }
        if (t instanceof Integer) {
            return (T) Integer.valueOf(sp.getInt(key, 0));
        } else if (t instanceof String) {
            return (T) sp.getString(key, "");
        } else if (t instanceof Boolean) {
            return (T) Boolean.valueOf(sp.getBoolean(key, false));
        } else if (t instanceof Long) {
            return (T) Long.valueOf(sp.getLong(key, 0L));
        } else if (t instanceof Float) {
            return (T) Float.valueOf(sp.getFloat(key, 0L));
        }
        Log.e(TAG, "无法找到" + key + "对应的值");
        return null;
    }

    /**
     * 通过反射创建类实例
     * 反射有两种方式创建类实例:
     * 1、Class.newInstance():只能调用无参的构造函数(默认构造函数)
     * 2、Constructor.newInstance():可以根据传入的参数,调用任意构造构造函数。
     * Integer、Boolean、Long、Float 没有默认构造函数,只能通过 Constructor.newInstance() 调用
     * String 是有默认构造函数的,两种方法都适用
     * @param clazz  String.class、Integer.class、Boolean.class、Long.class、Float.class
     * @param <T> T
     * @return T
     */
    private static <T> T getT(Class<T> clazz) {
        T t = null;
        String clazzName = clazz.getName();
        Log.e(TAG, "基本类型名字是[" + clazzName + "]");
        try {
            if ("java.lang.Integer".equals(clazzName)) {
                t = clazz.getConstructor(int.class).newInstance(1);
            } else if ("java.lang.String".equals(clazzName)) {
                t = clazz.newInstance();
            } else if ("java.lang.Boolean".equals(clazzName)) {
                t = clazz.getConstructor(boolean.class).newInstance(false);
            } else if ("java.lang.Long".equals(clazzName)) {
                t = clazz.getConstructor(long.class).newInstance(0L);
            } else if ("java.lang.Float".equals(clazzName)) {
                t = clazz.getConstructor(float.class).newInstance(0F);
            }
        } catch (InstantiationException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        } catch (NoSuchMethodException e) {
            Log.e(TAG, "类型输入错误或者复杂类型无法解析[" + e.getMessage() + "]");
            e.printStackTrace();
        }

        return t;
    }

    /**
     * 使用SharedPreference保存value
     *
     * @param context 上下文
     * @param key     储存值的key
     * @param value   值
     */
    public static void setValue(Context context, String key, Object value) {
        if (context == null) {
            throw new RuntimeException("context is null");
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        setValue(key, value, sp);
    }

    /**
     * 使用SharedPreference保存value
     *
     * @param key   储存值的key
     * @param value 值
     * @param sp    SharedPreferences
     */
    private static void setValue(String key, Object value, SharedPreferences sp) {
        SharedPreferences.Editor editor = sp.edit();
        if (value instanceof Integer) {
            editor.putInt(key, (Integer) value);
        } else if (value instanceof String) {
            editor.putString(key, (String) value);
        } else if (value instanceof Boolean) {
            editor.putBoolean(key, (Boolean) value);
        } else if (value instanceof Long) {
            editor.putLong(key, (Long) value);
        } else if (value instanceof Float) {
            editor.putFloat(key, (Float) value);
        }
        editor.commit();
    }

    /**
     * 使用SharedPreference保存序列化对象
     * 用Base64.encode将字节文件转换成Base64编码保存在String中
     *
     * @param context 上下文
     * @param key     储存对象的key
     * @param object  object对象  对象必须实现Serializable序列化,否则会出问题,
     *                out.writeObject 无法写入 Parcelable 序列化的对象
     */
    public static void setObject(Context context, String key, Object object) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        //创建字节输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //创建字节对象输出流
        ObjectOutputStream out = null;
        try {
            //然后通过将字对象进行64转码,写入sp中
            out = new ObjectOutputStream(baos);
            out.writeObject(object);
            String objectValue = new String(Base64.encode(baos.toByteArray(), Base64.DEFAULT));
            SharedPreferences.Editor editor = sp.edit();
            editor.putString(key, objectValue);
            editor.commit();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (baos != null) {
                    baos.close();
                }

                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 获取SharedPreference保存的对象
     * 使用Base64解密String,返回Object对象
     *
     * @param context 上下文
     * @param key     储存对象的key
     * @param <T>     泛型
     * @return 返回保存的对象
     */
    public static <T> T getObject(Context context, String key) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        if (sp.contains(key)) {
            String objectValue = sp.getString(key, null);
            byte[] buffer = Base64.decode(objectValue, Base64.DEFAULT);
            //一样通过读取字节流,创建字节流输入流,写入对象并作强制转换
            ByteArrayInputStream bais = new ByteArrayInputStream(buffer);
            ObjectInputStream ois = null;
            try {
                ois = new ObjectInputStream(bais);
                T t = (T) ois.readObject();
                return t;
            } catch (StreamCorruptedException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } finally {
                try {
                    if (bais != null) {
                        bais.close();
                    }

                    if (ois != null) {
                        ois.close();
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 使用SharedPreferences保存List
     * 支持类型:List<String>,List<JavaBean>
     *
     * @param context  上下文
     * @param key      储存的key
     * @param dataList 存储数据
     * @param <T>      泛型
     */
    public static <T> void setDataList(Context context, String key, List<T> dataList) {
        if (null == dataList || dataList.size() < 0) {
            return;
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        Gson gson = new Gson();
        //转换成json数据,再保存
        String strJson = gson.toJson(dataList);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString(key, strJson);
        editor.commit();
    }

    /**
     * 获取SharedPreferences保存的List
     *
     * @param context 上下文
     * @param key     储存的key
     * @param <T>     泛型
     * @return 存储List<T>数据
     */
    public static <T> List<T> getDataList(Context context, String key, Class<T> cls) {
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        List<T> dataList = new ArrayList<T>();
        String strJson = sp.getString(key, null);
        if (null == strJson) {
            return dataList;
        }

        Gson gson = new Gson();

        //使用泛型解析数据会出错,返回的数据类型是LinkedTreeMap
//        dataList = gson.fromJson(strJson, new TypeToken<List<T>>() {
//        }.getType());

        //这样写,太死
//        dataList = gson.fromJson(strJson, new TypeToken<List<UserModel>>() {
//        }.getType());

        JsonArray arry = new JsonParser().parse(strJson).getAsJsonArray();
        for (JsonElement jsonElement : arry) {
            dataList.add(gson.fromJson(jsonElement, cls));
        }

        return dataList;
    }

    /**
     * 使用SharedPreferences保存集合
     * @param context  上下文
     * @param key  储存的key
     * @param map  map数据
     */
    public static <K, V> void setHashMapData(Context context, String key, Map<K, V> map){
        if (map == null) {
            return ;
        }

        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        Gson gson = new Gson();
        String strJson = gson.toJson(map);
        SharedPreferences.Editor editor = sp.edit();
        editor.putString(key, strJson);
        editor.commit();
    }

    /**
     * 获取SharedPreferences保存的集合
     * @param context  上下文
     * @param key  储存的key
     * @param clsV  解析类型
     * @return  Map集合
     */
    public static <V> Map<String, V> getHashMapData(Context context, String key, Class<V> clsV){
        SharedPreferences sp = context.getSharedPreferences(SP_NAME, Context.MODE_PRIVATE);
        Map<String, V> map = new HashMap<>();
        String strJson = sp.getString(key, "");
        Gson gson = new Gson();
        JsonObject obj =new JsonParser().parse(strJson).getAsJsonObject();
        Set<Map.Entry<String, JsonElement>> entrySet = obj.entrySet();
        for (Map.Entry<String, JsonElement> entry : entrySet) {
            String entryKey = entry.getKey();
            JsonObject value = (JsonObject) entry.getValue();
            map.put(entryKey, gson.fromJson(value, clsV));
        }

        return map;
    }
}

使用

package com.example.rs.recyclerviewdemo.activity;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.View;
import android.widget.EditText;

import com.example.rs.recyclerviewdemo.R;
import com.example.rs.recyclerviewdemo.model.UserModel;
import com.example.rs.recyclerviewdemo.util.SPUtil;
import com.example.rs.recyclerviewdemo.util.ToastUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

/**
 * Created by sgll on 2019/1/15.
 */
public class SPTestActivity extends AppCompatActivity {
    @BindView(R.id.et_content)
    EditText etContent;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sp_test);
        ButterKnife.bind(this);
    }

    @OnClick({R.id.bt_set_base, R.id.bt_get_base, R.id.bt_set_object, R.id.bt_get_object,
            R.id.bt_set_list, R.id.bt_get_list, R.id.bt_set_map, R.id.bt_get_map})
    void onClick(View view){
        int viewId = view.getId();
        switch (viewId) {
            case R.id.bt_set_base:
                String str = etContent.getText().toString().trim();
                if (TextUtils.isEmpty(str)) {
                    ToastUtils.toast(this, "请输入内容");
                    return;
                }
//                SPUtil.setValue(this, "base_value", str);
//                SPUtil.setValue(this, "base_value_int", 2);
//                SPUtil.setValue(this, "base_value_boolean", true);
//                SPUtil.setValue(this, "base_value_long", 3L);
                SPUtil.setValue(this, "base_value_float", 3.5F);
                ToastUtils.toast(this, "数据保存成功!");
                break;
            case R.id.bt_get_base:
//                String value = SPUtil.getValue(this, "base_value",  String.class);
//                int value = SPUtil.getValue(this, "base_value_int",   Integer.class);
//                boolean value = SPUtil.getValue(this, "base_value_boolean", Boolean.class);
//                long value = SPUtil.getValue(this, "base_value_long",   Long.class);
                float value = SPUtil.getValue(this, "base_value_float",   Float.class);
                ToastUtils.toast(this, String.valueOf(value));
                break;
            case R.id.bt_set_object:
                UserModel userModel = new UserModel();
                userModel.name = "张三";
                userModel.age = 20;
                SPUtil.setObject(this, "user_model", userModel);
                ToastUtils.toast(this, "数据保存成功!");
                break;
            case R.id.bt_get_object:
                UserModel user = SPUtil.getObject(this, "user_model");
                ToastUtils.toast(this, "姓名:" + user.name + ",年龄:" + user.age);
                break;
            case R.id.bt_set_list:
                List<UserModel> userList = new ArrayList<>();
                for (int i = 0; i < 5; i ++) {
                    UserModel userModel1 = new UserModel();
                    userModel1.name = "名字:" + i;
                    userList.add(userModel1);
                }

//                List<String> userList = new ArrayList<>();
//                for (int i = 0; i < 5; i ++) {
//                    userList.add("你好:" + i);
//                }

//                List<Integer> userList = new ArrayList<>();
//                for (int i = 0; i < 5; i ++) {
//                    userList.add(i);
//                }

                SPUtil.setDataList(this, "user_list", userList);
                ToastUtils.toast(this, "数据保存成功!");
                break;
            case R.id.bt_get_list:
                List<UserModel> dataList = SPUtil.getDataList(this, "user_list",
                        UserModel.class);
                StringBuffer buffer = new StringBuffer();
                for (UserModel user1 : dataList) {
                    buffer.append(user1.name + ";");
                }

//                List<String> dataList = SPUtil.getDataList(this, "user_list", String.class);
//                for (String stra :  dataList) {
//                    buffer.append(stra + ";");
//                }

//                List<Integer> dataList = SPUtil.getDataList(this, "user_list", Integer.class);
//                for (Integer stra :  dataList) {
//                    buffer.append(stra + ";");
//                }

                ToastUtils.toast(this, buffer.toString());
                break;
            case R.id.bt_set_map:
                Map<String, UserModel> map = new HashMap<>();
                for (int i = 0; i < 5; i++) {
                    UserModel userModel1 = new UserModel();
                    userModel1.name = "姓名:" + i;
                    map.put(i +"", userModel1);
                }

                SPUtil.setHashMapData(this, "user_map", map);
                ToastUtils.toast(this, "数据保存成功!");
                break;
            case R.id.bt_get_map:
                Map<String, UserModel> userMap = SPUtil.getHashMapData(this, "user_map",
                        UserModel.class);
                UserModel userModel1 = userMap.get("1");
                ToastUtils.toast(this, userModel1.name);
                break;
            default:
                break;
        }
    }
}

xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <EditText
        android:id="@+id/et_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_set_base"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="存储值" />

        <Button
            android:id="@+id/bt_get_base"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="获取值" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_set_object"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="存储对象" />

        <Button
            android:id="@+id/bt_get_object"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="获取对象" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_set_list"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="存储List" />

        <Button
            android:id="@+id/bt_get_list"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="获取List" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">

        <Button
            android:id="@+id/bt_set_map"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="存储Map集合" />

        <Button
            android:id="@+id/bt_get_map"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="获取Map集合" />
    </LinearLayout>

</LinearLayout>

布局图:
在这里插入图片描述

UserModel

package com.example.rs.recyclerviewdemo.model;

import java.io.Serializable;

/**
 * Created by sgll on 2019/1/16.
 */
public class UserModel implements Serializable {
    public int id;
    public String name;
    public int age;

}

3 总结

SharedPreference s是无法直接保存 Object,List 集合和 Map 集合的,需要先将它们转换为字符串,再保存到 SharedPreferences 中,取的时候再把对应的字符串转为 Object,List 集合或者 Map 集合。

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值