在我们要保存一些数据量小且需要持久化的数据时,我们通常会使用SharedPreference来保存。正常情况下
是没有任何问题的。但我们知道SharedPreference保存实际上是保存一份XML文件。
我们访问SharedPreference资源:
首次访问会读取xml文件,把所有内容读入缓存。之后在该进程中都是从缓存中拿。
写入:Editor & apply
都是先写入缓存:
但是 Editor 是同步的: 写入缓存后提交写入Xml文件,完成后返回结果
apply: 写入缓存后 把写文件操作放入单线程池直接返回 QueuedWork.singleThreadExecutor().execute(writeToDiskRunnable)
因为Android在系统中进程间的内存是不能共享的,所以SharedPreference在进程间是无法直接公用的。下面我们就来讲下
SharedPreference的跨进程使用。
思路: 通常跨进程通行的方式有:
我这选用 ContentProvider
public class TestActivity extends Activity{ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ((TextView)findViewById(R.id.title)).setText("Activity1"); findViewById(R.id.btn0).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { startActivity(new Intent(TestActivity.this, TestActivity2.class)); } }); findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { putValues(TestActivity.this); } }); findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { int pid = android.os.Process.myPid(); Log.e("getValues", "pid:" + pid + " + processName:" + getAppName(TestActivity.this, pid)); getValues(TestActivity.this); } }); findViewById(R.id.btn3).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { testOther(TestActivity.this); } }); } private static void putValues(Context ctx) { SharedPreferences preferences = PreferenceUtil.getSharedPreference(ctx, "test"); Set<String> set = new HashSet<>(); set.add("well"); preferences.edit().putString("key1", "ok") .putStringSet("key2", set) .putLong("key3",30231L) .putBoolean("key4", true) .putFloat("key5", 0.3f) .putInt("key6", 6) .apply(); int pid = android.os.Process.myPid(); Log.e("getValues", "pid:" + pid); } private static void getValues(Context ctx) { SharedPreferences preferences = PreferenceUtil.getSharedPreference(ctx, "test"); Log.e("getValues", "Values:" + preferences.getString("key1", "xxx") + "/n" + "key3:" + preferences.getLong("key3", 0L) + "/n" + "key4:" + preferences.getBoolean("key4", false) + "/n" + "key5:" + preferences.getFloat("key5", 0.f) + "/n" + "key6:" + preferences.getInt("key6", 1) ); } private static void testOther(Context ctx) { //2 Test clear and default SharedPreferences preferences = PreferenceUtil.getSharedPreference(ctx, "test"); preferences.edit().clear().commit(); Assert.assertEquals("xxx", preferences.getString("key1", "xxx")); Assert.assertEquals(null, preferences.getStringSet("key2", null)); Assert.assertEquals(2L, preferences.getLong("key3", 2L)); Assert.assertEquals(false, preferences.getBoolean("key4", false)); Assert.assertEquals(0.4f, preferences.getFloat("key5", 0.4f)); Assert.assertEquals(1, preferences.getInt("key6", 1)); //3 Test remove preferences.edit().putInt("key6", 4).commit(); Assert.assertEquals(4, preferences.getInt("key6", 1)); preferences.edit().remove("key6").apply(); Assert.assertEquals(3, preferences.getInt("key6", 3)); } private static void ChangeValues(Context ctx) { SharedPreferences preferences = PreferenceUtil.getSharedPreference(ctx, "test"); Set<String> set = new HashSet<>(); set.add("well"); preferences.edit().putString("key1", "no") .putStringSet("key2", set) .putLong("key3",60262L) .putBoolean("key4", false) .putFloat("key5", 0.6f) .putInt("key6", 12) .apply(); int pid = android.os.Process.myPid(); Log.e("ChangeValues", "pid:" + pid); } /** * Activity in another process, the same with SharedPreferenceProvider */ public static class TestActivity2 extends Activity{ @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ((TextView)findViewById(R.id.title)).setText("Activity2"); findViewById(R.id.btn0).setVisibility(View.GONE); findViewById(R.id.btn1).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { ChangeValues(TestActivity2.this); } }); findViewById(R.id.btn2).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { int pid = android.os.Process.myPid(); Log.e("getValues", "pid:" + pid + " processName:" + getAppName(TestActivity2.this, pid)); getValues(TestActivity2.this); } }); findViewById(R.id.btn3).setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View v) { testOther(TestActivity2.this); } }); } } /** 2 * 根据Pid获取当前进程的名字,一般就是当前app的包名 3 * 4 * @param context 上下文 5 * @param pid 进程的id 6 * @return 返回进程的名字 7 */ private static String getAppName(Context context, int pid) { ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); List list = activityManager.getRunningAppProcesses(); Iterator i = list.iterator(); while (i.hasNext()) { ActivityManager.RunningAppProcessInfo info = (ActivityManager.RunningAppProcessInfo) (i.next()); try { if (info.pid == pid) { // 根据进程的信息获取当前进程的名字 return info.processName; } } catch (Exception e) { e.printStackTrace(); } } // 没有匹配的项,返回为null return null; } }
package com.lain.prefercene; import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Process; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.util.ArrayMap; import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; public class SharedPreferenceProxy implements SharedPreferences { private static Map<String, SharedPreferenceProxy> sharedPreferenceProxyMap; /** * Flag whether caller process is the same with provider * 0: unknown * 1: the same * -1: not the same */ private static AtomicInteger processFlag = new AtomicInteger(0); private Context ctx; private String preferName; private SharedPreferenceProxy(Context ctx, String name) { this.ctx = ctx.getApplicationContext(); this.preferName = name; } @Override public Map<String, ?> getAll() { throw new UnsupportedOperationException("Not support method getAll"); } @Nullable @Override public String getString(String key, @Nullable String defValue) { OpEntry result = getResult(OpEntry.obtainGetOperation(key).setStringValue(defValue)); return result == null ? defValue : result.getStringValue(defValue); } @Nullable @Override public Set<String> getStringSet(String key, @Nullable Set<String> defValues) { OpEntry result = getResult(OpEntry.obtainGetOperation(key).setStringSettingsValue(defValues)); if (result == null) { return defValues; } Set<String> set = result.getStringSet(); if (set == null) { return defValues; } return set; } @Override public int getInt(String key, int defValue) { OpEntry result = getResult(OpEntry.obtainGetOperation(key).setIntValue(defValue)); return result == null ? defValue : result.getIntValue(defValue); } @Override public long getLong(String key, long defValue) { OpEntry result = getResult(OpEntry.obtainGetOperation(key).setLongValue(defValue)); return result == null ? defValue : result.getLongValue(defValue); } @Override public float getFloat(String key, float defValue) { OpEntry result = getResult(OpEntry.obtainGetOperation(key).setFloatValue(defValue)); return result == null ? defValue : result.getFloatValue(defValue); } @Override public boolean getBoolean(String key, boolean defValue) { OpEntry result = getResult(OpEntry.obtainGetOperation(key).setBooleanValue(defValue)); return result == null ? defValue : result.getBooleanValue(defValue); } @Override public boolean contains(String key) { Bundle input = new Bundle(); input.putString(OpEntry.KEY_KEY, key); try { Bundle res = ctx.getContentResolver().call(PreferenceUtil.URI , PreferenceUtil.METHOD_CONTAIN_KEY , preferName , input); return res.getBoolean(PreferenceUtil.KEY_VALUES); } catch (Exception e) { e.printStackTrace(); return false; } } @Override public Editor edit() { return new EditorImpl(); } @Override public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { throw new UnsupportedOperationException(); } @Override public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) { throw new UnsupportedOperationException(); } private OpEntry getResult(@NonNull OpEntry input) { try { Bundle res = ctx.getContentResolver().call(PreferenceUtil.URI , PreferenceUtil.METHOD_QUERY_VALUE , preferName , input.getBundle()); return new OpEntry(res); } catch (Exception e) { e.printStackTrace(); return null; } } public class EditorImpl implements Editor { private ArrayList<OpEntry> mModified = new ArrayList<>(); @Override public Editor putString(String key, @Nullable String value) { OpEntry entry = OpEntry.obtainPutOperation(key).setStringValue(value); return addOps(entry); } @Override public Editor putStringSet(String key, @Nullable Set<String> values) { OpEntry entry = OpEntry.obtainPutOperation(key).setStringSettingsValue(values); return addOps(entry); } @Override public Editor putInt(String key, int value) { OpEntry entry = OpEntry.obtainPutOperation(key).setIntValue(value); return addOps(entry); } @Override public Editor putLong(String key, long value) { OpEntry entry = OpEntry.obtainPutOperation(key).setLongValue(value); return addOps(entry); } @Override public Editor putFloat(String key, float value) { OpEntry entry = OpEntry.obtainPutOperation(key).setFloatValue(value); return addOps(entry); } @Override public Editor putBoolean(String key, boolean value) { OpEntry entry = OpEntry.obtainPutOperation(key).setBooleanValue(value); return addOps(entry); } @Override public Editor remove(String key) { OpEntry entry = OpEntry.obtainRemoveOperation(key); return addOps(entry); } @Override public Editor clear() { return addOps(OpEntry.obtainClear()); } @Override public boolean commit() { Bundle input = new Bundle(); input.putParcelableArrayList(PreferenceUtil.KEY_VALUES, convertBundleList()); input.putInt(OpEntry.KEY_OP_TYPE, OpEntry.OP_TYPE_COMMIT); Bundle res = null; try { res = ctx.getContentResolver().call(PreferenceUtil.URI, PreferenceUtil.METHOD_EIDIT_VALUE, preferName, input); } catch (Exception e) { e.printStackTrace(); } if (res == null) { return false; } return res.getBoolean(PreferenceUtil.KEY_VALUES, false); } @Override public void apply() { Bundle intput = new Bundle(); intput.putParcelableArrayList(PreferenceUtil.KEY_VALUES, convertBundleList()); intput.putInt(OpEntry.KEY_OP_TYPE, OpEntry.OP_TYPE_APPLY); try { ctx.getContentResolver().call(PreferenceUtil.URI, PreferenceUtil.METHOD_EIDIT_VALUE, preferName, intput); } catch (Exception e) { e.printStackTrace(); } } private Editor addOps(OpEntry op) { synchronized (this) { mModified.add(op); return this; } } private ArrayList<Bundle> convertBundleList() { synchronized (this) { ArrayList<Bundle> bundleList = new ArrayList<>(mModified.size()); for (OpEntry entry : mModified) { bundleList.add(entry.getBundle()); } return bundleList; } } } public static SharedPreferences getSharedPreferences(@NonNull Context ctx, String preferName) { //First check if the same process if (processFlag.get() == 0) { Bundle bundle = ctx.getContentResolver().call(PreferenceUtil.URI, PreferenceUtil.METHOD_QUERY_PID, "", null); int pid = 0; if (bundle != null) { pid = bundle.getInt(PreferenceUtil.KEY_VALUES); // pid = android.os.Process.myPid(); } //Can not get the pid, something wrong! if (pid == 0) { return getFromLocalProcess(ctx, preferName); } processFlag.set(Process.myPid() == pid ? 1 : -1); return getSharedPreferences(ctx, preferName); } else if (processFlag.get() > 0) { return getFromLocalProcess(ctx, preferName); } else { return getFromRemoteProcess(ctx, preferName); } } private static SharedPreferences getFromRemoteProcess(@NonNull Context ctx, String preferName) { synchronized (SharedPreferenceProxy.class) { if (sharedPreferenceProxyMap == null) { sharedPreferenceProxyMap = new ArrayMap<>(); } SharedPreferenceProxy preferenceProxy = sharedPreferenceProxyMap.get(preferName); if (preferenceProxy == null) { preferenceProxy = new SharedPreferenceProxy(ctx.getApplicationContext(), preferName); sharedPreferenceProxyMap.put(preferName, preferenceProxy); } return preferenceProxy; } } private static SharedPreferences getFromLocalProcess(@NonNull Context ctx, String preferName) { return ctx.getSharedPreferences(preferName, Context.MODE_PRIVATE); } }
package com.lain.prefercene; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.content.SharedPreferences; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Process; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.util.ArrayMap; import java.util.ArrayList; import java.util.HashSet; import java.util.Map; import java.util.Set; public class SharedPreferenceProvider extends ContentProvider{ private Map<String, MethodProcess> processerMap = new ArrayMap<>(); @Override public boolean onCreate() { processerMap.put(PreferenceUtil.METHOD_QUERY_VALUE, methodQueryValues); processerMap.put(PreferenceUtil.METHOD_CONTAIN_KEY, methodContainKey); processerMap.put(PreferenceUtil.METHOD_EIDIT_VALUE, methodEditor); processerMap.put(PreferenceUtil.METHOD_QUERY_PID, methodQueryPid); return true; } @Nullable @Override public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) { throw new UnsupportedOperationException(); } @Nullable @Override public String getType(@NonNull Uri uri) { throw new UnsupportedOperationException(); } @Nullable @Override public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) { throw new UnsupportedOperationException(); } @Override public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) { throw new UnsupportedOperationException(); } @Override public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) { throw new UnsupportedOperationException(); } @Nullable @Override public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) { MethodProcess processer = processerMap.get(method); return processer == null?null:processer.process(arg, extras); } public interface MethodProcess { Bundle process(@Nullable String arg, @Nullable Bundle extras); } private MethodProcess methodQueryPid = new MethodProcess() { @Override public Bundle process(@Nullable String arg, @Nullable Bundle extras) { Bundle bundle = new Bundle(); bundle.putInt(PreferenceUtil.KEY_VALUES, Process.myPid()); return bundle; } }; private MethodProcess methodQueryValues = new MethodProcess() { @Override public Bundle process(@Nullable String arg, @Nullable Bundle extras) { if (extras == null) { throw new IllegalArgumentException("methodQueryValues, extras is null!"); } Context ctx = getContext(); if (ctx == null) { throw new IllegalArgumentException("methodQueryValues, ctx is null!"); } String key = extras.getString(OpEntry.KEY_KEY); SharedPreferences preferences = ctx.getSharedPreferences(arg, Context.MODE_PRIVATE); int valueType = extras.getInt(OpEntry.KEY_VALUE_TYPE); switch (valueType) { case OpEntry.VALUE_TYPE_BOOLEAN:{ boolean value = preferences.getBoolean(key, extras.getBoolean(OpEntry.KEY_VALUE)); extras.putBoolean(OpEntry.KEY_VALUE, value); return extras; } case OpEntry.VALUE_TYPE_FLOAT:{ float value = preferences.getFloat(key, extras.getFloat(OpEntry.KEY_VALUE)); extras.putFloat(OpEntry.KEY_VALUE, value); return extras; } case OpEntry.VALUE_TYPE_INT:{ int value = preferences.getInt(key, extras.getInt(OpEntry.KEY_VALUE)); extras.putInt(OpEntry.KEY_VALUE, value); return extras; } case OpEntry.VALUE_TYPE_LONG:{ long value = preferences.getLong(key, extras.getLong(OpEntry.KEY_VALUE)); extras.putLong(OpEntry.KEY_VALUE, value); return extras; } case OpEntry.VALUE_TYPE_STRING:{ String value = preferences.getString(key, extras.getString(OpEntry.KEY_VALUE)); extras.putString(OpEntry.KEY_VALUE, value); return extras; } case OpEntry.VALUE_TYPE_STRING_SET:{ Set<String> value = preferences.getStringSet(key, null); extras.putStringArrayList(OpEntry.KEY_VALUE, value == null?null:new ArrayList<>(value)); return extras; } default:{ throw new IllegalArgumentException("unknown valueType:" + valueType); } } } }; private MethodProcess methodContainKey = new MethodProcess() { @Override public Bundle process(@Nullable String arg, @Nullable Bundle extras) { if (extras == null) { throw new IllegalArgumentException("methodQueryValues, extras is null!"); } Context ctx = getContext(); if (ctx == null) { throw new IllegalArgumentException("methodQueryValues, ctx is null!"); } String key = extras.getString(OpEntry.KEY_KEY); SharedPreferences preferences = ctx.getSharedPreferences(arg, Context.MODE_PRIVATE); extras.putBoolean(PreferenceUtil.KEY_VALUES, preferences.contains(key)); return extras; } }; private MethodProcess methodEditor = new MethodProcess() { @Override public Bundle process(@Nullable String arg, @Nullable Bundle extras) { if (extras == null) { throw new IllegalArgumentException("methodQueryValues, extras is null!"); } Context ctx = getContext(); if (ctx == null) { throw new IllegalArgumentException("methodQueryValues, ctx is null!"); } SharedPreferences preferences = ctx.getSharedPreferences(arg, Context.MODE_PRIVATE); ArrayList<Bundle> ops = extras.getParcelableArrayList(PreferenceUtil.KEY_VALUES); if (ops == null) { ops = new ArrayList<>(); } SharedPreferences.Editor editor = preferences.edit(); for (Bundle opBundler : ops) { int opType = opBundler.getInt(OpEntry.KEY_OP_TYPE); switch (opType) { case OpEntry.OP_TYPE_PUT: { editor = editValue(editor, opBundler); break; } case OpEntry.OP_TYPE_REMOVE: { editor = editor.remove(opBundler.getString(OpEntry.KEY_KEY)); break; } case OpEntry.OP_TYPE_CLEAR: { editor = editor.clear(); break; } default: { throw new IllegalArgumentException("unkonw op type:" + opType); } } } int applyOrCommit = extras.getInt(OpEntry.KEY_OP_TYPE); if (applyOrCommit == OpEntry.OP_TYPE_APPLY) { editor.apply(); return null; } else if (applyOrCommit == OpEntry.OP_TYPE_COMMIT) { boolean res = editor.commit(); Bundle bundle = new Bundle(); bundle.putBoolean(PreferenceUtil.KEY_VALUES, res); return bundle; } else { throw new IllegalArgumentException("unknown applyOrCommit:" + applyOrCommit); } } private SharedPreferences.Editor editValue(SharedPreferences.Editor editor, Bundle opBundle) { String key = opBundle.getString(OpEntry.KEY_KEY); int valueType = opBundle.getInt(OpEntry.KEY_VALUE_TYPE); switch (valueType) { case OpEntry.VALUE_TYPE_BOOLEAN: { return editor.putBoolean(key, opBundle.getBoolean(OpEntry.KEY_VALUE)); } case OpEntry.VALUE_TYPE_FLOAT: { return editor.putFloat(key, opBundle.getFloat(OpEntry.KEY_VALUE)); } case OpEntry.VALUE_TYPE_INT: { return editor.putInt(key, opBundle.getInt(OpEntry.KEY_VALUE)); } case OpEntry.VALUE_TYPE_LONG: { return editor.putLong(key, opBundle.getLong(OpEntry.KEY_VALUE)); } case OpEntry.VALUE_TYPE_STRING: { return editor.putString(key, opBundle.getString(OpEntry.KEY_VALUE)); } case OpEntry.VALUE_TYPE_STRING_SET: { ArrayList<String> list = opBundle.getStringArrayList(OpEntry.KEY_VALUE); if (list == null) { return editor.putStringSet(key, null); } return editor.putStringSet(key, new HashSet<>(list)); } default: { throw new IllegalArgumentException("unknown valueType:" + valueType); } } } }; }
package com.lain.prefercene; import android.content.Context; import android.content.SharedPreferences; import android.net.Uri; import android.support.annotation.NonNull; public class PreferenceUtil { public static final String METHOD_CONTAIN_KEY = "method_contain_key"; public static final String AUTHORITY = "com.lain.preference"; public static final Uri URI = Uri.parse("content://" + AUTHORITY); public static final String METHOD_QUERY_VALUE = "method_query_value"; public static final String METHOD_EIDIT_VALUE = "method_edit"; public static final String METHOD_QUERY_PID = "method_query_pid"; public static final String KEY_VALUES = "key_result"; public static final Uri sContentCreate = Uri.withAppendedPath(URI, "create"); public static final Uri sContentChanged = Uri.withAppendedPath(URI, "changed"); public static SharedPreferences getSharedPreference(@NonNull Context ctx, String preferName) { return SharedPreferenceProxy.getSharedPreferences(ctx, preferName); } }
package com.lain.prefercene; import android.os.Bundle; import android.support.annotation.NonNull; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; class OpEntry { static final int OP_TYPE_GET = 1; static final int OP_TYPE_PUT = 2; static final int OP_TYPE_CLEAR = 3; static final int OP_TYPE_REMOVE = 4; static final int OP_TYPE_COMMIT = 5; static final int OP_TYPE_APPLY = 6; static final int VALUE_TYPE_STRING = 1; static final int VALUE_TYPE_INT = 2; static final int VALUE_TYPE_LONG = 3; static final int VALUE_TYPE_FLOAT = 4; static final int VALUE_TYPE_BOOLEAN = 5; static final int VALUE_TYPE_STRING_SET = 6; static final String KEY_KEY = "key_key"; static final String KEY_VALUE = "key_value"; static final String KEY_VALUE_TYPE = "key_value_type"; static final String KEY_OP_TYPE = "key_op_type"; @NonNull private Bundle bundle; private OpEntry() { this.bundle = new Bundle(); } public OpEntry(@NonNull Bundle bundle) { this.bundle = bundle; } public String getKey() { return bundle.getString(KEY_KEY, null); } public OpEntry setKey(String key) { bundle.putString(KEY_KEY, key); return this; } public int getValueType() { return bundle.getInt(KEY_VALUE_TYPE, 0); } public OpEntry setValueType(int valueType) { bundle.putInt(KEY_VALUE_TYPE, valueType); return this; } public int getOpType() { return bundle.getInt(KEY_OP_TYPE, 0); } public OpEntry setOpType(int opType) { bundle.putInt(KEY_OP_TYPE, opType); return this; } public String getStringValue(String defValue) { return bundle.getString(KEY_VALUE, defValue); } public OpEntry setStringValue(String value) { bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_STRING); bundle.putString(KEY_VALUE, value); return this; } public int getIntValue(int defValue) { return bundle.getInt(KEY_VALUE, defValue); } public OpEntry setIntValue(int value) { bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_INT); bundle.putInt(KEY_VALUE, value); return this; } public long getLongValue(long defValue) { return bundle.getLong(KEY_VALUE, defValue); } public OpEntry setLongValue(long value) { bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_LONG); bundle.putLong(KEY_VALUE, value); return this; } public float getFloatValue(float defValue) { return bundle.getFloat(KEY_VALUE); } public OpEntry setFloatValue(float value) { bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_FLOAT); bundle.putFloat(KEY_VALUE, value); return this; } public boolean getBooleanValue(boolean defValue) { return bundle.getBoolean(KEY_VALUE, defValue); } public OpEntry setBooleanValue(boolean value) { bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_BOOLEAN); bundle.putBoolean(KEY_VALUE, value); return this; } public Set<String> getStringSet() { ArrayList<String> list = bundle.getStringArrayList(KEY_VALUE); return list == null ? null : new HashSet<>(list); } public Bundle getBundle() { return bundle; } public OpEntry setStringSettingsValue(Set<String> value) { bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_STRING_SET); bundle.putStringArrayList(KEY_VALUE, value == null ? null : new ArrayList<>(value)); return this; } static OpEntry obtainGetOperation(String key) { return new OpEntry() .setKey(key) .setOpType(OP_TYPE_GET); } static OpEntry obtainPutOperation(String key) { return new OpEntry() .setKey(key) .setOpType(OP_TYPE_PUT); } static OpEntry obtainRemoveOperation(String key) { return new OpEntry() .setKey(key) .setOpType(OP_TYPE_REMOVE); } static OpEntry obtainClear() { return new OpEntry() .setOpType(OP_TYPE_CLEAR); } }