组件化中使用动态创建的作用是解耦
一.反射基础:
反射机制是在运行状态中,对于任意一个类,能够知道这个类的所有属性和方法。反射是可以在一个类运行的时候获取类的信息的机制,可以获取在编译期不可能获取类的信息。对于任何一个对象,能够调用它的任意一个方法和属性。因为类的信息是保存在Class对象中的,而这个Class对象是在程序运行时被类加载(ClassLoader)动态加载的。当类加载器装载运行了类后,动态获取了Class对象的信息以及动态操作Class对象的属性和方法称为Java语言的反射机制。
实现反射,实际上是得到Class对象,使用java.lang.Class这个类。这是Java反射机制的起源,当一个类被加载后,Java虚拟机会自动产生一个Class对象。
- 反射机制获取类:
Class clazz = Class.forName("com.example.demo1.demo.RxBus");
Class clazz1 = RxBus.class;
Class clazz2 = new RxBus().getClass();
第一种Class.forName()方式,会让ClassLoader装载类,并进行类的初始化;
第二种class方式,ClassLoader装载入内存,不对类进行类的初始化操作;
第三种getClass()方式,返回类对象运行时真正所指的对象/所属类型的Class对象。
- 无参数创建对象:
Class clazz = Class.forName("com.example.demo1.demo.RxBus");
Object object = clazz.newInstance();
newInstance()是使用类的加载机制,创建一个实例。这个类已经被类加载,并已经被连接,这是因为forName会让ClassLoader装载类和进行类的初始化工作,其实际是创建一个Object对象。
- 有参数创建对象:
Class clazz = Class.forName("com.example.demo1.demo.RxBus");
Constructor csr = clazz.getConstructor(String.class,int.class);
Object object = csr.newInstance("jiji",28);
这getContructor方法会返回一个Constructor对象,它反映了Class对象所表示的类指定的公共构造方法。
- 反射类中的属性需要使用Field对象:
Field field = clazz.getField("方法名");
- 修改属性中的修饰符:
Field field = clazz.getDeclaredField("方法名");
String priv = Modifier.toString(field.getModifiers());
- 反射类中的方法:
Method method = clazz.getDeclaredMethod("setString", String.class);
method.invoke(clazz,"nihao");
getDeclaredMethod()获取的是类自身声明的所有方法,包含public/protected/private方法。Method中invoke方法用于检查AccessibleObject的override属性是否为true。
二.反射进阶:
- 获取不到Class:
当Class.forName()中路径获取不到对应的Class是,会抛出异常。
- 获取不到Field:
一种是不存在这个Field,另一种是修饰符导致的权限问题。getField只能获取对象中的public修饰符的属性,并且能获取父类Class的public属性;getDeclaredField能获取对象中各种修饰符的属性,但无法获取父类的任何属性。
- 获取不到Method:
与Field相类似,当方法名或参数数目类型没对上,就会抛出异常。
- 获取不到Contructor:
构造方式无法调用到父类的任何构造方法,反射创建一个对象,可以使用Class.newInstance()和Contructor.newInstance()两种方法,不同之处在于Class.newInstance()的使用受到严格限制,对应的Class中必须存在一个无参数的构造方法,并且必须要有访问权限。而Contructor.newInstance()适应任何类型的构造方法,无论是否有参数都可以调用,只需要使用setAccessible()控制访问验证即可。
- 反射静态方法:
调用静态方法直接用Class.method()的形式就可以调用。
- 反射泛型参数方法:
泛型的基础是类型的擦除,当有一个方法中有泛型参数时,编译器会自动类型向上转型,最终向上转型就成为Object。所以getDeclaredMethod需要用Object.class作为参数。
- Proxy动态代理机制:
代理模式的作用是为了其他对象提供一种代理控制对这个对象的访问,是控制器访问的方式,而不只是对方法扩展。
- 声明一个共同的接口Subject:
public interface Subject {
void doSomething();
}
- 具体实现类RealSubject:
public class RealSubject implements Subject {
@Override
public void doSomething() {
// TODO: 2019-08-25 do something
}
}
- 实现InvocationHandler,做一些居图的逻辑处理:
public class ProxyHandler implements InvocationHandler {
private Object realSubject;
public ProxyHandler(Object realSubject) {
this.realSubject = realSubject;
}
@Override
public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
//在转调具体目标对象之前,可以执行一些功能处理
//调用具体对象的方法
Object result = method.invoke(realSubject);
//在转调具体目标对象之后,可以执行一些功能处理
return result;
}
}
- 通过Proxy新建代理类对象:
RealSubject realSubject = new RealSubject();
Subject proxySubject = (Subject)Proxy.newProxyInstance(Subject.class.getClassLoader()
,new Class[]{Subject.class}
,new ProxyHandler(realSubject)
);
proxySubject.doSomething();
动态代理的作用在于不修改源码的情况下,可以增强一些方法,在方法执行前后做任何想做的事情。
三.反射简化jOOR:
越来越多人使用jOOR反射框架,其具体文件只有两个Java文件,非常轻量,并且内部封装好了异常抛出,让代码编写更加优雅。
使用的方式也是熟悉的链式调用:
import static org.joor.Reflect.*;
String world = on("java.lang.String") // Like Class.forName()
.create("Hello World") // Call most specific matching constructor
.call("substring", 6) // Call most specific matching substring() method
.call("toString") // Call toString()
.get(); // Get the wrapped object, in this case a String
并且支持动态代理:
public interface StringProxy {
String substring(int beginIndex);
}
String substring = on("java.lang.String")
.create("Hello World")
.as(StringProxy.class) // Create a proxy for the wrapped object
.substring(6); // Call a proxy method
四.动态创建Fragment:
Fragment做成布局的承载,其拥有自身的声明周期,但自身无法独立分离Activity这是特殊的使用限制。
这种限制在我们正常使用Activity引用Fragment方式是强引用,需要使用import包名来完成。假如移除module,那么引用Fragment的module将会提示索引不到资源包。
录制APP的例子,使用PageConfig来封装这些配置。然后通过反射机制创建出Fragment,并添加到ViewPager中。如Fragment所在的module被整体移除,因为索引不到Fragment,则会捕获到异常,而不会造成奔溃。
使用反射相对会安全一点,也会降低耦合,但反射回造成一定的效率下降。
使用跨模块获取Fragment非常适合在单一Activity+Fragment的app框架中使用。因为Fragment划分模块作为入口设计,使用ARouter的方式非常适应模块间解耦的要求。
如想要把初始化的方法都解耦到每个module中,需要跨module调用方法,刚好ARouter也提供了跨模块调用对象方法的方案:
- 扩展IProvider接口,创建一个初始化参数的接口:
- 继承接口并实现初始化Fragment的方法:
- 通过ARouter和IProvider接口和路由地址调用初始化Fragment的方法:
五.动态配置Application:
如某些功能模块中需要做一些初始化的操作,只能强引用到主module的Application中,是否有方法可以降低耦合性?
这里有两种配置Application的思路:
第一种是通过module的Application获取各个module的初始化文件,然后通过反射初始化的Java文件来调用初始化方法:
- 在Base module中定义接口BaseAppInt,里面有两个方法,onInitSpeed的内容最快被Application初始化,onInitLow的内容可以等其他的Application都被初始化后再调用;
public interface BaseAppInt {
boolean onInitSpeed(Application application);
boolean onInitLow(Application application);
}
- 在module中使用BaseAppInt接口,在类中实现操作;
public class NewInit implements BaseAppInt {
@Override
public boolean onInitSpeed(Application application) {
// TODO: 2019-08-25 初始化操作
return false;
}
@Override
public boolean onInitLow(Application application) {
// TODO: 2019-08-25 初始化操作
return false;
}
}
- 在PageConfig中配置;
public class PageConfig {
private static final String NewsInit = "NewInit所在的包名地址";
public static String[] initModules = {
NewsInit
};
}
- 在主module的Application中实现两个初始化的方法;
public class BaseApplication extends Application {
public void initModuleSpeed() {
for (String init : PageConfig.initModules) {
try {
Class<?> clszz = Class.forName(init);
BaseAppInt moduleInit = (BaseAppInt) clszz.newInstance();
moduleInit.onInitSpeed(this);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
public void initModuleLow() {
for (String init : PageConfig.initModules) {
try {
Class<?> clszz = Class.forName(init);
BaseAppInt moduleInit = (BaseAppInt) clszz.newInstance();
moduleInit.onInitLow(this);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
}
- 在Application中的onCreate中调用;
public class GankApplication extends BaseApplication {
@Override
public void onCreate() {
super.onCreate();
initModuleSpeed();
// TODO: 2019-08-25 其他初始化的操作
initModuleLow();
}
}
使用这种反射方法完成初始化操作,基本可以满足组件化中 的需求和解耦需求,但是反射会带来一定的性能损耗。对于追求app秒开体验的需求,可以通过RxJava简单地使用非UI线程实现,减少对UI线程的阻塞,这种初始化操作尽量是飞非UI的操作。
第二种方式可以选择初始化延后的方式,因为某些模块的初始化操作不一定在Application启动时立即执行。可以采用延后带MainActivity初始化后的方式,保证在相关模块做使用前的懒加载;这种方式时通过主module的Application中继承Base module的Application来实现的,主module的Application将注册每个module的初始化文件,然后通过Base module中的Application来对初始化文件做启动封装。
- 在Base module中声明一个初始类;
public class BaseAppLogic {
protected BaseApplication mBaseApplication;
public BaseAppLogic() {
}
public void setApplication(@NonNull BaseApplication base1Application) {
this.mBaseApplication = base1Application;
}
public void onCreate() {
}
public void onTerminate() {
}
public void onLowMemory() {
}
public void onTrimMemory() {
}
public void onConfigurationChanged(Configuration configuration) {
}
}
- 在Base module中添加Application的注册和运行逻辑;
public abstract class BaseApplication extends Application {
private List<Class<? extends BaseAppLogic>> logicList = new ArrayList<>();
private List<BaseAppLogic> logicClassList = new ArrayList<>();
@Override
public void onCreate() {
super.onCreate();
initLogic();
logicCreate();
}
protected void registerApplicationLogic(Class<? extends BaseAppLogic> logicClass) {
logicList.add(logicClass);
}
protected void logicCreate() {
for (Class<? extends BaseAppLogic> logicClass : logicList) {
try {
BaseAppLogic appLogic = logicClass.newInstance();
logicClassList.add(appLogic);
appLogic.onCreate();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
}
protected abstract void initLogic(); //主module的Application调用
@Override
public void onTerminate() {
super.onTerminate();
for (BaseAppLogic logic : logicClassList) {
logic.onTerminate();
}
}
}
- 每个module需要初始化时即成此类,然后复写需要的接口;
public class NewInitLogic extends BaseAppLogic {
@Override
public void onCreate() {
super.onCreate();
}
}
- 在主module的Application调用initLogic注册所有的BaseAppLogic class类。
public class GankApplication extends BaseApplication {
@Override
protected void initLogic() {
registerApplicationLogic(NewInitLogic.class);
}
}