Java tutorial
/* * Copyright (c) 2016 ?. All Rights Reserved. * Use of this source code is governed by a Shanghai Unovo Information Technology Co.,Ltd license * that can be found in the LICENSE file in the root of the web site. * * http://www.unovo.com.cn * * An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. * */ package com.unovo.frame.utils; import android.content.Context; import android.content.SharedPreferences; import android.support.v4.content.SharedPreferencesCompat; import android.support.v4.util.ArrayMap; import com.unovo.frame.utils.log.Logger; import com.unovo.frame.utils.reflector.Reflector; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Map; import java.util.Set; /** * SharedPreferences Helper * SharedPreferences??Bean * ??"KEY" * <p> * Created by qiujuer * on 2016/10/27. */ public final class SharedPreferencesHelper { private static final String SEPARATOR = "#"; private static final String TAG = SharedPreferencesHelper.class.getName(); /** * Bean.class{@link SharedPreferences} ? * SharedPreferences??Bean??? * * @param context Context * @param clx Bean'class * @param <T> Any Bean * @return {@link SharedPreferences} */ public static <T> SharedPreferences getSharedPreferences(Context context, Class<T> clx) { return context.getSharedPreferences(clx.getName(), Context.MODE_PRIVATE); } /** * ?Bean{@link SharedPreferences} * {@link #getSharedPreferences(Context, Class)} * SharedPreferences?? * * @param context Context * @param t Bean * @param <T> Any Bean * @return ??? */ public static <T> boolean save(Context context, T t) { final Class<?> clx = t.getClass(); // We should remove all data before save data remove(context, clx); // Get all data form t Map<String, Data> map = new ArrayMap<>(); buildValuesToMap(clx, t, "", map); SharedPreferences sp = getSharedPreferences(context, clx); SharedPreferences.Editor editor = sp.edit(); // Get all existing key Set<String> existKeys = sp.getAll().keySet(); // Foreach the sava data Set<String> keys = map.keySet(); for (String key : keys) { Data data = map.get(key); final Class<?> type = data.type; final Object value = data.value; try { if (value == null) { removeKeyFamily(editor, existKeys, key); } else if (type.equals(Byte.class) || type.equals(byte.class)) { editor.putInt(key, (Byte) value); } else if (type.equals(Short.class) || type.equals(short.class)) { editor.putInt(key, (Short) value); } else if (type.equals(Integer.class) || type.equals(int.class)) { editor.putInt(key, (Integer) value); } else if (type.equals(Long.class) || type.equals(long.class)) { editor.putLong(key, (Long) value); } else if (type.equals(Float.class) || type.equals(float.class)) { editor.putFloat(key, (Float) value); } else if (type.equals(Double.class) || type.equals(double.class)) { editor.putString(key, (String.valueOf(value))); } else if (type.equals(Boolean.class) || type.equals(boolean.class)) { editor.putBoolean(key, (Boolean) value); } else if (type.equals(Character.class) || type.equals(char.class)) { editor.putString(key, value.toString()); } else if (type.equals(String.class)) { editor.putString(key, value.toString()); } else { Logger.e(TAG, String.format("Con't support save this type:%s, value:%s, key:%s", type, value, key)); } } catch (IllegalArgumentException e) { Logger.e(TAG, "Save error:" + e.getMessage()); } } SharedPreferencesCompat.EditorCompat.getInstance().apply(editor); return true; } /** * {@link SharedPreferences}?Bean * SharedPreferences??NULL * * @param context Context * @param clx Bean'class * @param <T> Any Bean * @return ?Bean */ @SuppressWarnings("unchecked") public static <T> T load(Context context, Class<T> clx) { SharedPreferences sp = getSharedPreferences(context, clx); // Get all existing key Set<String> existKeys = sp.getAll().keySet(); if (existKeys.size() == 0) return null; return (T) buildTargetFromSource(clx, null, "", existKeys, sp); } /** * {@link SharedPreferences}?Bean * SharedPreferences??NULL * {@link #load(Context, Class)} * ?????{@link SharedPreferences} * * @param context Context * @param clx Bean'class * @param <T> Any Bean * @return ?Bean */ @SuppressWarnings("unchecked") public static <T> T loadFormSource(Context context, Class<T> clx) { SharedPreferences sp = getSharedPreferences(context, clx); // Use reflection to force refresh data try { Reflector.with(sp).call("startReloadIfChangedUnexpectedly"); } catch (Exception e) { e.printStackTrace(); } // Get all existing key Set<String> existKeys = sp.getAll().keySet(); if (existKeys.size() == 0) return null; return (T) buildTargetFromSource(clx, null, "", existKeys, sp); } /** * Bean{@link SharedPreferences}? * ?{@link #load(Context, Class)}, {@link #loadFormSource(Context, Class)} NULL * * @param context Context * @param clx Bean'class * @param <T> Any Bean */ public static <T> void remove(Context context, Class<T> clx) { SharedPreferences sp = getSharedPreferences(context, clx); SharedPreferences.Editor editor = sp.edit(); editor.clear(); SharedPreferencesCompat.EditorCompat.getInstance().apply(editor); } private static <T> void buildValuesToMap(Class<?> clx, T t, String preFix, Map<String, Data> map) { if (clx == null || clx.equals(Object.class) || t == null) { return; } final Field[] fields = clx.getDeclaredFields(); if (fields == null || fields.length == 0) return; // Foreach fields for (Field field : fields) { if (isContSupport(field)) continue; final String fieldName = field.getName(); Class<?> fieldType = field.getType(); // Change the Field accessible status boolean isAccessible = field.isAccessible(); if (!isAccessible) field.setAccessible(true); Object value; try { value = field.get(t); } catch (IllegalArgumentException | IllegalAccessException e) { Logger.e(TAG, "buildValuesToMap error:" + e.getMessage()); continue; } if (isBasicType(fieldType)) { String key = preFix + fieldName; if (!map.containsKey(key)) { map.put(key, new Data(fieldType, value)); } } else { buildValuesToMap(fieldType, value, preFix + fieldName + SEPARATOR, map); } } // Get super class fields buildValuesToMap(clx.getSuperclass(), t, preFix, map); } @SuppressWarnings("TryWithIdenticalCatches") private static <T> Object buildTargetFromSource(Class<T> clx, T target, String preFix, Set<String> existKeys, SharedPreferences sp) { // Each to Object if (clx == null || clx.equals(Object.class)) { return target; } // Create instance if (target == null) { try { target = clx.newInstance(); } catch (InstantiationException e) { e.printStackTrace(); return null; } catch (IllegalAccessException e) { e.printStackTrace(); return null; } } // Get the class fields Field[] fields = clx.getDeclaredFields(); if (fields == null || fields.length == 0) return target; // Foreach fields for (Field field : fields) { if (isContSupport(field)) continue; final String fieldName = field.getName(); Class<?> fieldType = field.getType(); // Change the Field accessible status boolean isAccessible = field.isAccessible(); if (!isAccessible) field.setAccessible(true); // Build the key String key = preFix + fieldName; // Get target value Object value = null; if (isBasicType(fieldType)) { if (existKeys.contains(key)) { // From the share map if (fieldType.equals(Byte.class) || fieldType.equals(byte.class)) { value = (byte) sp.getInt(key, 0); } else if (fieldType.equals(Short.class) || fieldType.equals(short.class)) { value = (short) sp.getInt(key, 0); } else if (fieldType.equals(Integer.class) || fieldType.equals(int.class)) { value = sp.getInt(key, 0); } else if (fieldType.equals(Long.class) || fieldType.equals(long.class)) { value = sp.getLong(key, 0); } else if (fieldType.equals(Float.class) || fieldType.equals(float.class)) { value = sp.getFloat(key, 0); } else if (fieldType.equals(Double.class) || fieldType.equals(double.class)) { value = Double.valueOf(sp.getString(key, "0.00")); } else if (fieldType.equals(Boolean.class) || fieldType.equals(boolean.class)) { value = sp.getBoolean(key, false); } else if (fieldType.equals(Character.class) || fieldType.equals(char.class)) { value = sp.getString(key, "").charAt(0); } else if (fieldType.equals(String.class)) { value = sp.getString(key, ""); } } } else { value = buildTargetFromSource(fieldType, null, preFix + fieldName + SEPARATOR, existKeys, sp); } // Set the field value if (value != null) { try { field.set(target, value); } catch (IllegalAccessException e) { e.printStackTrace(); Logger.e(TAG, String.format("Set field error, Key:%s, type:%s, value:%s", key, fieldType, value)); } } else { Logger.e(TAG, String.format("Get field value error, Key:%s, type:%s", key, fieldType)); } } // Get super class fields return buildTargetFromSource(clx.getSuperclass(), target, preFix, existKeys, sp); } private static void removeKeyFamily(SharedPreferences.Editor editor, Set<String> existKeys, String removeKey) { String preFix = removeKey + SEPARATOR; for (String str : existKeys) { if (str.equals(removeKey) || str.startsWith(preFix)) editor.remove(str); } } private static boolean isContSupport(Field field) { return (Modifier.isStatic(field.getModifiers()) || Modifier.isFinal(field.getModifiers()) || Modifier.isAbstract(field.getModifiers())); } private static boolean isBasicType(Class<?> clx) { return clx.equals(Byte.class) || clx.equals(byte.class) || clx.equals(Short.class) || clx.equals(short.class) || clx.equals(Integer.class) || clx.equals(int.class) || clx.equals(Long.class) || clx.equals(long.class) || clx.equals(Float.class) || clx.equals(float.class) || clx.equals(Double.class) || clx.equals(double.class) || clx.equals(Boolean.class) || clx.equals(boolean.class) || clx.equals(Character.class) || clx.equals(char.class) || clx.equals(String.class); } private static class Data { Class<?> type; Object value; Data(Class<?> type, Object value) { this.type = type; this.value = value; } } }