开发记录:在日常开发中,长使用的几种本地序列化方式,SP的使用是最普遍的,所以对SP 的封装就异常重要了,用起来顺手的SP封装工具,能够节省你很多时间。
- 面试技能点
- 原生代码实现
- 工具类(只想要代码 Github 三个类直接贴到项目中就行了。)
枯燥的原理时间:
1. SharedPreferences读取xml文件时,会以DOM方式解析(把整个xml文件直接加载到内存中解析),在调用getXXX()方法时取到的是内存中的数据,方法执行时会有个锁来阻塞,目的是等待文件加载完毕,没加载完成之前会wait()。
2. SharedPreferences写文件时,如果调用的commit(),会将数据同步写入内存中,内存数据更新,再同步写入磁盘中;如果调用的apply(),会将数据同步写入内存中,内存数据更新,然后异步写人磁盘,也就是说可能写磁盘操作还没有完成就直接返回了。在主线程中建议使用apply(),因为同步写磁盘,当文件较大时,commit()会等到写磁盘完成再返回,可能会有ANR问题。
3. SP第一次初始化到读取到数据存在一定延迟,因为需要到文件中读取数据,因此可能会对UI线程流畅度造成一定影响。
原生实现:
//获得SharedPreferences的实例 sp_name是文件名
SharedPreferences sp = getSharedPreferences("sp_name", Context.MODE_PRIVATE);
//获得Editor 实例
SharedPreferences.Editor editor = sp.edit();
//以key-value形式保存数据
editor.putString("data_key", "data");
//apply()是异步写入数据
editor.apply();
//commit()是同步写入数据
//editor.commit();
PrefsHelper: SP 封装类
public class PrefsHelper {
//保存文件名称
private static final String PREFERENCE_FILE_NAME = "Example";
//用户个人的信息
private static final String KEY_USER_INFO = "UserInfo";
// 注册
public static void init(Context context) {
Prefs.init(context.getApplicationContext(), PREFERENCE_FILE_NAME);
}
/**
* 保存用户的登录数据。
*/
public static void setUserInfo(@NonNull UserBean info) {
String infoToSave = new Gson().toJson(info);
LogUtil.d("UserBeanToSp:%s" + infoToSave);
Prefs.set(KEY_USER_INFO, infoToSave);
}
/**
* 获取用户的个人信息。
*/
@Nullable
public static UserBean getUserInfo() {
String savedInfo = Prefs.getString(KEY_USER_INFO);
if (TextUtils.isEmpty(savedInfo)) {
return null;
}
return new Gson().fromJson(savedInfo, UserBean.class);
}
/**
* 清除用户登录数据。
*/
public static void removeLoginInfo() {
LogUtil.d("Removing UserBean info.");
Prefs.remove(KEY_USER_INFO);
}
}
Prefs:SP 具体操作类
/* package */ class Prefs {
private static Context appContext;
private static String fileName;
/**
* 为后续调用初始化此类.
* @param context 上下文.
* @param file SP的文件读写.
*/
public static void init(@NonNull Context context, @NonNull String file){
appContext = context.getApplicationContext();
fileName = file;
}
/**
*检索SP值,假设第二个参数是正确的类.
* @param <T> 支持类型Boolean, Integer, Long, Float, Double, String,不支持类型会抛出
*/
@SuppressWarnings("unchecked")
public static <T> T get(@NonNull String key, T fallback) throws
UnsupportedOperationException {
SharedPreferences sp = getSharedPreferences();
Object result;
if (fallback instanceof Boolean){
result = sp.getBoolean(key, (Boolean)fallback);
}
else if (fallback instanceof String){
result = sp.getString(key, (String) fallback);
}
else if (fallback instanceof Integer){
result = sp.getInt(key, (Integer) fallback);
}
else if (fallback instanceof Float){
result = sp.getFloat(key, (Float) fallback);
}
else if (fallback instanceof Long) {
result = sp.getLong(key, (Long) fallback);
}
else{
throw new UnsupportedOperationException("Type not supported: " + fallback.getClass()
.getSimpleName());
}
return (T)result;
}
/**
* 从首选项中检索字符串值,默认值为空字符串。.
*/
public static String getString(@NonNull String key){
return get(key, "");
}
/**
* 长值检索默认是 0 代码。
*/
public static long getLong(@NonNull String key){
return get(key, 0L);
}
/**
* 从首选项中检索整数值, default is <code>0</code>.
*/
public static int getInt(@NonNull String key){
return get(key, 0);
}
/**
* 检索一个布尔值, default is <code>false</code>.
*/
public static boolean getBoolean(@NonNull String key){
return get(key, false);
}
/**
* 把值放进SP 中
* @param <T> Boolean, Integer, Long, Float, Double, String allowed. For other types, an
* UnsupportedOperationException is thrown.
*/
public static <T> void set(@NonNull String key, @NonNull T value) {
SharedPreferences.Editor editor = getSharedPreferences().edit();
if (value instanceof Boolean){
editor.putBoolean(key, (Boolean) value);
}
else if (value instanceof String){
editor.putString(key, (String) value);
}
else if (value instanceof Integer){
editor.putInt(key, (Integer) value);
}
else if (value instanceof Float){
editor.putFloat(key, (Float) value);
}
else if (value instanceof Long) {
editor.putLong(key, (Long) value);
}
else{
throw new UnsupportedOperationException("Type not supported: " + value.getClass()
.getSimpleName());
}
//use apply instead of commit to improve performance on UI thread.
editor.apply();
}
public static void remove(String key) {
getSharedPreferences().edit().remove(key).apply();
}
/**
* clear all data!
* @see SharedPreferences.Editor#clear()
*/
public static void clear(){
getSharedPreferences().edit().clear().apply();
}
private static SharedPreferences getSharedPreferences() {
checkInitiatedOrThrow();
return appContext.getSharedPreferences(fileName, Context.MODE_PRIVATE);
}
/**
* Retrieve all values from the preferences.
* @see SharedPreferences#getAll()
*/
public static Map<String, ?> getAll(){
return getSharedPreferences().getAll();
}
private static void checkInitiatedOrThrow() {
if (appContext == null || TextUtils.isEmpty(fileName)) {
throw new IllegalStateException("The Prefs class is not initialized correctly.");
}
}
}
LocalDataManager:单例模式调用 PrefsHelper
public class LocalDataManager {
private static LocalDataManager instance;
private UserBean mUserBean;
private LocalDataManager() {
// 私有化构造
}
public static LocalDataManager getInstance() {
if (instance == null) {
synchronized (LocalDataManager.class) {
// 线程安全的单利模式
if (instance == null) {
instance = new LocalDataManager();
}
}
}
return instance;
}
/**
* 获取用户个人信息
*
* @return
*/
public UserBean getUserInfo() {
if (mUserBean == null) {
mUserBean = PrefsHelper.getUserInfo();
}
return mUserBean;
}
/**
* 获取用户个人信息
*
* @return
*/
public void saveUserInfo(@NonNull UserBean mUserBean) {
//Update cached object!
this.mUserBean = mUserBean;
PrefsHelper.setUserInfo(mUserBean);
}
/**
* 清楚用户信息
*
* @return
*/
public void clearLoginInfo() {
mUserBean = null;
PrefsHelper.removeLoginInfo();
}
}
MianActivity : 具体使用
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化Prefs 建议在Application 中进行初始化
PrefsHelper.init(this);
UserBean mUserBean = new UserBean();
mUserBean.setAll_money(1);
LocalDataManager.getInstance().saveUserInfo(mUserBean);
// 一般登录的时候,拿到用户的数据,直接保存在SP 中,或者APP 是否首次登录等判断
UserBean bean = LocalDataManager.getInstance().getUserInfo();
LogUtil.e(bean.getAll_money()+"元");
}
}
代码拿走,留个赞,互相帮助!!!
下一篇准备写ViewPager +Fragment 懒加载,也是在日常开发中遇到的问题。避免下次采坑。