项目地址:github.com/Nagi1225/Ne…

过段时间发现之前那篇《SharedPreferences的一种极简高雅且安全的用法》有蛮多人看,就修复了一些问题,把代码整理了下,并发布到jitpack以方便运用。本文是之前的总结归纳优化,思考进程可查阅前文,部分内容重复,添加以json格式存储JavaBean数据类的示例。

留意:作者比较懒,并且现在精力重心倾向PL和AI那儿,这个项目维护会很不及时(除非有重大问题),所以特意控制了下规划,整个库大约900行左右,希望看懂代码今后再运用。

新手入门

设置依靠

把下面装备添加到你的根目录build.gradle中在repositories的末尾:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

运用的模块中添加依靠:

dependencies {
        implementation 'com.github.Nagi1225:NeoPreference:0.1.0' //以实践版别为准
}

简单运用

创立装备:

@Config.Name(DemoConfig.NAME)
public interface DemoConfig extends Config {
    String NAME = "demo_config";
    Property<Integer> intProperty();
    @StringItem(supportEmpty = true)
    Property<String> stringProperty();
    @FloatItem(key = "height")
    Property<Float> floatProperty();
    @LongItem(key = "last_save_time")
    Property<Long> longProperty();
    @BooleanItem
    Property<Boolean> boolProperty();
    @StringSetItem(key = "collection_media_set", valueOf = {"mp3", "mp4", "png", "jpg", "mkv"})
    Property<Set<String>> collectMediaSet();
}

读写装备:

// 1. Get config instance
DemoConfig config = ConfigManager.getInstance().getConfig(DemoConfig.class);
// 2. Get property value
float fp = config.floatProperty().get();
// 3. Get property value or default value
int num = config.intProperty().get(-1);
// 4. Determines whether the property value exists
boolean present = config.longProperty().isPresent();
// 5. Use Optional to handle property 
config.stringProperty().opt().ifPresent((String str) -> {
    //handle string property
});

NeoPreference API 阐明

这个东西的API除了ConfigManager类以外主要分两部分:Property类以及类型对应的注解。

ConfigManager 接口阐明

ConfigManager是单例完成,维护一个SharedPreferencesConfig的注册表,供给getConfigaddListener两个办法。

以下是getConfig办法签名:

public <P extends Config> P getConfig(Class<P> pClass);
public <P extends Config> P getConfig(Class<P> pClass, int mode);

参数pClass是继承Config类的接口class,可选参数mode对应SharedPreferencesmode

addListener的办法监听指定preferenceName中内容的改变,签名如下:

public void addListener(String preferenceName, Listener listener);
public void addListener(LifecycleOwner lifecycleOwner, String preferenceName, Listener listener);
public void removeListener(String preferenceName, Listener listener);
public void removeListeners(String preferenceName);

第一个办法承受一个Listener,需求手动调用removeListenerremoveListeners,否则可能会内存溢出。第二个办法额定添加LifecycleOwner,这个监听器的声明周期选用LifecycleOwner对应的生命周期,在onDestroy时主动移除。

Property 类接口阐明

Property接口包含:

public final String getKey();//获取特点对应的key
public T get(T defValue);    //获取特点值,defValue为默认值
public T get();              //获取特点值,选用缺省默认值
public void set(T value);    //设置特点值
public Optional<T> opt();    //以Optional的方式返回特点值
public boolean exists();     //判别特点当时是否存在,没有set过就是false,set后即便是null也为true
public final void addListener(Listener<T> listener)    //相似ConfigManager,不过只监听该特点的值改变,需求手动remove
public final void addListener(LifecycleOwner owner, Listener<T> listener)//相似ConfigManager,不过只监听该特点的值改变,在owner onDestroy时主动remove

泛型参数支持LongIntegerFloatBooleanStringSet<String>SharedPreferences支持的几种类型。

类型相关注解介绍

这些注解对应SharedPreferences支持的几种类型(其中description字段暂时不用)。

@interface StringItem {
    String key() default "";
    boolean supportEmpty() default true;
    String[] valueOf() default {};
    String defaultValue() default "";
    String description() default "";
}
@interface BooleanItem {
    String key() default "";
    boolean defaultValue() default false;
    String description() default "";
}
@interface IntItem {
    String key() default "";
    int defaultValue() default 0;
    int start() default Integer.MIN_VALUE;
    int to() default Integer.MAX_VALUE;
    int[] valueOf() default {};
    String description() default "";
}
@interface LongItem {
    String key() default "";
    long defaultValue() default 0;
    long start() default Long.MIN_VALUE;
    long to() default Long.MAX_VALUE;
    long[] valueOf() default {};
    String description() default "";
}
@interface FloatItem {
    String key() default "";
    float defaultValue() default 0;
    float start() default -Float.MAX_VALUE;
    float to() default Float.MAX_VALUE;
    float[] valueOf() default {};
    String description() default "";
}
@interface StringSetItem {
    String key() default "";
    String[] valueOf() default {};
    String description() default "";
}
@interface SerializableItem {
    String key() default "";
    Class<?> type() default Object.class;
    String description() default "";
}

扩展存储类型

除了SharedPreferences原本支持的类型外,可以经过PropertyFactory来扩展类型,例如我们想以json的格式存储JavaBean:

  • 第一步:创立对应示意性接口和注解:
public interface JsonData {
    @interface JsonItem {
        String key();
        String description() default "";
    }
}
  • 第二步:创立对应的PropertyFactory并在运用前注册:
public class JsonPropertyFactory extends PropertyFactory<JsonData.JsonItem, JsonData> {
    private final static Gson gson = new Gson();
    @Override
    public Property<JsonData> createProperty(String key, JsonData.JsonItem annotation, String preferenceName, SharedPreferences preferences) {
        return new JsonProperty<>(key, annotation, preferenceName, preferences);
    }
    static class JsonProperty<T extends JsonData> extends Property.BaseProperty<T> {
        final String description;
        public JsonProperty(String key, JsonData.JsonItem annotation, String preferenceName, SharedPreferences preferences) {
            super(annotation != null ? annotation.key() : key, preferenceName, preferences);
            description = annotation != null ? annotation.description() : "no description";
        }
        @Override
        public String getValueString() {
            return getPreferences().getString(getKey(), "");
        }
        @Override
        public String getDescription() {
            return description;
        }
        @Override
        public T get(T defValue) {
            String data = getPreferences().getString(getKey(), "");
            if (TextUtils.isEmpty(data)) {
                return defValue;
            } else {
                int classEndIndex = data.indexOf(":");
                try {
                    Class<?> clazz = Class.forName(data.substring(0, classEndIndex));
                    String jsonStr = data.substring(classEndIndex + 1);
                    return (T) gson.fromJson(jsonStr, clazz);
                } catch (ClassNotFoundException | JsonSyntaxException e) {
                    throw new RuntimeException("parse data failed:", e);
                }
            }
        }
        @Override
        public T get() {
            return get(null);
        }
        @Override
        public void set(T value) {
            String className = value.getClass().getCanonicalName();
            getPreferences().edit().putString(getKey(), className + ":" + gson.toJson(value)).apply();
        }
    }
}

在运用前注册到ConfigManager

ConfigManager.registerFactory(new JsonPropertyFactory());
  • 第三步:让对应数据类完成前面定义的接口:
public class UserInfo implements JsonData{
    private String id;
    private String name;
    private int age;
    public UserInfo(String id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public UserInfo() {
    }
    //getter and setter...
}

这样就可以以json的格式存储JavaBean数据类了:

@Config.Name(DemoConfig.NAME)
public interface DemoConfig extends Config {
    String NAME = "demo_config";
    @JsonData.JsonItem(key = "current_user_info")
    Property<UserInfo> userInfo();
}
/* sample usage */
DemoConfig config = ConfigManager.getInstance().getConfig(DemoConfig.class);
config.userInfo().opt().ifPresentOrElse(userInfo -> {
    userInfo.setAge(userInfo.getAge() + 1);
    config.userInfo().set(userInfo);
    }, () -> {
    config.userInfo().set(new UserInfo("1", "Alice", 1));
});
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。