在将 EncryptedSharePreferences 与 AndroidX Preferences GUI 集成时,我遇到了一些问题。
- PreferenceManager 无法全局设置默认首选项数据存储,因为只能检索默认共享首选项(未加密的变体并与应用程序包名称相关联),而不能将默认共享首选项设置为加密的变体。
PreferenceManager.getDefaultSharedPreferences(context);
没有对应的set方法。
- PreferenceManager 只能由具有相同库组的包创建。
我创建的解决方案不依赖于 SharedPreferences,而是利用 PreferenceFragmentCompat 写入 EncryptedPreferenceDataStore。但是,这仍然会带来默认值在用户进入 Preference 屏幕之前不会初始化的问题。
依赖项
dependencies {
implementation 'androidx.preference:preference:1.1.1'
implementation 'androidx.security:security-crypto:1.0.0-rc01'
}
首选项片段
import android.app.Activity;
import android.app.ActivityManager;
import android.os.Bundle;
import androidx.preference.Preference;
import androidx.preference.PreferenceFragmentCompat;
import androidx.preference.PreferenceManager;
public class PreferencesFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
PreferenceManager preferenceManager = getPreferenceManager();
preferenceManager.setPreferenceDataStore(EncryptedPreferenceDataStore.getInstance(getContext()));
// Load the preferences from an XML resource
setPreferencesFromResource(R.xml.preferences, rootKey);
}
加密偏好数据存储
import android.content.Context;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceDataStore;
import androidx.security.crypto.EncryptedSharedPreferences;
import java.util.Set;
public class EncryptedPreferenceDataStore extends PreferenceDataStore {
private static final String CONFIG_FILE_NAME = "FileName";
private static final String CONFIG_MASTER_KEY_ALIAS = "KeyAlias";
private static EncryptedPreferenceDataStore mInstance;
private SharedPreferences mSharedPreferences;
private Context mContext;
private EncryptedPreferenceDataStore(Context context) {
try {
mContext = context;
mSharedPreferences = EncryptedSharedPreferences.create(
CONFIG_FILE_NAME,
CONFIG_MASTER_KEY_ALIAS,
context,
EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV, //for encrypting Keys
EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM ////for encrypting Values
);
} catch (Exception e) {
// Fallback
mSharedPreferences = context.getSharedPreferences(CONFIG_FILE_NAME, Context.MODE_PRIVATE);
}
}
@Override
public void putString(String key, @Nullable String value) {
mSharedPreferences.edit().putString(key, value).apply();
}
@Override
public void putStringSet(String key, @Nullable Set<String> values) {
mSharedPreferences.edit().putStringSet(key, values).apply();
}
@Override
public void putInt(String key, int value) {
mSharedPreferences.edit().putInt(key, value).apply();
}
@Override
public void putLong(String key, long value) {
mSharedPreferences.edit().putLong(key, value).apply();
}
@Override
public void putFloat(String key, float value) {
mSharedPreferences.edit().putFloat(key, value).apply();
}
@Override
public void putBoolean(String key, boolean value) {
mSharedPreferences.edit().putBoolean(key, value).apply();
}
@Nullable
@Override
public String getString(String key, @Nullable String defValue) {
return mSharedPreferences.getString(key, defValue);
}
@Nullable
@Override
public Set<String> getStringSet(String key, @Nullable Set<String> defValues) {
return mSharedPreferences.getStringSet(key, defValues);
}
@Override
public int getInt(String key, int defValue) {
return mSharedPreferences.getInt(key, defValue);
}
@Override
public long getLong(String key, long defValue) {
return mSharedPreferences.getLong(key, defValue);
}
@Override
public float getFloat(String key, float defValue) {
return mSharedPreferences.getFloat(key, defValue);
}
@Override
public boolean getBoolean(String key, boolean defValue) {
return mSharedPreferences.getBoolean(key, defValue);
}
}
用法
EncryptedPreferenceDataStore prefs = EncryptedPreferenceDataStore.getInstance(getContext());
boolean bIsXXX = prefs.getBoolean(getString(R.string.pref_access_xxx), true);