ARouter 核心原理分析
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. ...
里面就把跳转所需的各个信息囊括了进去,处理方式更优,核心实现就是这么一个逻辑,弄清楚这一层之后再去看它其他具体细节的处理就更加顺畅了。
好了,大概就先到这儿,记录一下这个过程。
本文来自博客园,作者:阿丟啊,转载请注明原文链接:https://www.cnblogs.com/qiyuexiaxun/p/15316514.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?