Application离散式注册方案
一、Application功能分析
在Android开发当中,一个APP有且只能有一个全局的Application对象,而且如果需要自定义的话,必须在AndroidManifest.xml中配置
Application的使用主要涉及以下两个功能:
- 入侵生命周期
Application具备attachBaseContext()、onCreate()等生命周期函数,它会在app启动过程中被系统触发
我们可以重写对应方法,实现一些需要提前初始化的功能,譬如MultiDex、初始化推送等 - 全局单例模式
Application是全局单例模式,而且相对于普通static对象,具有更强的引用关系,内部存储的变量基本在应用存活期间一直存有
我们往往利用这一点,在Application中存储稳定性要求更好的单例变量,譬如已登录用户的信息等
二、问题分析
在上文的分析中,我们已经说到,我们的业务需要入侵到Application中,也就是说 Application中必须持有 业务类的引用,相当于Application强依赖了业务代码,如下图所示
【图 Application依赖】
然而,在组件化模型中,我们需要将Application独立出去,也就是说业务层处于上层中,如下图所示:
此时,Application中已经不能持有业务的任何引用了
那么,我们需要一种方式去支持一种离散式Application[入侵生命周期]和[全局单例模式]使用;
三、入侵生命周期的离散式实现
Applcation的生命周期是在启动任何一个Activity之前就完成的,因此我们必须在系统启动应用的时候:
- Application主动去收集各个业务层需要入侵生命周期的回调
- 在系统触发Application的对应生命周期时,Applcation自动去触发业务层的回调
我们定义一个生命周期接口:
public interface ApplicationLifecycles {
void attachBaseContext(@NonNull Context base);
void onCreate(@NonNull Application application);
void onTerminate(@NonNull Application application);
}
然后想办法,让上层业务代码能主动的向某个地方去注册自己的实现,然后Application在启动时自动去收集,就可以实现一直“被动注册”逻辑
我们利用AndroidManifest.xml的属性,原因如下:
- [插拔式]:业务代码可以在自己module的AndroidManifest.xml中配置自己的属性,最终会被合并到唯一的AndroidManifest.xml中。
- [独立性]: 业务代码仅在自己的module中去维护自己需要入侵的 生命周期业务,便于后期的维护
为此,我们需要定义一个处理类:
public interface ModularAppConfiger {
/**
* 使用{@link ApplicationLifecycles}在Application的生命周期中注入一些操作
*
* @param context
* @param lifecycles
*/
void injectAppLifecycle(Context context, List<ApplicationLifecycles> lifecycles);
}
各个业务module实现该接口,并注册到 AndroidManifest.xml中
<meta-data
android:name="me.jessyan.mvparms.demo.app.GlobalConfiguration"
android:value="ModularAppConfiger"/>
然后,Application中去解析和反射,就可以持有对应回调:
private List<ApplicationLifecycles> mAppLifecycles = new ArrayList<>();
public List<ModularAppConfiger> parse() {
List<ModularAppConfiger> modules = new ArrayList<ModularAppConfiger>();
try {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(
context.getPackageName(), PackageManager.GET_META_DATA);
if (appInfo.metaData != null) {
for (String key : appInfo.metaData.keySet()) {
if (MODULE_VALUE.equals(appInfo.metaData.get(key))) {
modules.add(parseModule(key));
}
}
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("Unable to find metadata to parse ModularAppConfiger", e);
}
return modules;
}
private static ModularAppConfiger parseModule(String className) {
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to find ModularAppConfiger implementation", e);
}
Object module;
try {
module = clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException("Unable to instantiate ModularAppConfiger implementation for " + clazz, e);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to instantiate ModularAppConfiger implementation for " + clazz, e);
}
if (!(module instanceof ModularAppConfiger)) {
throw new RuntimeException("Expected instanceof ModularAppConfiger, but found: " + module);
}
return (ModularAppConfiger) module;
}
最终在自己的生命周期函数中触发相关回调:
@Override
public void attachBaseContext(@NonNull Context base) {
//(执行框架外部, 开发者扩展的逻辑)
//遍历 mAppLifecycles, 执行所有已注册的 AppLifecycles 的 attachBaseContext() 方法
for (ApplicationLifecycles lifecycle : mAppLifecycles) {
lifecycle.attachBaseContext(base);
}
}
四、全局单例模式的离散式使用
由于Applicaiton是全局唯一的,所以Applicaiton中持有的实例也是全局唯一的
我们知道java在内存不足的情况下,会自动触发内存回收机制,而Application中的内存在应用使用期间一般是不会被回收的
这也是为什么不能简单地使用静态单例模式来替换Applicaiton中变量的使用;
具体的实现方式,是我们定义一个接口,这个接口会返回一个单例模式的Cache缓存:
public interface AppSingleton {
//用来存取一些整个App公用的数据,切勿大量存放大容量数据
Cache<String, Object> extras();
//用于创建框架所需缓存对象的工厂
Cache.Factory cacheFactory();
}
在Application中,实例化该接口的具体类,并持有该实例
private AppSingleton mAppSingleton;
当然了,具体的实例化过程,我们也允许上层业务来自定义,还是借助上文的ModularAppConfiger,我们允许上层的业务代码自动配置缓存结构
public interface ModularAppConfiger {
/**
* 使用{@link AppSingletonConfig.Builder}给框架配置一些配置参数
*
* @param context
* @param builder
*/
void applyOptions(Context context, AppSingletonConfig.Builder builder);
}
public interface AppSingleton {
//用来存取一些整个App公用的数据,切勿大量存放大容量数据
Cache<String, Object> extras();
//用于创建框架所需缓存对象的工厂
Cache.Factory cacheFactory();
}
然后我们在Application中实现对应的功能,具体的代码不再赘述