“本文正在参加「金石方案」”
前语:
最近组里需求进行组件化结构的改造,用到了Arouter
这个开源结构,为了更好的对项目进行改造,笔者花了一些时刻去了解了下Arouter
ARouter
是阿里巴巴团队在17年初发布的一款针对组件化模块之间无触摸通讯的一个开源结构,经过多个版本的迭代,现在现已十分成熟了。
ARouter
首要效果:组件间通讯,组件解耦,路由跳转,涉及到咱们常用的Activity
,Provider
,Fragment
等多个场景的跳转
接下来笔者会以几个阶段来对Arouter
进行解说:
- (一 )运用办法详解
- (二)基本原理详解
- (三)APT技能详解
1.基本运用办法
前期预备
咱们先来新建一个项目:
项目架构如下:
- app:app模块
build.gradle
:
dependencies {
...
implementation 'com.alibaba:arouter-api:1.5.2'
implementation project(':module_java')
implementation project(':module_kotlin')
//annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
}
引进arouter的中心api和引用需求调用的模块即可
这儿假如需求运用依靠注入的办法查找class方针,则需求增加声明注解处理器
:
annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
- module_java:java言语lib模块
build.gradle:
android {
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
}
dependencies {
...
implementation 'com.alibaba:arouter-api:1.5.2'
annotationProcessor 'com.alibaba:arouter-compiler:1.5.2'
}
1.引进arouter-api
中心api和arouter-compiler
注解处理器api
2.设置主机处理器的外部参数AROUTER_MODULE_NAME
和AROUTER_GENERATE_DOC
,这两个参数会在编译器生成Arouter路由表
的时分运用到
- module_kotlin:kotli言语lib模块
build.gradle:
apply plugin: 'kotlin-kapt'
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
implementation 'com.alibaba:arouter-api:1.5.2'
kapt 'com.alibaba:arouter-compiler:1.5.2'
}
这儿的设置办法和java相同,区别:java运用关键字
annotationProcessor
,而kotlin运用kapt
。
同步项目后。就能够运用Arouter的api啦。
ARouter api
1.初始化Arouter
建议在application中初始化:
ARouter.init(this);
2.日志敞开
ARouter.openLog();
3.翻开调试形式
ARouter.openDebug();
调试形式不是必须敞开,可是为了防止有用户敞开了InstantRun, 忘了开调试形式,导致无法运用Demo,假如运用了InstantRun,必须在 初始化之前敞开调试形式,可是上线前需求封闭,InstantRun仅用于开 发阶段,线上敞开调试形式有安全危险,能够运用BuildConfig.DEBUG 来区分环境
4.Activity模块的调用:
- 4.1:注解声明
@Route(path = "/gtest/test")
//或许@Route(path = "/gtest/test",group = "gtest")
public class ArouterTestActivity extends AppCompatActivity {
}
- 4.2:组件跨模块调用
ARouter.getInstance().build("/gtest/test").navigation();
记住:假如注解没有指明group,arouter默许会以path的榜首个节点gtest为组名进行分组
5.特点依靠注入
- 5.1:注解声明
@Route(path = "/test/activity1", name = "测验用 Activity")
public class Test1Activity extends BaseActivity {
@Autowired
int age = 10;
@Autowired
int height = 175;
@Autowired(name = "boy", required = true)
boolean girl;
@Autowired
char ch = 'A';
@Autowired
float fl = 12.00f;
@Autowired
double dou = 12.01d;
@Autowired
TestSerializable ser;
@Autowired
TestParcelable pac;
@Autowired
TestObj obj;
@Autowired
List<TestObj> objList;
@Autowired
Map<String, List<TestObj>> map;
private long high;
@Autowired
String url;
@Autowired
HelloService helloService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test1);
ARouter.getInstance().inject(this);
}
}
- 5.2:注解跨模块调用
ARouter.getInstance().build("/test/activity1")
.withString("name", "老王")
.withInt("age", 18)
.withBoolean("boy", true)
.withLong("high", 180)
.withString("url", "https://a.b.c")
.withSerializable("ser", testSerializable)
.withParcelable("pac", testParcelable)
.withObject("obj", testObj)
.withObject("objList", objList)
.withObject("map", map)
.navigation();
传入的Object需求运用Serializable序列化
public class TestSerializable implements Serializable {
public String name;
public int id;
public TestSerializable() {
}
public TestSerializable(String name, int id) {
this.name = name;
this.id = id;
}
}
留意点:
- 1.常用数据类型直接运用:
Autowired
注解即可 - 2.假如需求指定外部传入的数据key:能够运用
name
指定,假如这个字段是必须传入的,则需求指定required = true
,外部未传入可能会报错 - 3.传入自界说类的特点则需求先对自界说进行序列化,且需求指定一个
SerializationService
:这个类是用于Object
方针和json进行转化用
@Route(path = "/yourservicegroupname/json")
public class JsonServiceImpl implements SerializationService {
@Override
public void init(Context context) {
}
@Override
public <T> T json2Object(String text, Class<T> clazz) {
return JSON.parseObject(text, clazz);
}
@Override
public String object2Json(Object instance) {
return JSON.toJSONString(instance);
}
@Override
public <T> T parseObject(String input, Type clazz) {
return JSON.parseObject(input, clazz);
}
}
6.Service模块的调用
- 6.1:声明接口,其他组件经过接口来调用服务
public interface HelloService extends IProvider {
String sayHello(String name);
}
- 6.2:完结接口
@Route(path = "/yourservicegroupname/hello", name = "测验服务")
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello, " + name;
}
@Override
public void init(Context context) {
}
}
- 6.3:运用接口
6.3.1
:运用依靠查找的办法发现服务,主动去发现服务并运用,下面两种办法分别是byName和byType
1.运用class类型查找
helloService3 = ARouter.getInstance().navigation(HelloService.class);
helloService3.sayHello("Vergil");
2.运用path查找
helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
helloService4.sayHello("Vergil");
6.3.2
:(引荐)运用依靠注入的办法发现服务,经过注解标示字段,即可运用,无需主动获取
@Autowired
HelloService helloService;
@Autowired(name = "/yourservicegroupname/hello")
HelloService helloService2;
helloService.sayHello("mikeaaaa");
helloService2.sayHello("mikeaaaa");
7.Fragment和BOARDCAST以及CONTENT_PROVIDER
运用办法和Activity
相似,可是这三者都是直接获取对应的Class类方针,拿到方针后,能够操作方针,这儿不再解说
8.拦截器的运用
拦截器便是在跳转的进程中,设置的对跳转的检测和预处理等操作。
- 8.1:拦截器声明 能够增加拦截器的
优先级
,优先级越高,越优先履行
@Interceptor(priority = 7)
public class Test1Interceptor implements IInterceptor {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
@Override
public void process(final Postcard postcard, final InterceptorCallback callback) {
if ("/test/activity4".equals(postcard.getPath())) {
// 这儿的弹窗仅做举例,代码写法不具有可参阅价值
final AlertDialog.Builder ab = new AlertDialog.Builder(postcard.getContext());
ab.setCancelable(false);
ab.setTitle("温馨提示");
ab.setMessage("想要跳转到Test4Activity么?(触发了"/inter/test1"拦截器,拦截了本次跳转)");
ab.setNegativeButton("持续", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onContinue(postcard);
}
});
ab.setNeutralButton("算了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
callback.onInterrupt(null);
}
});
ab.setPositiveButton("加点料", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
postcard.withString("extra", "我是在拦截器中附加的参数");
callback.onContinue(postcard);
}
});
MainLooper.runOnUiThread(new Runnable() {
@Override
public void run() {
ab.create().show();
}
});
} else {
callback.onContinue(postcard);
}
}
/**
* Do your init work in this method, it well be call when processor has been load.
*
* @param context ctx
*/
@Override
public void init(Context context) {
Log.e("testService", Test1Interceptor.class.getName() + " has init.");
}
}
能够设置绿色通道形式:greenChannel
()不做拦截器处理
9.监听跳转状况
.navigation(this, new NavCallback() {
@Override
public void onArrival(Postcard postcard) {
Log.d("ARouter", "onArrival");
Log.d("ARouter", "成功跳转了");
}
@Override
public void onInterrupt(Postcard postcard) {
super.onInterrupt(postcard);
Log.d("ARouter", "被拦截了");
}
@Override
public void onFound(Postcard postcard) {
super.onFound(postcard);
Log.d("ARouter", "onFound:path:"+postcard.getPath());
Log.d("ARouter", "找到了对应的path");
}
@Override
public void onLost(Postcard postcard) {
super.onLost(postcard);
Log.d("ARouter", "onLost:path:"+postcard.getPath());
Log.d("ARouter", "没找到了对应的path");
}
});
10.自界说大局降级策略
// 完结DegradeService接口,并加上一个Path内容恣意的注解即可
@Route(path = "/xxx/xxx")
public class DegradeServiceImpl implements DegradeService {
@Override
public void onLost(Context context, Postcard postcard) {
// do something.
}
@Override
public void init(Context context) {
}
}
11.动态注册路由信息
适用于部分插件化架构的App以及需求动态注册路由信息的场景,能够经过 ARouter 供给的接口完结动态注册 路由信息, 方针页面和服务能够不标示 @Route 注解,留意:同一批次仅答应相同 group 的路由信息注册
ARouter.getInstance().addRouteGroup(new IRouteGroup() {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/dynamic/activity", // path
RouteMeta.build(
RouteType.ACTIVITY, // 路由信息
TestDynamicActivity.class, // 方针的 Class
"/dynamic/activity", // Path
"dynamic", // Group, 尽量保持和 path 的榜首段相同
0, // 优先级,暂未运用
0 // Extra,用于给页面打标
)
);
}
});
12:运用自己的日志东西打印日志
ARouter.setLogger();
13.运用自己供给的线程池
ARouter.setExecutor();
14.重写跳转URL
// 完结PathReplaceService接口,并加上一个Path内容恣意的注解即可
@Route(path = "/xxx/xxx") // 必须标明注解
public class PathReplaceServiceImpl implements PathReplaceService {
/**
* For normal path.
*
* @param path raw path
*/
String forString(String path) {
return path; // 按照必定的规矩处理之后回来处理后的成果
}
/**
* For uri type.
*
* @param uri raw uri
*/
Uri forUri(Uri uri) {
return url; // 按照必定的规矩处理之后回来处理后的成果
}
}
15.运用agp插件动态路由表加载
- 1.在项目级
build.gradle
中:
buildscript {
dependencies {
classpath "com.alibaba:arouter-register:$arouter_register_version"
}
}
- 2.在模块级app下引进路由表主动加载插件
id 'com.alibaba.arouter'
同步代码后,在编译期会主动进行路由表的加载,能够加快arouter
的榜首次初始化时刻,为什么是榜首次呢,因为arouter初始化会缓存路由表信息到文件中。
Q&A
1.”W/ARouter::: ARouter::No postcard![ ]”
这个Log正常的状况下也会打印出来,假如您的代码中没有完结DegradeService和PathReplaceService的话,因为ARouter自身的一些功用也依靠 自己供给的Service管理功用,ARouter在跳转的时分会尝试寻觅用户完结的PathReplaceService,用于对途径进行重写(可选功用),所以假如您没有 完结这个服务的话,也会抛出这个日志
引荐在app中完结DegradeService、PathReplaceService
2.”W/ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group xxx”
通常来说这种状况是没有找到方针页面,方针不存在 假如这个页面是存在的,那么您能够按照下面的进程进行排查
- a.查看方针页面的注解是否装备正确,正确的注解形式应该是 (@Route(path=”/test/test”), 如没有特别需求,请勿指定group字段,废弃功用)
- b.查看方针页面地点的模块的gradle脚本中是否依靠了 arouter-compiler sdk (需求留意的是,要运用apt依靠,而不是compile关键字依靠)
- c.查看编译打包日志,是否出现了形如 ARouter::�Compiler >>> xxxxx 的日志,日志中会打印出发现的路由方针
- d.发动App的时分,敞开debug、log(openDebug/openLog), 查看映射表是否现已被扫描出来,形如 D/ARouter::: LogisticsCenter has already been loaded, GroupIndex[4],GroupIndex > 0
3.敞开InstantRun之后无法跳转(高版本Gradle插件下无法跳转)?
因为敞开InstantRun之后,许多类文件不会放在本来的dex中,需求独自去加载,ARouter默许不会去加载这些文件,因为安全原因,只要在敞开了openDebug之后 ARouter才回去加载InstantRun产生的文件,所以在以上的状况下,需求在init之前调用openDebug
4.TransformException:java.util.zip.ZipException: duplicate entry ….
ARouter有按组加载的机制,关于分组能够参阅 6-1 部分,ARouter答应一个module中存在多个分组,可是不答应多个module中存在相同的分组,会导致映射文件抵触
5.Kotlin类中的字段无法注入怎么处理?
首要,Kotlin中的字段是能够主动注入的,可是注入代码为了减少反射,运用的字段赋值的办法来注入的,Kotlin默许会生成set/get办法,并把特点设置为private 所以只要确保Kotlin中字段可见性不是private即可,简略处理能够在字段上增加 @JvmField
6.经过URL跳转之后,在intent中拿不到参数怎么处理?
需求留意的是,假如不运用主动注入,那么能够不写 ARouter.getInstance().inject(this),可是需求取值的字段依然需求标上 @Autowired 注解,因为 只要标上注解之后,ARouter才干知道以哪一种数据类型提取URL中的参数并放入Intent中,这样您才干在intent中获取到对应的参数
7.新增页面之后,无法跳转?
ARouter加载Dex中的映射文件会有必定耗时,所以ARouter会缓存映射文件,直到新版本升级(版本号或许versionCode改变),而假如是开发版本(ARouter.openDebug()), ARouter 每次发动都会从头加载映射文件,开发阶段必定要翻开 Debug 功用
2.Arouter的基本原理
1.ARouter认知
官方界说
:
一个用于协助 Android App 进行组件化改造的结构 —— 支撑模块间的路由、通信、解耦
那么什么是路由呢? 简略理解便是:一个公共平台转发系统
工作办法:
- 1.注册服务:将咱们需求对外露出的页面或许服务注册到ARouter公共平台中
- 2.调用服务:调用ARouter的接口,传入地址和参数,ARouter解析传入的地址和参数转发到对应的服务中
经过ARouter形成了一个无触摸解耦的调用进程
2.ARouter架构解析
咱们来看下ARouter
的源码架构:
- app:是ARouter供给的一个测验Demo
- arouter-annotation:这个lib模块中声明了许多注解信息和一些枚举类
- arouter-api:ARouter的中心api,转换进程的中心操作都在这个模块里边
- arouter-compiler:APT处理器,主动生成路由表的进程便是在这儿面完结的
- arouter-gradle-plugin:这是一个编译期运用的Plugin插件,首要效果是用于编译器主动加载路由表,节省运用的发动时刻。
3.原理解说
这儿咱们不会一开端就大篇幅对源码进行解说: 咱们先来介绍ARouter中的几个重要概念:有了这几个概念,后边在去看源码就会轻松多了
前置根底概念:
概念1:PostCard
(明信片)
既然是明信片要将信件寄到意图人的手上就至少需求:收件人的名字和地址,寄件人以及电话和地址等
ARouter便是运用PostCard
这个类来存储寄件人和收件人信息的。
public final class Postcard extends RouteMeta {
// Base
private Uri uri; //假如运用Uri办法建议luyou
private Object tag; // A tag prepare for some thing wrong. inner params, DO NOT USE!
private Bundle mBundle; // 需求传递的参数运用bundle存储
private int flags = 0; // 发动Activity的标志:如NEW_FALG
private int timeout = 300; // 路由超时
private IProvider provider; // 运用IProvider的办法跳转
private boolean greenChannel; //绿色通道,能够不经过拦截器
private SerializationService serializationService; //序列化服务serializationService:需求传递Object自界说类型方针,就需求完结这个服务
private Context context; // May application or activity, check instance type before use it.
private String action; //Activity跳转的Action
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim = -1;
private int exitAnim = -1;
...
}
PostCard
承继了RouteMeta
:
public class RouteMeta {
private RouteType type; // 路由类型:如Activity,Fragment,Provider等
private Element rawType; // 路由原始类型,在编译时用来判别
private Class<?> destination; // 意图Class方针
private String path; // 路由注册的path
private String group; // 路由注册的group分组
private int priority = -1; // 路由履行优先级,priority越低,优先级越高,这个一般在拦截器中运用
private int extra; // Extra data
private Map<String, Integer> paramsType; // 参数类型,例如activity中运用@Autowired的参数类型
private String name; //路由名字,用于生成javadoc
private Map<String, Autowired> injectConfig; // 参数装备(对应paramsType).
}
RouteMeta
:首要存储的是一些意图方针的信息,这些方针是在路由注册的时分才会生成。
概念2:Interceptor
拦截器
了解OkHttp
的都知道,其内部调用进程便是运用的拦截器形式,每个拦截器履行的对应的使命。
而ARouter
中也是如此,一切的路由调用进程在到达意图地前都会先经过自界说的一系列拦截器,完结一些AOP切面
编程。
public interface IInterceptor extends IProvider {
/**
* The operation of this interceptor.
*
* @param postcard meta
* @param callback cb
*/
void process(Postcard postcard, InterceptorCallback callback);
}
IInterceptor
是一个接口,承继了IProvider
,所以其也是一个服务类型
只需求完结process
办法就能够完结拦截操作。
概念3:greenChannel
:绿色通道
设置了绿色通道的跳转进程,能够不经过拦截器
概念4:Warehouse
:路由库房
Warehouse意为库房,用于寄存被 @Route、@Interceptor
注释的 路由相关的信息,也便是咱们重视的destination
等信息
举个例子:
moduleB建议路由跳转到moduleA的activity,moduleB没有依靠moduleA,只是在moduleA的activity上增加了@Route注解。 因为进行activity跳转需求方针Activity的class方针来构建intent,所以必须有一个中间人,把途径”/test/activity”翻译成Activity的class方针,然后moduleB才干完结跳转。(因此在ARouter的运用中 moduleA、moduleB 都是需求依靠 arouter-api的)
这个中间人那便是ARouter
了,而这个翻译工所效果到的词典便是Warehouse
,它存着一切路由信息。
class Warehouse {
//一切IRouteGroup完结类的class方针,是在ARouter初始化中赋值,key是path榜首级
//(IRouteGroup完结类是编译时生成,代表一个组,即path榜首级相同的一切路由,包括Activity和Provider服务)
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
//一切路由元信息,是在completion中赋值,key是path
//首次进行某个路由时就会加载整个group的路由,即IRouteGroup完结类中一切路由信息。包括Activity和Provider服务
static Map<String, RouteMeta> routes = new HashMap<>();
//一切服务provider实例,在completion中赋值,key是IProvider完结类的class
static Map<Class, IProvider> providers = new HashMap<>();
//一切provider服务的元信息(完结类的class方针),是在ARouter初始化中赋值,key是IProvider完结类的全类名。
//首要用于运用IProvider完结类的class建议的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)
static Map<String, RouteMeta> providersIndex = new HashMap<>();
//一切拦截器完结类的class方针,是在ARouter初始化时搜集到,key是优先级
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");
//一切拦截器实例,是在ARouter初始化完结后当即创立
static List<IInterceptor> interceptors = new ArrayList<>();
...
}
Warehouse存了哪些信息呢?
- groupsIndex:存储一切路由组元信息:
key:group的称号
value:路由组的模块类class类:
赋值时机:初始化的时分
- routes:存储一切路由元信息。切记和上面路由组分隔,路由是单个路由,路由组是一批次路由
key:路由的path
value:路由元信息
赋值时机:LogisticsCenter.completion中赋值
补白:首次进行某个路由时就会加载整个group的路由,即IRouteGroup完结类中一切路由信息。包括Activity和Provider服务
- providers:存储一切服务provider实例。
key:IProvider完结类的class
value:IProvider实例
赋值时机:在LogisticsCenter.completion中赋值
- providersIndex:存储一切provider服务元信息(完结类的class方针)。
key:IProvider完结类的全类名
value:provider服务元信息
赋值时机:ARouter初始化中赋值。
补白:用于运用IProvider完结类class建议的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)
- interceptorsIndex:存储一切拦截器完结类class方针。
key:优先级
value:一切拦截器完结类class方针
赋值时机:是在ARouter初始化时搜集到
- interceptors,一切拦截器实例。是在ARouter初始化完结后当即创立
其中groupsIndex、providersIndex、interceptorsIndex是ARouter初始化时就预备好的根底信息,为事务中随时建议路由操作(Activity跳转、服务获取、拦截器处理)做好预备。
概念5:APT
注解处理器
ARouter运用注解处理器,主动生成路由协助类: 咱们运用ARouter编译后,会在对应模块下主动生成以下类: 这些类的生成规矩都是经过APT在编译器主动生成的,关于APT在ARouter中的运用办法,后边会独自拿一节出来解说:
- Android开源系列-组件化结构Arouter-(三)APT技能详解
概念6:AGP
插件
ARouter运用了一个可选插件:”com.alibaba:arouter-register:1.0.2″ 运用这个插件能够在编译器在包中主动检测以及加载路由表信息,而不需求在运转发动阶段再运用包名去dex文件中加载,提高app发动功率 关于这块的,后边会在:
- Android开源系列-组件化结构Arouter-(四)AGP插件详解
有了以上几个概念做根底现在咱们再到源码中去看看ARouter是怎么跨模块运转起来的
源码剖析:
首要咱们来看路由进程:
- 进程1:初始化ARouter
ARouter.init(this)
- 进程2:注册Activity路由
@Route(path = "/test/activity1", name = "测验用 Activity")
public class Test1Activity extends BaseActivity {
@Autowired
int age = 10;
protected void onCreate(Bundle savedInstanceState) {
ARouter.getInstance().inject(this);
}
}
- 进程3:经过path发动对应的Activity
ARouter.getInstance().build("/test/activity2").navigation();
下面咱们分别来剖析以上进程:
进程1剖析:ARouter.init(this)
/**
* Init, it must be call before used router.
*/
public static void init(Application application) {
if (!hasInit) {
logger = _ARouter.logger;
_ARouter.logger.info(Consts.TAG, "ARouter init start.");
hasInit = _ARouter.init(application);
if (hasInit) {
_ARouter.afterInit();
}
_ARouter.logger.info(Consts.TAG, "ARouter init over.");
}
}
调用了_ARouter同名init办法,进入看看
protected static synchronized boolean init(Application application) {
mContext = application;
LogisticsCenter.init(mContext, executor);
logger.info(Consts.TAG, "ARouter init success!");
hasInit = true;
mHandler = new Handler(Looper.getMainLooper());
return true;
}
内部初始化了一些mContext,mHandler以及字段信息 最重要的是LogisticsCenter.init
(mContext, executor):这句 进入看看:
/**
* LogisticsCenter init, load all metas in memory. Demand initialization
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
try {
//运用AGP插件进行路由表的主动加载
loadRouterMap();
//假如registerByPlugin被设置为true,阐明运用的是插件加载,直接越过
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
//假如是false,则调用下面进程加载
Set<String> routerMap;
// 假如是debug形式或许是新版本的,则每次都会去加载routerMap,这会是一个耗时操作
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// These class was generated by arouter-compiler.
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
//假如是其他的状况,则直接去文件中读取。
logger.info(TAG, "Load router map from cache.");
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
//这儿循环获取routerMap中的信息
for (String className : routerMap) {
//假如className = "com.alibaba.android.arouter.routes.ARouter$$Root"格局,则将路由组信息增加到Warehouse.groupsIndex中
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
//假如className = "com.alibaba.android.arouter.routes.ARouter$$Interceptors"格局,则将拦截器信息增加到Warehouse.interceptorsIndex中
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
//假如className = "com.alibaba.android.arouter.routes.ARouter$$Providers"格局,则将服务Provider信息增加到Warehouse.providersIndex中
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
总结_ARouter的init操作:
- 1.优先运用插件加载路由表信息到库房中,假如没有运用插件,则运用包名com.alibaba.android.arouter.routes去dex文件中查找对应的类方针 查找到后,保存到sp文件中,非debug或许新版本的状况下,下次就直接运用sp文件中缓存的类信息即可。
- 2.查找到对应的类文件后,运用反射调用对应的类的loadInto办法,将路由组,拦截器以及服务Provider信息加载到Warehouse库房中
持续看init办法中给的_ARouter.afterInit
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
找到/arouter/service/interceptor
注解处
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService
这儿给ARouter创立了一个InterceptorServiceImpl
服务的实例方针,后边讲到拦截器的时分会用到
进程2剖析:注册Activity路由
咱们注册的Activity,Provider等路由信息,会在编译器被注解处理器处理后生成对应的路由表:
路由表在进程1中ARouter
初始化的时分被加载到Warehouse
中
进程3剖析:经过path发动对应的Activity
ARouter.getInstance().build("/test/activity2").navigation();
这儿咱们拆分成三个部分:getInstance
,build
,navigation
- 3.1:getInstance
public static ARouter getInstance() {
if (!hasInit) {
throw new InitException("ARouter::Init::Invoke init(context) first!");
} else {
if (instance == null) {
synchronized (ARouter.class) {
if (instance == null) {
instance = new ARouter();
}
}
}
return instance;
}
}
做了init查看并创立了一个ARouter方针
- 3.2:build
public Postcard build(String path) {
return _ARouter.getInstance().build(path);
}
调用了_ARouter的同名build办法
protected Postcard build(String path) {
if (TextUtils.isEmpty(path)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return build(path, extractGroup(path), true);
}
}
1.运用PathReplaceService,能够替换原path为新的path
持续看build办法:
protected Postcard build(String path, String group, Boolean afterReplace) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
if (!afterReplace) {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
}
return new Postcard(path, group);
}
}
看到这儿创立了一个Postcard
,传入path和group,对Postcard前面有解说,这儿不再重复
- 3.3:navigation
最终会走到_ARouter
中的同名navigation
办法中:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//预处理服务
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
// Pretreatment failed, navigation canceled.
return null;
}
try {
//完善PostCard信息 留个点1
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
if (debuggable()) {
// Show friendly tips for user.
runInMainThread(new Runnable() {
@Override
public void run() {
Toast.makeText(mContext, "There's no route matched!\n" +
" Path = [" + postcard.getPath() + "]\n" +
" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
}
});
}
//没有找到路由信息,则直接回来callback.onLost
if (null != callback) {
callback.onLost(postcard);
} else {
// 没有callback则调用大局降级服务DegradeService的onLost办法
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//回调callback.onFound提示用户现已找到path
if (null != callback) {
callback.onFound(postcard);
}
//非绿色通道走到拦截器中
if (!postcard.isGreenChannel()) { // It must be run in async thread, maybe interceptor cost too mush time made ANR.
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
/**
* Continue process
*
* @param postcard route meta
*/
@Override
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
/**
* Interrupt process, pipeline will be destory when this method called.
*
* @param exception Reson of interrupt.
*/
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
//绿色通道直接调用_navigation
return _navigation(postcard, requestCode, callback);
}
return null;
}
办法使命:
- 1.预处理服务
- 2.完善
PostCard
信息 - 3.假如对错绿色通道,则运用拦截器处理恳求
- 4.调用
_navigation
处理
这儿咱们看下第3点:拦截器处理
interceptorService.doInterceptions{
public void onContinue(Postcard postcard) {
_navigation(postcard, requestCode, callback);
}
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
}
}
假如被拦截回调callback.onInterrupt 假如没有就履行_navigation办法
进入interceptorService.doInterceptions看下:
前面剖析过interceptorService是InterceptorServiceImpl方针
@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
private static boolean interceptorHasInit;
private static final Object interceptorInitLock = new Object();
@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
checkInterceptorsInitStatus();
if (!interceptorHasInit) {
callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
return;
}
LogisticsCenter.executor.execute(new Runnable() {
@Override
public void run() {
//运用CancelableCountDownLatch计数器
CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
try {
_execute(0, interceptorCounter, postcard);
interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
if (interceptorCounter.getCount() > 0) { // Cancel the navigation this time, if it hasn't return anythings.
//拦截器处理超时
callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
} else if (null != postcard.getTag()) { // Maybe some exception in the tag.
//拦截器进程出现反常
callback.onInterrupt((Throwable) postcard.getTag());
} else {
//持续履行下面使命onContinue
callback.onContinue(postcard);
}
} catch (Exception e) {
callback.onInterrupt(e);
}
}
});
} else {
callback.onContinue(postcard);
}
}
private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
if (index < Warehouse.interceptors.size()) {
IInterceptor iInterceptor = Warehouse.interceptors.get(index);
iInterceptor.process(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
// Last interceptor excute over with no exception.
counter.countDown();
//递归调用_execute履行拦截器
_execute(index + 1, counter, postcard); // When counter is down, it will be execute continue ,but index bigger than interceptors size, then U know.
}
@Override
public void onInterrupt(Throwable exception) {
// Last interceptor execute over with fatal exception.
postcard.setTag(null == exception ? new HandlerException("No message.") : exception); // save the exception message for backup.
counter.cancel();
// Be attention, maybe the thread in callback has been changed,
// then the catch block(L207) will be invalid.
// The worst is the thread changed to main thread, then the app will be crash, if you throw this exception!
// if (!Looper.getMainLooper().equals(Looper.myLooper())) { // You shouldn't throw the exception if the thread is main thread.
// throw new HandlerException(exception.getMessage());
// }
}
});
}
}
}
拦截器总结:
- 1.运用计数器对拦截器技能,履行开端计数器+1,履行结束计数器-1,假如拦截器履行时刻到,计数器数大于0,则阐明还有未履行完结的拦截器,这个时分就超时了退出
- 2.拦截器履行运用递归的办法进行
- 3.拦截器履行完结持续履行
_navigation
办法
咱们来看_navigation办法:
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
switch (postcard.getType()) {
case ACTIVITY:
// Build intent
final Intent intent = new Intent(currentContext, postcard.getDestination());
intent.putExtras(postcard.getExtras());
// Set flags.
int flags = postcard.getFlags();
if (0 != flags) {
intent.setFlags(flags);
}
// Non activity, need FLAG_ACTIVITY_NEW_TASK
if (!(currentContext instanceof Activity)) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
// Set Actions
String action = postcard.getAction();
if (!TextUtils.isEmpty(action)) {
intent.setAction(action);
}
// Navigation in main looper.
runInMainThread(new Runnable() {
@Override
public void run() {
startActivity(requestCode, currentContext, intent, postcard, callback);
}
});
break;
case PROVIDER:
return postcard.getProvider();
case BOARDCAST:
case CONTENT_PROVIDER:
case FRAGMENT:
Class<?> fragmentMeta = postcard.getDestination();
try {
Object instance = fragmentMeta.getConstructor().newInstance();
if (instance instanceof Fragment) {
((Fragment) instance).setArguments(postcard.getExtras());
} else if (instance instanceof android.support.v4.app.Fragment) {
((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
}
return instance;
} catch (Exception ex) {
logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
}
case METHOD:
case SERVICE:
default:
return null;
}
return null;
}
这个办法其实便是依据PostCard
的type
来处理不同的恳求了
- 1.Activity,直接跳转
- 2.Fragment,Provider,BroadcaseReceiver和ContentProvider,直接回来类的实例方针。
整个进程咱们就基本了解了。 上面还留了一个点:
留的点1:ARouter
是怎么完善PostCard
信息
看LogisticsCenter.completion(postcard);
进入这个办法:
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//去Warehouse.routes去取路由元数据,开端肯定是没有的
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
//没获取到
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
//判别Warehouse.groupsIndex路由组中是否有这个group
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
try {
//动态增加路由元信息到路由中
addRouteGroupDynamic(postcard.getGroup(), null);
} catch (Exception e) {
}
//从头加载。这个时分就会有路由元信息了
completion(postcard); // Reload
}
} else {
//给postcard设置意图地,设置类型,设置优先级,设置Extra等信息
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
...
switch (routeMeta.getType()) {
case PROVIDER: // if the route is provider, should find its instance
// Its provider, so it must implement IProvider
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) { // There's no instance of this provider
IProvider provider;
try {
provider = providerMeta.getConstructor().newInstance();
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
logger.error(TAG, "Init provider failed!", e);
throw new HandlerException("Init provider failed!");
}
}
postcard.setProvider(instance);
postcard.greenChannel(); // Provider should skip all of interceptors
break;
case FRAGMENT:
postcard.greenChannel(); // Fragment needn't interceptors
default:
break;
}
}
}
进入addRouteGroupDynamic
public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
if (Warehouse.groupsIndex.containsKey(groupName)){
// If this group is included, but it has not been loaded
// load this group first, because dynamic route has high priority.
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
看上面代码可知: 数据完善进程是经过组名group去groupsIndex获取对应的组的class方针,然后调用class方针的loadInto办法,将路由元数据加载到Warehouse.routes 然后从头调用completion完善办法去Warehouse.routes中取出路由信息并加载到PostCard中,这样PostCard中就获取到了意图地址信息。
下面我画了一张图描绘了上面的调用进程 一图胜千言
小节结
本节介绍了ARouter
运用进程中 的一些基本概念,理解了这些概念后,咱们再从运用进程触发,对每个运用节点进行了介绍。 最终运用一张图总结了整个运用原理进程: 这儿咱们还有一些悬念:
- 1.ARouter协助类是怎么生成的,这儿运用到了APT注解处理器的技能。
-
2.这儿还有个有趣的现象,咱们在调用路由表加载的时分: 运用了
loadRouterMap
加载,可是查看里边代码:
private static void loadRouterMap() {
registerByPlugin = false;
// auto generate register code by gradle plugin: arouter-auto-register
// looks like below:
// registerRouteRoot(new ARouter..Root..modulejava());
// registerRouteRoot(new ARouter..Root..modulekotlin());
}
居然是空的。。 呃呃呃 不要紧看注解:
auto generate register code by gradle plugin: arouter-auto-register
能够看到这儿运用了arouter-auto-register
插件中主动生成注册代码的办法: 这儿其实便是运用到了字节码插庄技能,动态增加了代码,能够参阅这篇文章:
深化探索编译插桩(三,ASM揭秘以及实战)
好了,本节就到这儿了。
> 持续输出中。。你的点赞,重视是我最大的动力。
下面咱们来说下APT技能。
3.APT技能详解
注解根底:
1.元注解
-
1.
@Target
:方针,表明注解润饰的方针-
ElementType.
ANNOTIONS_TYPE
: 方针是注解,给注解设置的注解 -
ElementType.
CONSTRUCTOR
: 结构办法 -
ElementType.
FIELD
: 特点注解 -
ElementType.
METHOD
: 办法注解 -
ElementType.
Type
: 类型如:类,接口,枚举 -
ElementType.
PACKAGE
: 能够给一个包进行注解 -
ElementType.
PARAMETER
: 能够给一个办法内的参数进行注解 -
ElementType.
LOCAL_VARIABLE
: 能够给局部变量进行注解
-
ElementType.
-
2.
@Retention
:表明需求在什么级别保存该注解信息-
RetentionPolicy.
SOURCE
:在编译阶段有用,编译之后会被丢掉,不会保存到字节码class文件中 -
RetentionPolicy.
CLASS
:注解在class文件中可用,可是会被VM丢掉,在类加载时会被丢掉,在字节码文件处理中有用,注解默许运用这种办法 -
RetentionPolicy.
RUNTIME
:运转时有用,能够经过反射获取注解信息
-
RetentionPolicy.
-
3.
@Document
:将注解包括到javaDoc中 -
4.
@Inherit
:运转子类承继父类的注解 -
5.
@Repeatable
:界说注解可重复
2.元注解的运用办法
- 2.1:基本运用办法
@Target(ElementType.METHOD) ://表明效果在办法中
@Retention(RetentionPolicy.SOURCE) ://表明只在编译器有用
public @interface Demo1 {
public int id(); //注解的值,无默许值,在创立注解的时分需求设置该值
public String desc() default "no info";//注解默许值
}
@Demo1(id=1)
public void getData() {
}
- 2.2:重复注解运用办法
界说Persons
:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Persons {
Person[] value();
}
界说Person
:
@Repeatable(Persons.class)
public @interface Person{
String role() default "";
}
运用运用
:
@Person(role="CEO")
@Person(role="husband")
@Person(role="father")
@Person(role="son")
public class Man {
String name="";
}
调用注解
if(Man.class.isAnnotationPresent(Persons.class)) {先判别是否存在这个注解
Persons p2=Man.class.getAnnotation(Persons.class);获取注解
for(Person t:p2.value()){
System.out.println(t.role());
}
}
成果:
1
CEO
husband
father
son
3.运转时注解
需求运用反射获取
@Retention(RetentionPolicy.RUNTIME)
public void getAnnoInfo() {
Class clazz = GetAnno.class;
//取得一切的办法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
method.setAccessible(true);//禁用安全机制
if (method.isAnnotationPresent(Demo1.class)) {//查看是否运用了Demo1注解
Demo1 demo1 = method.getAnnotation(Demo1.class);//取得注解实例
String name = method.getName();//取得办法称号
}
}
4.编译时注解
需求运用到APT
东西
@Retention(RetentionPolicy.SOURCE
)或许CLASS
注解的获取 能够运用编译期注解动态生成代码,许多优秀的开源库都是运用这个办法:如Arouter
ButterKnife
,GreenDao
,EventBus3
等
APT常识储备
- 1.APT是一种
注解解析东西
:
在编译期
找出源代码中一切的注解信息,假如指定了注解器(承继AbstractProcessor
),那么在编译期会调用这个注解器里边的代码,咱们能够在这儿面做一些处理, 如依据注解信息动态生成一些代码,并将代码注入到源码中
- 运用到的东西类:
东西类1:Element
表明程序的一个元素,它只在编译期存在。能够是package,class,interface,method
,成员变量,函数参数,泛型类型等。
Element
的子类介绍:
-
ExecutableElement
:类或许接口中的办法,结构器或许初始化器等元素 -
PackageElement
:代表一个包元素程序 -
VariableElement
:代表一个类或许接口中的特点或许常量的枚举类型,办法或许结构器的参数,局部变量,资源变量或许反常参数 -
TypeElement
:代表一个类或许接口元素 -
TypeParameterElement
:代表接口,类或许办法的泛型参数元素
经过Element
能够获取什么信息呢?
1.asType() 回来TypeMirror:
TypeMirror是元素的类型信息,包括包名,类(或办法,或参数)名/类型
TypeMirror的子类:
ArrayType, DeclaredType, DisjunctiveType, ErrorType, ExecutableType, NoType, NullType, PrimitiveType, ReferenceType, TypeVariable, WildcardType
getKind能够获取类型:
2.equals(Object obj) 比较两个Element运用equals办法。
3.getAnnotation(Class annotationType) 传入注解能够获取该元素上的一切注解。
4.getAnnotationMirrors() 获该元素上的注解类型。
5.getEnclosedElements() 获取该元素上的直接子元素,相似一个类中有VariableElement。
6.getEnclosingElement() 获取该元素的父元素,
假如是特点VariableElement,则其父元素为TypeElement,
假如是PackageElement则回来null,
假如是TypeElement则回来PackageElement,
假如是TypeParameterElement则回来泛型Element
7.getKind() 回来值为ElementKind,经过ElementKind能够知道是那种element,详细便是Element的那些子类。
8.getModifiers() 获取润饰该元素的拜访润饰符,public,private
9.getSimpleName() 获取元素名,不带包名,
假如是变量,获取的便是变量名,
假如是界说了int age,获取到的name便是age。
假如是TypeElement回来的便是类名
10.getQualifiedName():获取类的全限定名,Element没有这个办法它的子类有,例如TypeElement,得到的便是类的全类名(包名)。
11.Elements.getPackageOf(enclosingElement).asType().toString():获取地点的包名:
东西类2: ProcessingEnvironment
:
APT运转环境
:里边供给了写新文件, 陈述过错或许查找其他东西.
1.getFiler():回来用于创立新的源,类或辅助文件的文件管理器。
2.getElementUtils():回来对元素进行操作的一些有用办法的完结.
3.getMessager():回来用于陈述过错,正告和其他告诉的信使。
4.getOptions():回来传递给注解处理东西的处理器特定选项。
5.getTypeUtils():回来一些用于对类型进行操作的有用办法的完结。
东西类3:ElementKind
怎么判别Element的类型呢,需求用到ElementKind
,ElementKind
为元素的类型,元素的类型判别不需求用instanceof
去判别,而应该经过getKind()
去判别对应的类型
element.getKind()==ElementKind.CLASS;
东西类4: TypeKind
TypeKind
为类型的特点,类型的特点判别不需求用instanceof去判别,而应该经过getKind()
去判别对应的特点
element.asType().getKind() == TypeKind.INT
javapoet:生成java文件
3种生成文件的办法:
- 1.
StringBuilder
进行拼接 - 2.模板文件进行字段替换
- 3.
javaPoet
生成
StringBuilder进行拼接,模板文件进行字段替换进行简略文件生成还好,假如是杂乱文件,拼接起来会相当杂乱
所以一般杂乱的都运用Square
出品的sdk:javapoet
implementation "com.squareup:javapoet:1.11.1"
自己完结自界说APT东西类
进程
:
1.创立一个独自javalib模块lib_annotions
:
创立需求的注解类:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
2.再创立一个javalib
模块lib_compilers
:
在模块中创立一个承继AbstractProcessor
的类:
@AutoService(Processor.class)
public class CustomProcessorTest extends AbstractProcessor {
public Filer filer;
private Messager messager;
private List<String> result = new ArrayList<>();
private int round;
private Elements elementUtils;
private Map<String, String> options;
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> annotations = new LinkedHashSet<>();
annotations.add(CustomBindAnnotation.class.getCanonicalName());
return annotations;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
filer = processingEnvironment.getFiler();
messager = processingEnvironment.getMessager();
elementUtils = processingEnv.getElementUtils();
options = processingEnv.getOptions();
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
messager.printMessage(Diagnostic.Kind.NOTE,"process");
Map<TypeElement, Map<Integer, VariableElement>> typeElementMap = getTypeElementMap(roundEnv);
messager.printMessage(Diagnostic.Kind.NOTE,"2222");
for(TypeElement key:typeElementMap.keySet()){
Map<Integer, VariableElement> variableElementMap = typeElementMap.get(key);
TypeSpec typeSpec = generalTypeSpec(key,variableElementMap);
String packetName = elementUtils.getPackageOf(key).getQualifiedName().toString();
messager.printMessage(Diagnostic.Kind.NOTE,"packetName:"+packetName);
JavaFile javaFile = JavaFile.builder(packetName,typeSpec).build();
try {
javaFile.writeTo(processingEnv.getFiler());
messager.printMessage(Diagnostic.Kind.NOTE,"3333");
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
private TypeSpec generalTypeSpec(TypeElement key,Map<Integer, VariableElement> variableElementMap) {
return TypeSpec.classBuilder(key.getSimpleName().toString()+"ViewBinding")
.addModifiers(Modifier.PUBLIC)
.addMethod(generalMethodSpec(key,variableElementMap))
.build();
}
private MethodSpec generalMethodSpec(TypeElement typeElement, Map<Integer, VariableElement> variableElementMap) {
ClassName className = ClassName.bestGuess(typeElement.getQualifiedName().toString());
String parameter = "_" + toLowerCaseFirstChar(className.simpleName());
MethodSpec.Builder builder = MethodSpec.methodBuilder("bind")
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)
.returns(void.class)
.addParameter(className,parameter);
messager.printMessage(Diagnostic.Kind.NOTE,"typeElement.getQualifiedName().toString():"+typeElement.getQualifiedName().toString());
messager.printMessage(Diagnostic.Kind.NOTE,"typeElement.className():"+className.simpleName().toString());
messager.printMessage(Diagnostic.Kind.NOTE,"parameter:"+parameter);
for(int viewId:variableElementMap.keySet()){
VariableElement variableElement = variableElementMap.get(viewId);
String elementName = variableElement.getSimpleName().toString();
String elementType = variableElement.asType().toString();
messager.printMessage(Diagnostic.Kind.NOTE,"elementName:"+elementName);
messager.printMessage(Diagnostic.Kind.NOTE,"elementType:"+elementType);
// builder.addCode("$L.$L = ($L)$L.findViewById($L);\n",parameter,elementName,elementType,parameter,viewId);
builder.addStatement("$L.$L = ($L)$L.findViewById($L)",parameter,elementName,elementType,parameter,viewId);
}
// for (int viewId : varElementMap.keySet()) {
// VariableElement element = varElementMap.get(viewId);
// String name = element.getSimpleName().toString();
// String type = element.asType().toString();
// String text = "{0}.{1}=({2})({3}.findViewById({4}));";
// builder.addCode(MessageFormat.format(text, parameter, name, type, parameter, String.valueOf(viewId)));
// }
return builder.build();
}
private Map<TypeElement, Map<Integer, VariableElement>> getTypeElementMap(RoundEnvironment roundEnv) {
Map<TypeElement, Map<Integer, VariableElement>> typeElementMap = new HashMap<>();
messager.printMessage(Diagnostic.Kind.NOTE,"1111");
Set<? extends Element> variableElements = roundEnv.getElementsAnnotatedWith(CustomBindAnnotation.class);
for(Element element:variableElements){
VariableElement variableElement = (VariableElement) element;//效果在字段上,能够强制转换为VariableElement
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
Map<Integer, VariableElement> varElementMap = typeElementMap.get(typeElement);
if(varElementMap == null){
varElementMap = new HashMap<>();
typeElementMap.put(typeElement,varElementMap);
}
CustomBindAnnotation customBindAnnotation = variableElement.getAnnotation(CustomBindAnnotation.class);
int viewId = customBindAnnotation.value();
varElementMap.put(viewId,variableElement);
}
return typeElementMap;
}
//将首字母转为小写
private static String toLowerCaseFirstChar(String text) {
if (text == null || text.length() == 0 || Character.isLowerCase(text.charAt(0))) return text;
else return String.valueOf(Character.toLowerCase(text.charAt(0))) + text.substring(1);
}
}
这个类中:重写以下办法
1.getSupportedAnnotationTypes:
该办法首要效果是:回来支撑的注解类型
public Set<String> getSupportedAnnotationTypes() {
Set<String> hashSet = new HashSet<>();
hashSet.add(BindView.class.getCanonicalName());
return hashSet;
}
2.getSupportedSourceVersion:
效果:回来支撑的jdk版本
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
3.init(ProcessingEnvironment processingEnvironment)
效果:回来一个ProcessingEnvironment
这个东西内部有许多处理类
1.getFiler():回来用于创立新的源,类或辅助文件的文件管理器。
2.getElementUtils():回来对元素进行操作的一些有用办法的完结.
3.getMessager():回来用于陈述过错,正告和其他告诉的信使。
4.getOptions():回来传递给注解处理东西的处理器特定选项。
5.getTypeUtils():回来一些用于对类型进行操作的有用办法的完结。
4.process(Set<? extends TypeElement> set, RoundEnvironment environment):
效果:apt中心处理办法,能够在这儿面临搜集到的注解进行处理,生成动态原文件等
3.在模块的build.gradle
文件中
implementation "com.google.auto.service:auto-service:1.0-rc6" //运用Auto-Service来主动注册APT
//Android Plugin for Gradle >= 3.4 或许 Gradle Version >=5.0 都要在自己的annotation processor工程里边增加如下的句子
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation "com.squareup:javapoet:1.11.1"//辅助生成文件的东西类
implementation project(':lib_annotionss')//该模块是注解存再的库中
4.最终编译会主动生成对应的类。
然后在需求的地方加上注解就能够了。
编译器主动生成的文件:
public class AnnotationActivityViewBinding {
public static void bind(AnnotationActivity _annotationActivity) {
_annotationActivity.btn1 = (android.widget.Button)_annotationActivity.findViewById(2131296347);
_annotationActivity.lv = (android.widget.ListView)_annotationActivity.findViewById(2131296475);
_annotationActivity.btn = (android.widget.Button)_annotationActivity.findViewById(2131296346);
}
}
ARouter中APT的运用
咱们来看ARouter源码结构
- app:是ARouter供给的一个测验Demo
- arouter-annotation:这个lib模块中声明了许多注解信息和一些枚举类
- arouter-api:ARouter的中心api,转换进程的中心操作都在这个模块里边
- arouter-compiler:APT处理器,主动生成路由表的进程便是在这儿面完结的
- arouter-gradle-plugin:这是一个编译期运用的Plugin插件,首要效果是用于编译器主动加载路由表,节省运用的发动时刻。
咱们首要看arouter-annotation和arouter-compiler这两个模块
1.arouter-annotation
能够看到这儿面完结了几个注解类
-
Autowired
:特点注解
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.CLASS)
public @interface Autowired {
// 标志咱们外部调用运用的key
String name() default "";
// 假如有要求,必定要传入,不然app会crash
// Primitive type wont be check!
boolean required() default false;
// 注解字段描绘
String desc() default "";
}
@Target({ElementType.FIELD}):指定咱们注解是运用在特点字段上 @Retention(RetentionPolicy.CLASS):指定咱们注解只在编译期存在
-
Interceptor
:拦截器注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Interceptor {
/**
* The priority of interceptor, ARouter will be excute them follow the priority.
*/
int priority();
/**
* The name of interceptor, may be used to generate javadoc.
*/
String name() default "Default";
}
@Target({ElementType.TYPE}):指定注解是在类上
@Retention(RetentionPolicy.CLASS):指定注解在编译期存在
-
Route
:路由注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route {
/**
* Path of route
*/
String path();
/**
* Used to merger routes, the group name MUST BE USE THE COMMON WORDS !!!
*/
String group() default "";
/**
* Name of route, used to generate javadoc.
*/
String name() default "";
/**
* Extra data, can be set by user.
* Ps. U should use the integer num sign the switch, by bits. 10001010101010
*/
int extras() default Integer.MIN_VALUE;
/**
* The priority of route.
*/
int priority() default -1;
}
@Target({ElementType.TYPE}):指定注解是运用在类上
@Retention(RetentionPolicy.CLASS):指定注解是在编译期存在
枚举类:
-
RouteType
:路由类型
public enum RouteType {
ACTIVITY(0, "android.app.Activity"),
SERVICE(1, "android.app.Service"),
PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
BOARDCAST(-1, ""),
METHOD(-1, ""),
FRAGMENT(-1, "android.app.Fragment"),
UNKNOWN(-1, "Unknown route type");
}
TypeKind
public enum TypeKind {
// Base type
BOOLEAN,
BYTE,
SHORT,
INT,
LONG,
CHAR,
FLOAT,
DOUBLE,
// Other type
STRING,
SERIALIZABLE,
PARCELABLE,
OBJECT;
}
model类
-
RouteMeta
:路由元数据
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.
}
总结下arouter-annotation:
- 1.创立了
Autowired
:特点注解,Interceptor
:拦截器注解,Route
:路由注解 - 2.创立了
RouteType
:路由类型枚举,RouteMeta
:路由元数据
2.arouter-compiler
- AutowiredProcessor:特点Autowired注解处理器
- InterceptorProcessor:拦截器Interceptor注解处理器
- RouteProcessor:路由Route注解处理器
- BaseProcessor:注解处理器基类,首要获取一些通用参数,上面三个都承继这个基类
- incremental.annotation.processors:拦截器声明,这儿将咱们需求运用的几个注解处理器做了声明
com.alibaba.android.arouter.compiler.processor.RouteProcessor,aggregating
com.alibaba.android.arouter.compiler.processor.AutowiredProcessor,aggregating
com.alibaba.android.arouter.compiler.processor.InterceptorProcessor,aggregating
下面依次来看:
AutowiredProcessor:
@AutoService(Processor.class)//运用AutoService能够将处理器主动注册到processors文件中
@SupportedAnnotationTypes({ANNOTATION_TYPE_AUTOWIRED}) //设置需求匹配的注解类:"com.alibaba.android.arouter.facade.annotation.Autowired"
public class AutowiredProcessor extends BaseProcessor {
private Map<TypeElement, List<Element>> parentAndChild = new HashMap<>(); // Contain field need autowired and his super class.
private static final ClassName ARouterClass = ClassName.get("com.alibaba.android.arouter.launcher", "ARouter");
private static final ClassName AndroidLog = ClassName.get("android.util", "Log");
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
logger.info(">>> AutowiredProcessor init. <<<");
}
//这是注解处理器的中心办法
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (CollectionUtils.isNotEmpty(set)) {
try {
//这儿将一切声明Autowired注解的特点包括在parentAndChild中:parentAndChild的key为注解的类TypeElement
//parentAndChild{List<Element>{element1,element2,element3...}}
categories(roundEnvironment.getElementsAnnotatedWith(Autowired.class));
//生成协助类
generateHelper();
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void generateHelper() throws IOException, IllegalAccessException {
//获取com.alibaba.android.arouter.facade.template.ISyringe的TypeElement
TypeElement type_ISyringe = elementUtils.getTypeElement(ISYRINGE);
//获取com.alibaba.android.arouter.facade.service.SerializationService的TypeElement
TypeElement type_JsonService = elementUtils.getTypeElement(JSON_SERVICE);
//获取com.alibaba.android.arouter.facade.template.IProvider的TypeMirror:元素的类型信息,包括包名,类(或办法,或参数)名/类型
TypeMirror iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
//获取android.app.Activity的TypeMirror:元素的类型信息,包括包名,类(或办法,或参数)名/类型
TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
//获取android.app.Fragment的TypeMirror:元素的类型信息,包括包名,类(或办法,或参数)名/类型
TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// 生成特点参数的辅助类
ParameterSpec objectParamSpec = ParameterSpec.builder(TypeName.OBJECT, "target").build();
if (MapUtils.isNotEmpty(parentAndChild)) {
//遍历parentAndChild:每个entry运用的key为当时类的TypeElement,value为当时类内部一切运用注解Autowired符号的特点
for (Map.Entry<TypeElement, List<Element>> entry : parentAndChild.entrySet()) {
//MethodSpec生成办法的辅助类 METHOD_INJECT = 'inject'
/**
办法名:inject
办法注解:Override
办法权限:public
办法参数:前面objectParamSpec生成的:Object target
*/
MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(METHOD_INJECT)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(objectParamSpec);
//key为当时类的TypeElement
TypeElement parent = entry.getKey();
//value为当时类内部一切运用注解Autowired符号的特点
List<Element> childs = entry.getValue();
//类的全限定名
String qualifiedName = parent.getQualifiedName().toString();
//类的包名
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
//类的文件名:NAME_OF_AUTOWIRED = $$ARouter$$Autowired,完好fileName = BaseActivity$$ARouter$$Autowired
String fileName = parent.getSimpleName() + NAME_OF_AUTOWIRED;
//TypeSpec生成类的辅助类
/**
类名:BaseActivity$$ARouter$$Autowired
类doc:"DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER."
父类:com.alibaba.android.arouter.facade.template.ISyringe
权限:public
*/
TypeSpec.Builder helper = TypeSpec.classBuilder(fileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_ISyringe))
.addModifiers(PUBLIC);
//生成字段特点辅助类
/**
字段类型:SerializationService
字段名:serializationService
字段特点:private
*/
FieldSpec jsonServiceField = FieldSpec.builder(TypeName.get(type_JsonService.asType()), "serializationService", Modifier.PRIVATE).build();
//将字段增加到类:BaseActivity$$ARouter$$Autowired中
helper.addField(jsonServiceField);
/**
给inject办法增加句子:这儿parent = BaseActivity
1.serializationService = ARouter.getInstance().navigation(SerializationService.class);
2.BaseActivity substitute = (BaseActivity)target;
*/
injectMethodBuilder.addStatement("serializationService = $T.getInstance().navigation($T.class)", ARouterClass, ClassName.get(type_JsonService));
injectMethodBuilder.addStatement("$T substitute = ($T)target", ClassName.get(parent), ClassName.get(parent));
/**
生成办法内部代码,注入特点
*/
for (Element element : childs) {
//获取当时element注解Autowired的特点:
Autowired fieldConfig = element.getAnnotation(Autowired.class);
//获取注解的称号
String fieldName = element.getSimpleName().toString();
//判别是否是iProvider的子类,阐明iProvider字段假如运用Autowired注解的话,会独自处理
if (types.isSubtype(element.asType(), iProvider)) { // It's provider
if ("".equals(fieldConfig.name())) { // User has not set service path, then use byType.
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = $T.getInstance().navigation($T.class)",
ARouterClass,
ClassName.get(element.asType())
);
} else { // use byName
// Getter
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = ($T)$T.getInstance().build($S).navigation()",
ClassName.get(element.asType()),
ARouterClass,
fieldConfig.name()
);
}
// Validator 这儿假如设置了required为true,则必定要有值,否则会报错
if (fieldConfig.required()) {
injectMethodBuilder.beginControlFlow("if (substitute." + fieldName + " == null)");
injectMethodBuilder.addStatement(
"throw new RuntimeException("The field '" + fieldName + "' is null, in class '" + $T.class.getName() + "!")", ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
} else { // It's normal intent value
//普通特点
/**
假定fieldName = "name"
originalValue = "substitute.name"
statement = "substitute.name = substitute."
*/
String originalValue = "substitute." + fieldName;
String statement = "substitute." + fieldName + " = " + buildCastCode(element) + "substitute.";
boolean isActivity = false;
//判别是Activity 则statement += "getIntent()."
if (types.isSubtype(parent.asType(), activityTm)) { // Activity, then use getIntent()
isActivity = true;
statement += "getIntent().";
//判别是Fragment 则statement += "getArguments()."
} else if (types.isSubtype(parent.asType(), fragmentTm) || types.isSubtype(parent.asType(), fragmentTmV4)) { // Fragment, then use getArguments()
statement += "getArguments().";
} else {
//非Activity和Fragment,其他状况抛反常
throw new IllegalAccessException("The field [" + fieldName + "] need autowired from intent, its parent must be activity or fragment!");
}
//statement = "substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras()
statement = buildStatement(originalValue, statement, typeUtils.typeExchange(element), isActivity, isKtClass(parent));
if (statement.startsWith("serializationService.")) { // Not mortals
injectMethodBuilder.beginControlFlow("if (null != serializationService)");
injectMethodBuilder.addStatement(
"substitute." + fieldName + " = " + statement,
(StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name()),
ClassName.get(element.asType())
);
injectMethodBuilder.nextControlFlow("else");
injectMethodBuilder.addStatement(
"$T.e("" + Consts.TAG + "", "You want automatic inject the field '" + fieldName + "' in class '$T' , then you should implement 'SerializationService' to support object auto inject!")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
} else {
//将statement注入到injectMethodBuilder办法中
injectMethodBuilder.addStatement(statement, StringUtils.isEmpty(fieldConfig.name()) ? fieldName : fieldConfig.name());
}
// 增加null判别
if (fieldConfig.required() && !element.asType().getKind().isPrimitive()) { // Primitive wont be check.
injectMethodBuilder.beginControlFlow("if (null == substitute." + fieldName + ")");
injectMethodBuilder.addStatement(
"$T.e("" + Consts.TAG + "", "The field '" + fieldName + "' is null, in class '" + $T.class.getName() + "!")", AndroidLog, ClassName.get(parent));
injectMethodBuilder.endControlFlow();
}
}
}
//将办法inject注入到类中
helper.addMethod(injectMethodBuilder.build());
//生成java文件
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
logger.info(">>> " + parent.getSimpleName() + " has been processed, " + fileName + " has been generated. <<<");
}
logger.info(">>> Autowired processor stop. <<<");
}
}
/**
* Categories field, find his papa.
*
* @param elements Field need autowired
*/
private void categories(Set<? extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
for (Element element : elements) {
//获取element的父元素:假如是特点,父元素便是类或许接口:TypeElement
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
//假如element特点是PRIVATE,则直接报错,所以对于需求依靠注入的特点,必定不能为private
if (element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
}
//判别parentAndChild是否包括enclosingElement,榜首次循环是空值会走到else分支,第二次才会包括
//格局:parentAndChild{List<Element>{element1,element2,element3...}}
if (parentAndChild.containsKey(enclosingElement)) { // Has categries
parentAndChild.get(enclosingElement).add(element);
} else {
List<Element> childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
}
经过在编译器运用注解处理器AutowiredProcessor处理后,主动生成了以下文件 BaseActivity$$ARouter$$Autowired.java
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class BaseActivity$$ARouter$$Autowired implements ISyringe {
private SerializationService serializationService;
@Override
public void inject(Object target) {
serializationService = ARouter.getInstance().navigation(SerializationService.class);
BaseActivity substitute = (BaseActivity)target;
substitute.name = substitute.getIntent().getExtras() == null ? substitute.name : substitute.getIntent().getExtras().getString("name", substitute.name);
}
}
生成进程:
1.运用Map<TypeElement, List> parentAndChild = new HashMap<>()存储一切被Autowired注解的特点 key:每个类的TypeElement value:当时类TypeElement中一切的Autowired注解的特点字段
2.运用ParameterSpec生成参数
3.运用MethodSpec生成办法:METHOD_INJECT = ‘inject’
办法名:inject
办法注解:Override
办法权限:public
办法参数:前面objectParamSpec生成的:Object target
4.运用TypeSpec生成类:
类名:BaseActivity$$ARouter$$Autowired
类doc:"DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER."
父类:com.alibaba.android.arouter.facade.template.ISyringe
权限:public
5.运用addStatement给办法增加句子body
6.将办法注入到协助类中
helper.addMethod(injectMethodBuilder.build());
7.写入java文件
JavaFile.builder(packageName, helper.build()).build().writeTo(mFiler);
RouteProcessor
@AutoService(Processor.class)
@SupportedAnnotationTypes({ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
//这儿表明咱们的RouteProcessor能够处理Route和Autowired两种注解
public class RouteProcessor extends BaseProcessor {
private Map<String, Set<RouteMeta>> groupMap = new HashMap<>(); // ModuleName and routeMeta.
private Map<String, String> rootMap = new TreeMap<>(); // Map of root metas, used for generate class file in order.
private TypeMirror iProvider = null;
private Writer docWriter; // Writer used for write doc
//初始化
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
//这儿假如支撑generateDoc,则翻开docWriter,待写入文件:generateDoc字段由模块中的build.gradle文件传入
if (generateDoc) {
try {
docWriter = mFiler.createResource(
StandardLocation.SOURCE_OUTPUT,
PACKAGE_OF_GENERATE_DOCS,
"arouter-map-of-" + moduleName + ".json"
).openWriter();
} catch (IOException e) {
logger.error("Create doc writer failed, because " + e.getMessage());
}
}
//获取IPROVIDER的类型TypeMirror
iProvider = elementUtils.getTypeElement(Consts.IPROVIDER).asType();
logger.info(">>> RouteProcessor init. <<<");
}
//中心处理api
/**
* {@inheritDoc}
*
* @param annotations
* @param roundEnv
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty(annotations)) {
Set<? extends Element> routeElements = roundEnv.getElementsAnnotatedWith(Route.class);
try {
logger.info(">>> Found routes, start... <<<");
//解析Routes
this.parseRoutes(routeElements);
} catch (Exception e) {
logger.error(e);
}
return true;
}
return false;
}
private void parseRoutes(Set<? extends Element> routeElements) throws IOException {
if (CollectionUtils.isNotEmpty(routeElements)) {
// prepare the type an so on.
logger.info(">>> Found routes, size is " + routeElements.size() + " <<<");
rootMap.clear();
//获取Activity的TypeMirror
TypeMirror type_Activity = elementUtils.getTypeElement(ACTIVITY).asType();
//获取Service的TypeMirror
TypeMirror type_Service = elementUtils.getTypeElement(SERVICE).asType();
//获取Fragment的TypeMirror
TypeMirror fragmentTm = elementUtils.getTypeElement(FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
// Interface of ARouter
//获取IRouteGroup的TypeElement
TypeElement type_IRouteGroup = elementUtils.getTypeElement(IROUTE_GROUP);
////获取IProviderGroup的TypeElement
TypeElement type_IProviderGroup = elementUtils.getTypeElement(IPROVIDER_GROUP);
//获取RouteMeta的ClassName:权限定名
ClassName routeMetaCn = ClassName.get(RouteMeta.class);
//获取RouteType的ClassName:权限定名
ClassName routeTypeCn = ClassName.get(RouteType.class);
/*创立Map<String, Class<? extends IRouteGroup>>类型的ParameterizedTypeName
Build input type, format as :
```Map<String, Class<? extends IRouteGroup>>```
*/
ParameterizedTypeName inputMapTypeOfRoot = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ParameterizedTypeName.get(
ClassName.get(Class.class),
WildcardTypeName.subtypeOf(ClassName.get(type_IRouteGroup))
)
);
/*创立Map<String, RouteMeta>类型的ParameterizedTypeName
```Map<String, RouteMeta>```
*/
ParameterizedTypeName inputMapTypeOfGroup = ParameterizedTypeName.get(
ClassName.get(Map.class),
ClassName.get(String.class),
ClassName.get(RouteMeta.class)
);
/*创立参数类型rootParamSpec,groupParamSpec,providerParamSpec
Build input param name.
*/
ParameterSpec rootParamSpec = ParameterSpec.builder(inputMapTypeOfRoot, "routes").build();
ParameterSpec groupParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "atlas").build();
ParameterSpec providerParamSpec = ParameterSpec.builder(inputMapTypeOfGroup, "providers").build(); // Ps. its param type same as groupParamSpec!
/*创立loadInto办法的MethodSpec
Build method : 'loadInto'
*/
MethodSpec.Builder loadIntoMethodOfRootBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(rootParamSpec);
// Follow a sequence, find out metas of group first, generate java file, then statistics them as root.
//遍历routeElements一切的path注解方针
for (Element element : routeElements) {
//获取方针element的TypeMirror
TypeMirror tm = element.asType();
//获取element的注解Route
Route route = element.getAnnotation(Route.class);
RouteMeta routeMeta;
// Activity or Fragment 假如是Activity或许Fragment:依据不同状况创立不同的routeMeta路由元数据
if (types.isSubtype(tm, type_Activity) || types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
// Get all fields annotation by @Autowired
Map<String, Integer> paramsType = new HashMap<>();
Map<String, Autowired> injectConfig = new HashMap<>();
//这儿是搜集一切的Autowired特点参数
injectParamCollector(element, paramsType, injectConfig);
if (types.isSubtype(tm, type_Activity)) {
// Activity
logger.info(">>> Found activity route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
} else {
// Fragment
logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), paramsType);
}
routeMeta.setInjectConfig(injectConfig);
} else if (types.isSubtype(tm, iProvider)) { // IProvider
logger.info(">>> Found provider route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
} else if (types.isSubtype(tm, type_Service)) { // Service
logger.info(">>> Found service route: " + tm.toString() + " <<<");
routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
} else {
throw new RuntimeException("The @Route is marked on unsupported class, look at [" + tm.toString() + "].");
}
//搜集路由元数据
categories(routeMeta);
}
//创立IProvider注解的loadInto办法
MethodSpec.Builder loadIntoMethodOfProviderBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(providerParamSpec);
Map<String, List<RouteDoc>> docSource = new HashMap<>();
// Start generate java source, structure is divided into upper and lower levels, used for demand initialization.
for (Map.Entry<String, Set<RouteMeta>> entry : groupMap.entrySet()) {
String groupName = entry.getKey();
//创立IGroupRouter的loadInto办法
MethodSpec.Builder loadIntoMethodOfGroupBuilder = MethodSpec.methodBuilder(METHOD_LOAD_INTO)
.addAnnotation(Override.class)
.addModifiers(PUBLIC)
.addParameter(groupParamSpec);
List<RouteDoc> routeDocList = new ArrayList<>();
// 创立 group 办法的 body
Set<RouteMeta> groupData = entry.getValue();
for (RouteMeta routeMeta : groupData) {
RouteDoc routeDoc = extractDocInfo(routeMeta);
ClassName className = ClassName.get((TypeElement) routeMeta.getRawType());
switch (routeMeta.getType()) {
//创立PROVIDER的loadInto办法体
case PROVIDER: // Need cache provider's super class
List<? extends TypeMirror> interfaces = ((TypeElement) routeMeta.getRawType()).getInterfaces();
for (TypeMirror tm : interfaces) {
routeDoc.addPrototype(tm.toString());
if (types.isSameType(tm, iProvider)) { // Its implements iProvider interface himself.
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
(routeMeta.getRawType()).toString(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
} else if (types.isSubtype(tm, iProvider)) {
// This interface extend the IProvider, so it can be used for mark provider
loadIntoMethodOfProviderBuilder.addStatement(
"providers.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, null, " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
tm.toString(), // So stupid, will duplicate only save class name.
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath(),
routeMeta.getGroup());
}
}
break;
default:
break;
}
// Make map body for paramsType
StringBuilder mapBodyBuilder = new StringBuilder();
Map<String, Integer> paramsType = routeMeta.getParamsType();
Map<String, Autowired> injectConfigs = routeMeta.getInjectConfig();
if (MapUtils.isNotEmpty(paramsType)) {
List<RouteDoc.Param> paramList = new ArrayList<>();
for (Map.Entry<String, Integer> types : paramsType.entrySet()) {
mapBodyBuilder.append("put("").append(types.getKey()).append("", ").append(types.getValue()).append("); ");
RouteDoc.Param param = new RouteDoc.Param();
Autowired injectConfig = injectConfigs.get(types.getKey());
param.setKey(types.getKey());
param.setType(TypeKind.values()[types.getValue()].name().toLowerCase());
param.setDescription(injectConfig.desc());
param.setRequired(injectConfig.required());
paramList.add(param);
}
routeDoc.setParams(paramList);
}
String mapBody = mapBodyBuilder.toString();
//创立IGroupRouter的办法体
loadIntoMethodOfGroupBuilder.addStatement(
"atlas.put($S, $T.build($T." + routeMeta.getType() + ", $T.class, $S, $S, " + (StringUtils.isEmpty(mapBody) ? null : ("new java.util.HashMap<String, Integer>(){{" + mapBodyBuilder.toString() + "}}")) + ", " + routeMeta.getPriority() + ", " + routeMeta.getExtra() + "))",
routeMeta.getPath(),
routeMetaCn,
routeTypeCn,
className,
routeMeta.getPath().toLowerCase(),
routeMeta.getGroup().toLowerCase());
routeDoc.setClassName(className.toString());
routeDocList.add(routeDoc);
}
// Generate groups 生成IGroupRrouter的子类文件
String groupFileName = NAME_OF_GROUP + groupName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(groupFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IRouteGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfGroupBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated group: " + groupName + "<<<");
rootMap.put(groupName, groupFileName);
docSource.put(groupName, routeDocList);
}
if (MapUtils.isNotEmpty(rootMap)) {
// Generate root meta by group name, it must be generated before root, then I can find out the class of group.
for (Map.Entry<String, String> entry : rootMap.entrySet()) {
loadIntoMethodOfRootBuilder.addStatement("routes.put($S, $T.class)", entry.getKey(), ClassName.get(PACKAGE_OF_GENERATE_FILE, entry.getValue()));
}
}
// Output route doc
if (generateDoc) {
//将path联系写入doc
docWriter.append(JSON.toJSONString(docSource, SerializerFeature.PrettyFormat));
docWriter.flush();
docWriter.close();
}
// Write provider into disk 写入provider
String providerMapFileName = NAME_OF_PROVIDER + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(providerMapFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(type_IProviderGroup))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfProviderBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated provider map, name is " + providerMapFileName + " <<<");
// Write root meta into disk.写入root meta
String rootFileName = NAME_OF_ROOT + SEPARATOR + moduleName;
JavaFile.builder(PACKAGE_OF_GENERATE_FILE,
TypeSpec.classBuilder(rootFileName)
.addJavadoc(WARNING_TIPS)
.addSuperinterface(ClassName.get(elementUtils.getTypeElement(ITROUTE_ROOT)))
.addModifiers(PUBLIC)
.addMethod(loadIntoMethodOfRootBuilder.build())
.build()
).build().writeTo(mFiler);
logger.info(">>> Generated root, name is " + rootFileName + " <<<");
}
}
}
生成进程: 和上面生成AutoWried进程相似,都是运用javapoet的api生成对应的java文件 这儿咱们需求生成三种文件:
-
ARouter$$Root$$xxx
:xxx是当时模块名的缩写,存储当时模块路由组的信息:value是路由组的类名
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Root$$modulejava implements IRouteRoot {
@Override
public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
routes.put("m2", ARouter$$Group$$m2.class);
routes.put("module", ARouter$$Group$$module.class);
routes.put("test", ARouter$$Group$$test.class);
routes.put("yourservicegroupname", ARouter$$Group$$yourservicegroupname.class);
}
}
-
ARouter$$Group$$xxx
:xxx是当时路由组的组名,存储一个路由组内路由的信息:内部包括多个路由信息
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Group$$test implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("ch", 5); put("fl", 6); put("dou", 7); put("boy", 0); put("url", 8); put("pac", 10); put("obj", 11); put("name", 8); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", new java.util.HashMap<String, Integer>(){{put("ser", 9); put("pac", 10); put("ch", 5); put("obj", 11); put("fl", 6); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 11); put("map", 11); put("age", 3); put("height", 3); }}, -1, -2147483648));
atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
}
}
-
ARouter$$Providers$$xxx
,xxx是模块名,存储的是当时模块中的IProvider信息,key是IProvider的称号,value是RouteMeta路由元数据
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY AROUTER. */
public class ARouter$$Providers$$modulejava implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.alibaba.android.arouter.demo.service.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
providers.put("com.alibaba.android.arouter.demo.module1.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/yourservicegroupname/single", "yourservicegroupname", null, -1, -2147483648));
}
}
还有其他比方拦截器的java文件生成办法就不再描绘了,和前面两个注解处理器是相同的原理。 主动生成了这些协助类之后,在编译器或许运转期,经过调用这些类的loadInto
办法,能够将路由元信息加载到内存中。
总结
本文首要以三个层次来解说Arouter的常识系统 :1 .根底用法 2.内部原理 3.APT技能,对Arouter做了一个详细的介绍,相信看完本文你对Arouter现已很熟悉了 。
我是小余,假如你觉得文章对你有协助欢迎点赞和重视。
参阅
Arouter官方文档