Android中使用动态代理巧妙的管理SharedPreferences配置项
RAE3,254人阅读
在Android应用程序中不少地方会使用SharedPreferences
来保存配置文件,这样你就会出现不少下面的写法:
// 程序配置文件
public class MyAppConfig {
SharedPreferences mSharedPreferences;
public MyAppConfig(Context context) {
mSharedPreferences = context.getSharedPreferences("AppConfig", Context.MODE_PRIVATE);
}
/**
* 设置配置1
*/
public void setConfig1(String value) {
mSharedPreferences.edit().putString("config1", value).apply();
}
/**
* 获取配置1
*/
public String getConfig1() {
return mSharedPreferences.getString("config1", "");
}
// ... 省略其他更多的配置项
}
这样一来我们就需要写很多跟SharedPreferences
打交道的代码,其实我们关注的只有SET方法
和GET方法
两个方法罢了。那有没有办法,我只需要定义好配置的接口就直接获取到值呢?这就是本篇文章要讨论的啦。其实我们理想效果应该是这样的:
// 定义一个配置接口
public interface ITestConfig {
// 定义获取配置的Get、Set方法
void setName(String name);
String getName();
}
// 实际操作中的调用
private void runTestMethod() {
// 能实现这样的效果就完美了
ITestConfig config = XXX.create(context, ITestConfig.class);
Log.i("rae", "结果:" + config.getName());
}
探索
不妨思考一下,这种需求好像我们在哪个地方见过呢?没错,就是我们经常用到的Retrofit
框架中就有用到,我能对public <T> T create(final Class<T> service){}
这个方法应该很熟悉了,我们好奇它为什么传入一个接口它就能构造出接口的实例呢?让我们走进源码看看:
public <T> T create(final Class<T> service) {
Utils.validateServiceInterface(service);
if (validateEagerly) {
eagerlyValidateMethods(service);
}
// 关键的地方来了:Proxy,动态代理!
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
// If the method is a method from Object then defer to normal invocation.
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
}
});
}
就是这个神奇的动态代理。具体什么是动态代理这里就不做解析了,有兴趣的同学可以看看这里介绍的Java动态代理。
动手前操作
知道大概的原理之后,我们就开始动手操作了。理一理咱们的思路,定义一个动态代理实现以下功能:
- 处理我们定义的Get、Set方法,利用
SharedPreferences
保存配置项 - 还可以处理一个
clear()
方法清除配置 - 还可以处理一个
remove(String key)
方法移除配置项 - 还要处理保存对象类型(利用
json
字符串来实现)
动态代理的类
/**
* 应用程序配置注解
* Created by rae on 2020-02-20.
* Copyright (c) https://github.com/raedev All rights reserved.
*/
@Documented
@Retention(RUNTIME)
public @interface Config {
/**
* 程序配置名称
*/
String value();
}
/**
* 应用程序代理类
* Created by rae on 2020-02-20.
* Copyright (c) https://github.com/raedev All rights reserved.
*/
public final class AppConfigHandler {
private AppConfigHandler() {
}
/**
* 创建程序配置代理类
*
* @param cls 类的Class
*/
@SuppressWarnings("unchecked")
public static <T> T create(Context context, Class<T> cls) {
Config config = cls.getAnnotation(Config.class);
if (config == null) {
throw new RuntimeException("请在配置类标注@Config()");
}
if (!cls.isInterface()) {
throw new RuntimeException("配置类必须是接口");
}
String configName = config.value();
if (TextUtils.isEmpty(configName)) {
configName = cls.getName();
}
SharedPreferences preferences = context.getSharedPreferences(configName, Context.MODE_PRIVATE);
// 创建动态代理
return (T) Proxy.newProxyInstance(cls.getClassLoader(), new Class<?>[]{cls}, new ConfigProxy(preferences));
}
private static class ConfigProxy implements InvocationHandler {
private final SharedPreferences mPreference;
private final Gson mGson = new Gson();
private ConfigProxy(SharedPreferences preference) {
this.mPreference = preference;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
String methodName = method.getName().toUpperCase();
// 清除配置文件
if (methodName.equalsIgnoreCase("clear")) {
mPreference.edit().clear().apply();
}
// 移除配置项处理
else if (methodName.equalsIgnoreCase("remove") && args != null) {
String key = args[0].toString().toUpperCase();
mPreference.edit().remove(key).apply();
}
// Get方法处理
else if (methodName.startsWith("SET")) {
setValue(methodName.replace("SET", ""), method, args);
}
// Set方法处理
else if (methodName.startsWith("GET")) {
return getValue(methodName.replace("GET", ""), method, args);
}
// Is方法处理,比如:isLogin()、isVip(),这类的布尔值
else if (methodName.startsWith("IS")) {
boolean value = mPreference.getBoolean(methodName.replace("IS", ""), false);
return value;
}
return null;
}
/**
* 设置配置值
*/
private void setValue(String name, Method method, Object[] args) {
if (args.length != 1) throw new IllegalArgumentException("set方法的方法参数只允许一个");
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> parameterType = parameterTypes[0];
Object arg = args[0];
SharedPreferences.Editor editor = mPreference.edit();
if (parameterType == String.class) {
editor.putString(name, (String) arg);
} else if (parameterType == int.class) {
editor.putInt(name, (int) arg);
} else if (parameterType == boolean.class) {
editor.putBoolean(name, (boolean) arg);
} else if (parameterType == float.class) {
editor.putFloat(name, (float) arg);
} else if (parameterType == long.class) {
editor.putLong(name, (long) arg);
} else {
// 其他值默认使用Json字符串
String json = mGson.toJson(arg);
editor.putString(name, json);
}
editor.apply();
}
/**
* 获取配置值
*/
private Object getValue(String name, Method method, Object[] args) {
Class<?> type = method.getReturnType();
Object defaultValue = args == null ? null : args[0];
if (type == String.class) {
return mPreference.getString(name, (String) defaultValue);
} else if (type == int.class) {
return mPreference.getInt(name, defaultValue == null ? 0 : (int) defaultValue);
} else if (type == boolean.class) {
return mPreference.getBoolean(name, defaultValue != null && (boolean) defaultValue);
} else if (type == float.class) {
return mPreference.getFloat(name, defaultValue == null ? 0 : (float) defaultValue);
} else if (type == long.class) {
return mPreference.getLong(name, defaultValue == null ? 0 : (long) defaultValue);
} else {
// 其他值默认使用Json字符串
String json = mPreference.getString(name, null);
return mGson.fromJson(json, type);
}
}
}
}
实践操作
最终我们定义一个接口,就可以轻松实现读取配置文件啦!
/**
* 程序配置
* Created by rae on 2020/2/22.
* Copyright (c) https://github.com/raedev All rights reserved.
*/
@Config("YourAppConfig")
public interface IAppConfig {
void setUserName(String name);
String getUserName(String defaultValue);
void setVip(boolean isVip);
boolean isVip();
void setVersion(int version);
int getVersion();
void clear();
void remove(String key);
}
方法调用
private void runTestMethod() {
IAppConfig config = AppConfigHandler.create(getApplicationContext(), IAppConfig.class);
config.clear();
config.setUserName("RAE");
config.remove("UserName");
Log.i("Rae", "username is " + config.getUserName("DefaultValue"));
config.setVip(true);
Log.i("Rae", "is vip: " + config.isVip());
config.setVersion(10);
Log.i("Rae", "version is " + config.getVersion());
}
输出结果
I/Rae: username is DefaultValue
I/Rae: is vip: true
I/Rae: version is 10
评论 | 0 条评论