1. 程式人生 > >SharedPreferences在專案中應用——開發記錄

SharedPreferences在專案中應用——開發記錄

開發記錄:在日常開發中,長使用的幾種本地序列化方式,SP的使用是最普遍的,所以對SP 的封裝就異常重要了,用起來順手的SP封裝工具,能夠節省你很多時間。

  1. 面試技能點
  2. 原生程式碼實現
  3. 工具類(只想要程式碼 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  懶載入,也是在日常開發中遇到的問題。避免下次採坑。