对组件化开发的基本认识中,各个模块之间的通讯方式明显不能满足项目需求,此时就需要一个通讯框架实现模块之间的通讯。框架的意义就在于实现解耦的前提下,能够让各组件之间完成通讯。所以简单学习了一个Aroute框架权当记录,错误的地方还请指正
本demo设置了主程序和登录模块,一些基本的配置,例如library开关,manifest设置等本文就不再描述,直接简述Aroute的流程
创建ArouteLibrary依赖库,让主程序和登录模块依赖此项目
public class Aroute {
// 存放key和对应activity的map
private Map<String, Class<? extends Activity>> maps;
// 上下文对象
private Context context;
// 初始化上下文,遍历整个apk中类,找到放入ActivityUtil类,执行往map中放入activity的方法
public void init(Context context) {
this.context = context;
// 获取com.syw.util包下所有的类
List<String> classNames = getClassName("com.syw.util");
for (String className : classNames) {
try {
// 通过反射获取类
Class<?> aClass = Class.forName(className);
// 判断该类是否是IRouter的子类
if (IRouter.class.isAssignableFrom(aClass)) {
// 创建类对象
// IRouter接口包含putActivity方法,由注解处理器生成的代码重写,具体实现添加k-v到map中。规范代码,方便统一调用
IRouter iRouter = (IRouter) aClass.newInstance();
// 执行类对象中putActivity方法,将所有有注解的类加入到map中
iRouter.putActivity();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public Aroute() {
// 在Aroute初始化的时候,创建map
maps = new HashMap<>();
}
public static Aroute instance = new Aroute();
public static Aroute getInstance() {
return instance;
}
/**
* 将有注解的类存入map
* @param key 注解内容
* @param clazz 该注解对应的类
*/
public void addActivity(String key, Class<? extends Activity> clazz) {
if (key != null && clazz != null && !maps.containsKey(key)) {
maps.put(key, clazz);
}
}
/**
* 根据用户传入的key,找到类,完成跳转
* @param key
* @param bundle
*/
public void jumpActivity(String key, Bundle bundle) {
Class<? extends Activity> activityClass = maps.get(key);
if (activityClass != null) {
Intent intent = new Intent(context, activityClass);
if (bundle != null) {
intent.putExtras(bundle);
}
context.startActivity(intent);
}
}
/**
* 获取所有指定包名下的类
* @param packageName
* @return
*/
private List<String> getClassName(String packageName) {
List<String> classList = new ArrayList<>();
String path = null;
try {
// 获取apk的完整路径
path = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0).sourceDir;
// 根据apk路径获取编译后的dex文件目录
DexFile dexFile = new DexFile(path);
// 获取编译后dex文件中的所有class
Enumeration<String> entries = dexFile.entries();
// 遍历当前应用所有文件class
while (entries.hasMoreElements()) {
String name = entries.nextElement();
if (name.contains(packageName)) {
// 如果符合就添加到集合
classList.add(name);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return classList;
}
}
编写注解和注解处理器
Aroute用到的是apt技术,也就是编译时技术。在编译期执行,包括注解和注解处理器
注解
我们可以自定义注解,对类/方法等进行标记
新建一个java依赖库,创建一个类对象
@Target(ElementType.TYPE)//TYPE表示该注解放到类上边,@Override放到方法上,@Target(ElementType.METHOD)
@Retention(RetentionPolicy.CLASS)//表示生命周期存在于编译期,代码有源码期--编译期--运行期,Override是源码期,所以编译后的代码,没有@Override
public @interface BindPath {
// 用于获取注解中的内容
String value();
}
注解处理器
本demo中注解处理器的作用是:在编译期在指定目录下,在各个模块生成我们想要的java代码。
新建一个java依赖库,依赖注解模块,创建一个注解处理器类
//向javac注册我们这个自定义的注解处理器,这样,在javac编译时,才会调用到我们这个自定义的注解处理器方法。
//AutoService这里主要是用来生成META-INF/services/javax.annotation.processing.Processor文件的。
//使用AutoService需要添加以下依赖
// as 3.4以上版本
// annotationProcessor 'com.google.auto.service:auto-service:1.0-rc4'
// compileOnly 'com.google.auto.service:auto-service:1.0-rc3'
// as 3.4以下版本
// implementation 'com.google.auto.service:auto-service:1.0-rc3'
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
}
@Override
// 获取java版本
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
@Override
//设置该处理器能处理的节点,必须是注解的全类名
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindPath.class.getCanonicalName());
return types;
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 查看该项目下的BindPath节点集合
Set<? extends Element> elementsAnnotateWith = roundEnvironment.getElementsAnnotatedWith(BindPath.class);
Map<String, String> map = new HashMap<>();
// 遍历节点
for (Element element : elementsAnnotateWith) {
TypeElement typeElement = (TypeElement) element;
// 从节点获取注解
BindPath annotation = typeElement.getAnnotation(BindPath.class);
// 获取注解内容
String key = annotation.value();
// 获取节点全类名
Name activityName = typeElement.getQualifiedName();
// 将key-value存入map
map.put(key, activityName + ".class");
}
if (map.size()>0){
Writer writer=null;
String activityName="ActivityUtil"+System.currentTimeMillis();
try {
// 创建java文件
JavaFileObject sourceFile = filer.createSourceFile("com.syw.util." + activityName);
writer=sourceFile.openWriter();
// 拼接stringbuffer
StringBuffer stringBuffer=new StringBuffer();
stringBuffer.append("package com.syw.util;\n");
stringBuffer.append("import com.syw.aroutelibrary.Aroute;\n");
stringBuffer.append("import com.syw.aroutelibrary.IRouter;\n");
stringBuffer.append("public class "+activityName+" implements IRouter{\n"
+"@Override\n"+
"public void putActivity(){\n");
Iterator<String> iterator = map.keySet().iterator();
while (iterator.hasNext()){
String key=iterator.next();
String className=map.get(key);
stringBuffer.append("Aroute.getInstance().addActivity(\""+key+"\","+className
+");\n");
}
stringBuffer.append("\n}\n}");
// 写入java文件
writer.write(stringBuffer.toString());
} catch (IOException e) {
e.printStackTrace();
}finally {
if (writer!=null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
return false;
}
}
各个模块添加注解依赖
// 编译时模块
implementation project(':annotation')
// 编译到这行代码时,
// 1.向javac注册我们这个自定义的注解处理器
// 2.生成META-INF/services/javax.annotation.processing.Processor文件
// 3.通过反射执行生成文件的方法
annotationProcessor project(':annotation_compiler')
AutoService生成文件如下图所示
自定义注解处理器最终生成的代码如下图所示
所有生成的部分已经完成,现在需要执行生成的代码,将所有内容存到map中也就是执行Aroute的init()方法
我在主项目的mainactivity中执行了该方法,如下所示
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Aroute.getInstance().init(this);
}
public void tologin(View view) {
Aroute.getInstance().jumpActivity("login/login",null);
}
}
完成跳转
总结:1.创建一个JavaLibrary用于注解,自定义我们的注解
2.创建一个JavaLibiary用于注解处理器,功能是:遍历dex文件中的类,将包含我们制定注解的对象添加到map,然后写入指定路径的java文件
3.各个模块依赖注解和注解处理器,当编译到annotationProcessor时,向javac注册注解处理器,生成Processor文件,通过反射方法执行注解处理器生成代码的方法
4.通过Aroute.init()方法,获取所有的生成代码,然后执行,实现将所有方法添加到map。当需要跳转的时候,直接根据key获取到对应的activity,完成跳转