编写Singleton类来管理Android SharedPreferences

时间:2013-10-26 23:23:33

标签: android sharedpreferences

我正在尝试编写一个单例类来监督涉及共享首选项的所有操作。

我有3个首选项文件,常规,设置和临时

我希望能够使用此类编写给定类型的首选项,例如:

stg_full_screen: true // as boolean

这是我到目前为止所做的:

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;

public class SharedPrefManager extends Activity {

    // Globals
    private int GENERAL             = 1000;
    private int SETTINGS            = 2000;
    private int TEMP_STORE          = 3000;

    private String PREF_GENERAL     = "com.example.general";
    private String PREF_SETTINGS    = "com.example.settings";
    private String PREF_TEMP_STORE  = "com.example.temp_store";


    private SharedPreferences general;
    private SharedPreferences settings;
    private SharedPreferences tempStore;

    private SharedPreferences.Editor general_editor;
    private SharedPreferences.Editor settings_editor;
    private SharedPreferences.Editor temp_store_editor;





    // Instantiate singleton object
    private static SharedPrefManager ourInstance = new SharedPrefManager();


    public static SharedPrefManager getInstance() { return ourInstance; }

    private SharedPrefManager() {
        // Get handle on all preference files
        general   = getSharedPreferences(PREF_GENERAL, Context.MODE_PRIVATE);
        settings  = getSharedPreferences(PREF_SETTINGS, Context.MODE_PRIVATE);
        tempStore = getSharedPreferences(PREF_TEMP_STORE, Context.MODE_PRIVATE);

        // provision editors for all preference files
        general_editor    = general.edit();
        settings_editor   = settings.edit();
        temp_store_editor = tempStore.edit();
    }



    private String read_prefs (String pref_name) {
        // this method reads a preference and returns it
        // ideally, i would want to be able to return appropriate types by request
        // e.g boolean, string
        return null;
    }

    private void write_prefs (String pref_name, String pref_val) {
        // this method would take a preference and write the appropriate type to prefs
    }


    // this method determines where to put a preference by checking the name of the key
    // this works because i use the following naming conventions
    // stg_name for settings, tmp_name for all that goes into tempStore

    private String resolve_pref_category (String path) {
        if (path.startsWith("stn"))         return PREF_SETTINGS;
        else if (path.startsWith("tmp"))    return PREF_TEMP_STORE;
        else                                return PREF_GENERAL;
    }

}

我的问题是:

  1. 这是明智之举吗?
  2. 我如何有效地确定偏好的类型?
  3. 由于

7 个答案:

答案 0 :(得分:25)

正确的单身共享首选项类。它可能对将来的其他人有所帮助。

import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;

public class SharedPref
{
    private static SharedPreferences mSharedPref;
    public static final String NAME = "NAME";
    public static final String AGE = "AGE";
    public static final String IS_SELECT = "IS_SELECT";

    private SharedPref()
    {

    }

    public static void init(Context context)
    {
        if(mSharedPref == null)
            mSharedPref = context.getSharedPreferences(context.getPackageName(), Activity.MODE_PRIVATE);
    }

    public static String read(String key, String defValue) {
        return mSharedPref.getString(key, defValue);
    }

    public static void write(String key, String value) {
        SharedPreferences.Editor prefsEditor = mSharedPref.edit();
        prefsEditor.putString(key, value);
        prefsEditor.commit();
    }

    public static boolean read(String key, boolean defValue) {
        return mSharedPref.getBoolean(key, defValue);
    }

    public static void write(String key, boolean value) {
        SharedPreferences.Editor prefsEditor = mSharedPref.edit();
        prefsEditor.putBoolean(key, value);
        prefsEditor.commit();
    }

    public static Integer read(String key, int defValue) {
        return mSharedPref.getInt(key, defValue);
    }

    public static void write(String key, Integer value) {
        SharedPreferences.Editor prefsEditor = mSharedPref.edit();
        prefsEditor.putInt(key, value).commit();
    }
}

只需在MainActivity上调用SharedPref.init()

SharedPref.init(getApplicationContext());

*写数据*

    SharedPref.write(SharedPref.NAME, "XXXX");//save string in shared preference.
    SharedPref.write(SharedPref.AGE, "25");//save int in shared preference.
    SharedPref.write(SharedPref.IS_SELECT, true);//save boolean in shared preference.

读取数据

    String name = SharedPref.read(SharedPref.NAME, null);//read string in shared preference.
    String age = SharedPref.read(SharedPref.AGE, 0);//read int in shared preference.
    String isSelect = SharedPref.read(SharedPref.IS_SELECT, false);//read boolean in shared preference.

*输出:*

Name : "XXXX";
Age : "25";
IsSelect : "true";

答案 1 :(得分:21)

通常,我使用这样的东西:

没有静态Context引用,每个属性的静态getter / setter,在需要时,您可以为某些属性添加内存缓存值,以便从内存中更快地获取它,而不是从SharedPreferences中读取。明确的api。

public class SharedPreferencesManager {

    private static final String APP_SETTINGS = "APP_SETTINGS";


    // properties
    private static final String SOME_STRING_VALUE = "SOME_STRING_VALUE";
    // other properties...


    private SharedPreferencesManager() {}

    private static SharedPreferences getSharedPreferences(Context context) {
        return context.getSharedPreferences(APP_SETTINGS, Context.MODE_PRIVATE);
    }

    public static String getSomeStringValue(Context context) {
        return getSharedPreferences(context).getString(SOME_STRING_VALUE , null);
    }

    public static void setSomeStringValue(Context context, String newValue) {
        final SharedPreferences.Editor editor = getSharedPreferences(context).edit();
        editor.putString(SOME_STRING_VALUE , newValue);
        editor.commit();
    }

    // other getters/setters
}

答案 2 :(得分:2)

Kotlin 示例,用于使用anrdoidx的安全性加密库(最低API 23)进行双重用途的加密和未加密共享首选项。


我在需要的地方使用Dagger2将其作为@Singleton注入。

在Dagger模块中使用@Name批注来区分SharedPreferences实例,并且可以有2个独立的.xml文件(1个加密,1个未加密)来读写。


如果您不想要加密的东西,请撕掉;-D。


添加到build.gradle中的依赖项:

implementation "androidx.security:security-crypto:1.0.0-beta01"


import android.content.Context
import android.content.SharedPreferences
import androidx.security.crypto.EncryptedSharedPreferences

class Prefs(prefsName: String, context: Context) {

    private lateinit var ANDX_SECURITY_KEY_KEYSET: String
    private lateinit var ANDX_SECURITY_VALUE_KEYSET: String
    private lateinit var cntext: Context
    private lateinit var prefName: String

    private lateinit var prefs: SharedPreferences

    constructor(
        prefsName: String,
        context: Context,
        masterKeyAlias: String,
        prefKeyEncryptionScheme: EncryptedSharedPreferences.PrefKeyEncryptionScheme,
        prefValueEncryptionScheme: EncryptedSharedPreferences.PrefValueEncryptionScheme
    ): this(prefsName, context) {
        ANDX_SECURITY_KEY_KEYSET = "__androidx_security_crypto_encrypted_prefs_key_keyset__"
        ANDX_SECURITY_VALUE_KEYSET =    "__androidx_security_crypto_encrypted_prefs_value_keyset__"
        cntext = context
        prefName = prefsName
        prefs =
            EncryptedSharedPreferences.create(
                prefsName,
                masterKeyAlias,
                context,
                prefKeyEncryptionScheme,
                prefValueEncryptionScheme
            )
    }

    init {
        if (!::ANDX_SECURITY_KEY_KEYSET.isInitialized) {
            prefs =
                context.getSharedPreferences(
                    prefsName,
                    Context.MODE_PRIVATE
                )
        }
    }

    companion object {
        const val INVALID_BOOLEAN: Boolean = false
        const val INVALID_FLOAT: Float = -11111111111F
        const val INVALID_INT: Int = -1111111111
        const val INVALID_LONG: Long = -11111111111L
        const val INVALID_STRING: String = "INVALID_STRING"
        val INVALID_STRING_SET: Set<String> = setOf(INVALID_STRING)
    }

    /**
     * OnChangeListener
     * */
    fun registerOnSharedPreferenceChangeListener(
        listener: SharedPreferences.OnSharedPreferenceChangeListener) =
        prefs.registerOnSharedPreferenceChangeListener(listener)

    fun unregisterOnSharedPreferenceChangeListener(
        listener: SharedPreferences.OnSharedPreferenceChangeListener) =
        prefs.unregisterOnSharedPreferenceChangeListener(listener)

    /**
     * Read Shared Prefs
     * */
    fun contains(key: String): Boolean =
        prefs.contains(key)

    fun getAll(): Map<String, *> =
        prefs.all

    // Returns null if the Boolean value is not in
    //  Shared Preferences
    fun read(key: String): Boolean? =
        if (contains(key)) {
            read(key, INVALID_BOOLEAN)
        } else {
            null
        }

    // Boolean
    fun read(key: String, returnIfInvalid: Boolean): Boolean =
        prefs.getBoolean(key, returnIfInvalid)

    // Float
    fun read(key: String, returnIfInvalid: Float): Float =
        prefs.getFloat(key, returnIfInvalid)

    // Int
    fun read(key: String, returnIfInvalid: Int): Int =
        prefs.getInt(key, returnIfInvalid)

    // Long
    fun read(key: String, returnIfInvalid: Long): Long =
        prefs.getLong(key, returnIfInvalid)

    // Set<String>
    fun read(key: String, returnIfInvalid: Set<String>): Set<String>? =
        prefs.getStringSet(key, returnIfInvalid)

    // String
    fun read(key: String, returnIfInvalid: String): String? =
        prefs.getString(key, returnIfInvalid)

    /**
     * Modify Shared Prefs
     * */
    fun clear() {
        if (::ANDX_SECURITY_KEY_KEYSET.isInitialized) {
            val clearTextPrefs = cntext.getSharedPreferences(prefName, Context.MODE_PRIVATE)
            val keyKeyset = clearTextPrefs.getString(ANDX_SECURITY_KEY_KEYSET, INVALID_STRING)
            val valueKeyset = clearTextPrefs.getString(ANDX_SECURITY_VALUE_KEYSET, INVALID_STRING)
            if (keyKeyset != null && keyKeyset != INVALID_STRING
                && valueKeyset != null && valueKeyset != INVALID_STRING) {
                if (!clearTextPrefs.edit().clear().commit()) {
                    clearTextPrefs.edit().clear().apply()
                }
                if (!clearTextPrefs.edit().putString(ANDX_SECURITY_KEY_KEYSET, keyKeyset).commit()) {
                    clearTextPrefs.edit().putString(ANDX_SECURITY_KEY_KEYSET, keyKeyset).apply()
                }
                if (!clearTextPrefs.edit().putString(ANDX_SECURITY_VALUE_KEYSET, valueKeyset).commit()) {
                    clearTextPrefs.edit().putString(ANDX_SECURITY_VALUE_KEYSET, valueKeyset).apply()
                }
            }
        } else {
            if (!prefs.edit().clear().commit()) {
                prefs.edit().clear().apply()
            }
        }
    }

    fun remove(key: String) {
        if (!prefs.edit().remove(key).commit()) {
            prefs.edit().remove(key).apply()
        }
    }

    // Boolean
    fun write(key: String, value: Boolean) {
        if (!prefs.edit().putBoolean(key, value).commit()) {
            prefs.edit().putBoolean(key, value).apply()
        }
    }

    // Float
    fun write(key: String, value: Float) {
        if (!prefs.edit().putFloat(key, value).commit()) {
            prefs.edit().putFloat(key, value).apply()
        }
    }

    // Int
    fun write(key: String, value: Int) {
        if (!prefs.edit().putInt(key, value).commit()) {
            prefs.edit().putInt(key, value).apply()
        }
    }

    // Long
    fun write(key: String, value: Long) {
        if (!prefs.edit().putLong(key, value).commit()) {
            prefs.edit().putLong(key, value).apply()
        }
    }

    // Set<String>
    fun write(key: String, value: Set<String>) {
        if (!prefs.edit().putStringSet(key, value).commit()) {
            prefs.edit().putStringSet(key, value).apply()
        }
    }

    // String
    fun write(key: String, value: String) {
        if (!prefs.edit().putString(key, value).commit()) {
            prefs.edit().putString(key, value).apply()
        }
    }
}

using Dagger2 to inject as a @Singleton的另一种选择是:

AppPrefs.kt

object AppPrefs {
    lateinit var encryptedPrefs: Prefs
    lateinit var prefs: Prefs

    // Add your key strings here...

    fun initEncryptedPrefs(context: Context) {
        encryptedPrefs =
            Prefs(
                "ENCRYPTED_PREFS",
                context,
                MasterKeys.getOrCreate(MasterKeys.AES256_GCM_SPEC),
                EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
                EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
            )
    }

    fun initPrefs(context: Context) {
        prefs = Prefs("PREFS", context)
    }
}

Application.kt

class Application: Application() {
    override fun onCreate() {
        super.onCreate()
        AppPrefs.initEncryptedPrefs(this.applicationContext)
        AppPrefs.initPrefs(this.applicationContext)
    }
}

然后仅从AppPrefs.prefsAppPrefs.encryptedPrefs的任何地方拨打电话

答案 3 :(得分:1)

科特林解决方案:

object PrefsHelper {

    private lateinit var prefs: SharedPreferences

    private const val PREFS_NAME = "params"

    const val ID_USER = "id_user"
    const val TOKEN = "token"

    fun init(context: Context) {
        prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)
    }

    fun read(key: String, value: String): String? {
        return prefs.getString(key, value)
    }

    fun read(key: String, value: Long): Long? {
        return prefs.getLong(key, value)
    }

    fun write(key: String, value: String) {
        val prefsEditor: SharedPreferences.Editor = prefs.edit()
        with(prefsEditor) {
            putString(key, value)
            commit()
        }
    }

    fun write(key: String, value: Long) {
        val prefsEditor: SharedPreferences.Editor = prefs.edit()
        with(prefsEditor) {
            putLong(key, value)
            commit()
        }
    }
}

在第一个应用运行时调用init()函数。

答案 4 :(得分:1)

SharedPreferences具有ApplicationContext的单例

public class SharedPrefsSingleton {

    private SharedPreferences sharedPref;
    private Context appContext;

    private static SharedPrefsSingleton instance;

    public static synchronized SharedPrefsSingleton getInstance(Context applicationContext){
        if(instance == null)
            instance = new SharedPrefsSingleton(applicationContext);
        return instance;
    }

    private SharedPrefsSingleton(Context applicationContext) {
        appContext = applicationContext;
        sharedPref = appContext.getSharedPreferences(
                appContext.getString(R.string.key_pref), Context.MODE_PRIVATE );
    }

    public void writeData(float value) {
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putFloat(appContext.getString(R.string.key_data), value);
        editor.apply();
    }

    public float readData() {
        return sharedPref.getFloat(appContext.getString(R.string.key_data), 0);
    }
}

答案 5 :(得分:1)

import android.content.Context; 导入android.content.SharedPreferences;

导入com.example.day.weeklypapers.model.LoginModel; 导入com.google.gson.Gson;

公共类WeeklyPreference {

private static WeeklyPreference mInstance = null;
private static SharedPreferences mPreferences;
private static SharedPreferences.Editor mEditor;
private Context context;
private static String SharedPreferenceKey = "Weekly"   ;

private WeeklyPreference() {

}

public static WeeklyPreference getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new WeeklyPreference();
    }
    if (mPreferences == null) {
        mPreferences = context.getApplicationContext().getSharedPreferences(SharedPreferenceKey, Context.MODE_PRIVATE);
        mEditor = mPreferences.edit();
        mEditor.commit();
    }
    return mInstance;
}

public void saveInPreference(String key, String value) {
    mEditor.putString(key, value);
    mEditor.commit();
}

public String getFromPreference(String key) {
    return mPreferences.getString(key, "");
}

public LoginModel getUserDetails() {
    String userJson = mPreferences.getString(PrefrenceConstants.KEY_USER_JSON_DETAILS, "");
    LoginModel user = null;
    if (userJson != null && !userJson.equals("")) {
        user = new Gson().fromJson(userJson, LoginModel.class);
    }
    return user;
}

public void saveUserDetails(LoginModel user) {
    mEditor.putString(PrefrenceConstants.KEY_USER_JSON_DETAILS, new Gson().toJson(user));
    mEditor.commit();
}

public boolean isAutoLogin() {
    String userJson = mPreferences.getString(PrefrenceConstants.KEY_USER_JSON_DETAILS, "");
    LoginModel user = null;
    if (userJson != null && !userJson.equals("")) {
        user = new Gson().fromJson(userJson, LoginModel.class);
        return user != null;
    } else {
        return false;
    }
}

}

您可以在此单例课程中直接传递pojo类或Model类 减轻精力

保重.. 享受。

答案 6 :(得分:0)

我提出了我的版本,该版本从Max Zonov的答案开始。 我创建了一个抽象类,实现了与特定项目无关的读写操作,因此适合在“核心”模块中进行定义。

import android.content.SharedPreferences

abstract class SharedPreferenceWrapper {

    protected lateinit var prefs: SharedPreferences

    fun isInitialized() = ::prefs.isInitialized

    open fun read(key: String, defValue: Any): Any? {
        return when(defValue){
            is String   -> prefs.getString(key, defValue)
            is Int      -> prefs.getInt(key, defValue)
            is Boolean  -> prefs.getBoolean(key, defValue)
            is Long     -> prefs.getLong(key, defValue)
            is Float    -> prefs.getFloat(key, defValue)
            is Set<*>   -> {
                        if(defValue.isNotEmpty() && defValue.elementAt(0) is String)    prefs.getStringSet(key, defValue as Set<String>)
                        else                                                            return null
            }
            else        -> null
        }
    }

    open fun write(key: String, value: Any):Any? {
        val prefsEditor: SharedPreferences.Editor = prefs.edit()
        with(prefsEditor) {
            when(value){
                is String   -> putString(key, value)
                is Int      -> putInt(key, value)
                is Boolean  -> putBoolean(key, value)
                is Long     -> putLong(key, value)
                is Float    -> putFloat(key, value)
                is Set<*>   -> {
                                if(value.isNotEmpty() && value.elementAt(0) is String)    putStringSet(key, value as Set<String>)
                                else                                                            return null
                }
                else        -> return null
            }
            commit()
        }
        return value
    }
}

然后,在我的特定项目中,我创建一个单例,该单例初始化先前定义的SharedPreference并对给定键进行完整性检查。

import android.content.Context
import androidx.preference.PreferenceManager
import org.albaspazio.core.sharedpreferences.SharedPreferenceWrapper


object PsySuitePreferences: SharedPreferenceWrapper() {

    private val validKeys:List<String> = listOf(
        "pref_delay_a1", "pref_delay_a2", "pref_delay_a3",
        "pref_delay_t1", "pref_delay_t2",
        "pref_delay_v1", "pref_delay_v2",
        "pref_main_email")


    //call it once
    fun init(context:Context, pref_name:String="", mode:Int = Context.MODE_PRIVATE){

        if(isInitialized()) return      // prevent multiple init

        prefs = if(pref_name.isEmpty()) PreferenceManager.getDefaultSharedPreferences(context)
                else                    context.getSharedPreferences(pref_name, mode)
    }

    override fun read(key: String, defValue: Any): Any?{
        return  if(!validKeys.contains(key) || !isInitialized()) null
                else super.read(key, defValue)
    }

    override fun write(key: String, value: Any): Any?{
        return  if(!validKeys.contains(key) || !isInitialized())    null
                else super.write(key, value)
    }
}

ciao