Arouter [组件化框架]
官方简介:一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
Arouter框架地址
使用集成就不在此赘述,记录一下我所理解中这个框架。
dependencies {
// 替换成最新版本, 需要注意的是api
// 要与compiler匹配使用,均使用最新版可以保证兼容
compile 'com.alibaba:arouter-api:x.x.x'
annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
...
}
集成里面有这么一个操作,需要引入一个注解处理器,再看了下具体的使用方式,分析猜测框架实现的大概思路是 注解+动态生成文件
实现,具体是不是这样,接着分析
注解处理器
注解处理器,是javac处理注解的一种工具,它用来在编译时扫描和处理注解。简单来说就是在编译期,通过注解采集信息,生成.java文件。减少重复的代码的编写。
用的比较多的就是页面注册跳转和传值这两个功能,对应的两个注解是 @Autowired
和@Route
-
Aoute
注解,给页面添加一个路由url,跳转的时候直接使用这个url进行跳转,代替系统自带的那种方式来找到跳转的目标界面 -
@Autowired
注解,跳转的传值自动解析值,代替手动获取intent的传值(自带的获取值的方式也可以使用)
主要看看这两个注解的实现思路;
1、Aroute
给界面添加该注解后,build 一下,就能生成对应的代码类,如下所示
// build 类1
public class ARouter$$Group$$my implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/my/MainActivity", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/my/mainactivity", "my", null, -1, -2147483648));
atlas.put("/my/TwoActivity", RouteMeta.build(RouteType.ACTIVITY, TwoActivity.class, "/my/twoactivity", "my", new java.util.HashMap<String, Integer>(){{put("name", 8); }}, -1, -2147483648));
}
}
// build 类2
public class ARouter$$Providers$$app implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
}
}
// build 类3
public class ARouter$$Root$$app implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("my", ARouter$$Group$$my.class);
}
}
// build 类4
public class TwoActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
TwoActivity substitute = (TwoActivity)target;
substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
}
}
分析一下注解生成的类
-
@Route(path = "/my/MainActivity")
,路由前一节/my
是group名,区分不同组别的路由,第二段是targer 目标类,在build类1中可以看到把我们添加该注解的界面放到了一个 Map 里面,key
是route注解的path,value
里面存放的是一个RouteMate
对象。
分析就是该注解帮我们把对应界面存到了一个,map里面,与模块解耦,路由跳转就直接在这个map里面寻找 key 值对应的 value 来实现跳转
根据此原理,手动实现一个类似功能的注解
a、定义注解
b、注解处理器定义解析注解,自动把注解类添加到map集合
c、提供一个工具类,方便route的初始话化,以及各种操作(比如添加,启动等)
- 第一步,定义一个注解
Route
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Route {
String value();
}
- 第二步 定义一个注解处理器
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
Filer filer;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotaions = new HashSet<>();
annotaions.add(Route.class.getCanonicalName());
return annotaions;
}
//文件的内容是什么
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
}
由于想只通过一个注解达到此目的,所以添加和启动的操作就需要在解析注解的类里面去完成,生成一个build 类1 块所示的java类,此处设计就是一些反射相关的操作,
//文件的内容是什么
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("------------22222222-------------");
//在build目录生成文件
//原理,就是去找所有继承了activity文件,并且是被Router注解了的文件
//map.put(path,ativity的全类名
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Route.class);
System.out.println(elements.size());
//这个表示被Route注解的类的所有元素
//只需要全类名
Map<String,String> map = new HashMap<>();
//map,收集被Route注解的类
for(Element element:elements){
TypeElement typeElement = (TypeElement) element;
//通过这个方法得到activity的全类名
String activityName = typeElement.getQualifiedName().toString();
//key值
String key = typeElement.getAnnotation(Route.class).value();
map.put(key,activityName);
}
//生成文件
return false;
}
//生成文件方式
// 1、拼接字符串
//2、javapoet方式
相关方法实现之后,build一下看看生成的文件:
public class ActivityUtil_1632152688013 implements IRouter {
@Override
public void putActivity() {
Arouter.getInstance().putActivity("/my/TwoActivity",com.example.arouterdemo.MainActivity.class);
Arouter.getInstance().putActivity("/my/TwoActivity",com.example.arouterdemo.TwoActivity.class);
}
}
- 第三步 工具类提供的方法
public void startActivity(String activityName, Bundle bundle) {
Intent intent = new Intent();
Class<? extends Activity> aCls = activityMap.get(activityName);
if (aCls == null) {
return;
}
if (bundle != null) {
intent.putExtras(bundle);
}
intent.setClass(mContext, aCls);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// TODO 是不是很熟悉了
mContext.startActivity(intent);
}
public void init(Context context) {
List<String> className = getAllActivityUtils("com.example.arouterdemo.");
// 反射调用
for (String cls : className)
try {
Class<?> aClass = Class.forName(cls);
if (IRouter.class.isAssignableFrom(aClass)) {
IRouter iRouter = (IRouter) aClass.newInstance();
iRouter.putActivity();
}
} catch ( Exception e) {
e.printStackTrace();
}
}
}
public List<String> getAllActivityUtils(String packageName) {
List<String> list = new ArrayList<>();
String path;
try {
path = mContext.getPackageManager().getApplicationInfo(mContext.getPackageName(), 0).sourceDir;
DexFile dexFile = new DexFile(path);
Enumeration<String> enumeration = dexFile.entries();
while (enumeration.hasMoreElements()) {
String name = enumeration.nextElement();
if (name.startsWith(packageName)) {
list.add(name);
}
}
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
基本功能就实现了跳转,ARouter
框架的核心思想其实也就是这样,只不过是更加强大的升级版本,比如说注解类的路由保存用了一个实体包装
*/
public class RouteMeta {
private RouteType type; // Type of route
private Element rawType; // Raw type of route
private Class<?> destination; // Destination
private String path; // Path of route
private String group; // Group of route
private int priority = -1; // The smaller the number, the higher the priority
private int extra; // Extra data
private Map<String, Integer> paramsType; // Param type
private String name;
private Map<String, Autowired> injectConfig; // Cache inject config.
...
里面就把跳转所需的各个信息囊括了进去,处理方式更优,核心实现就是这么一个逻辑,弄清楚这一层之后再去看它其他具体细节的处理就更加顺畅了。
好了,大概就先到这儿,记录一下这个过程。