arouter-compiler模块用于处理注解,编译期生成类文件,annotationProcessor 'com.alibaba:arouter-compiler:latestversion'
即是引入的这个module。annotationProcessor的作用是在编译期处理注解,并不会打包进apk。
我们在要跳转的Activity上面添加了@Route注解,ARouter 的自动注册是利用了编译期自定义注解的处理来完成的,我们看一下这个注解的定义:
@Target({
ElementType.TYPE})
@Retention(RetentionPolicy.CLASS)
public @interface Route{
String path();
String group() default "";
String name() default "";
int extras() default Integer.MIN_VALUE;
int priority() default -1;
}
Route 使用 path 来唯一标识一个路由节点,而 group 用于将路由节点进行分组,实现按组动态加载,@Route 不仅可用于 Activity 类,还可用于模块对外接口的实现类,实现类似于 AIDL 的功能。
APT就是注解处理器,它的作用是在编译阶段扫描并处理代码中的注解,然后根据注解输出Java文件。
自己构造APT,需要写一个类继承AbstractProcessor然后重写它的方法,ARouter的注解处理器是RouteProcessor
@AutoService(Processor.class)
@SupportedAnnotationTypes({
ANNOTATION_TYPE_ROUTE, ANNOTATION_TYPE_AUTOWIRED})
public class RouteProcessor extends BaseProcessor {
...
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
...
}
这里面调用了其父类BaseProcessor的init方法
public abstract class BaseProcessor extends AbstractProcessor {
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
...
// Attempt to get user configuration [moduleName]
Map<String, String> options = processingEnv.getOptions();
if (MapUtils.isNotEmpty(options)) {
moduleName = options.get(KEY_MODULE_NAME);
generateDoc = VALUE_ENABLE.equals(options.get(KEY_GENERATE_DOC_NAME));
}
BaseProcessor的init方法中获取每个模块的moduleName并存入map集合options。
RouteProcessor中的process方法是处理注解方法的地方,在这个方法中会获取所有Route注解的元素:
public class RouteProcessor extends BaseProcessor {
...
@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... <<<");
this.parseRoutes(routeElements);
...
}
process方法中获取所有注解了Route的元素放到set集合routeElement中,并把这个当作参数传入this.parseRoutes方法中。这个方法会调用poet库生成Java文件,他生成了两个类,一个是ARouter xxx implement IRouterGroup,一个是ARouter xxx implement IRouteRoot。
看下编译后生成的文件
public class ARouter$$Group$$arouter implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
atlas.put("/arouter/service/autowired", RouteMeta.build(RouteType.PROVIDER, AutowiredServiceImpl.class, "/arouter/service/autowired", "arouter", null, -1, -2147483648));
atlas.put("/arouter/service/interceptor", RouteMeta.build(RouteType.PROVIDER, InterceptorServiceImpl.class, "/arouter/service/interceptor", "arouter", null, -1, -2147483648));
}
}
这个类实现了IRouterGroup接口,实现了loadInto方法,路径名path为key,RouteMeta为value放入map集合atlas中,其中RouteMeta是存放了@Route注解修饰的元素的一些重要信息,有了这个,我们后续可以通过intent来跳转到这个Activity了。这个方法调用之后,activity就完成了注册。
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);
}
}
实现了IRouteRoot接口,它的loadInto方法中,以路径名后面第一个斜杠后面的内容Group为key,以上一个类IRouterGroup类为value放入集合routes中。可以通过Group来获取到IRouterGroup的实现类,通过这个实现类拿到Activity.class。
在apt阶段,生成的class类有Arouter$$Root$$模块
名,模块名是在gradle中配置的arg("AROUTER_MODULE_NAME", "${project.getName()}")
属性,把所有组的信息放到传进来的map中,这个组是通过我们在Route注解的path属性拆分的,比如定义/login/loginActivity,会认为组名是login,Arouter$$Root$$组名
放的是该组下,所有的路由表信息,包括route、provider注解通过RouteMeta包装对应的class类信息,provider注解会放在Arouter$$Providers$$模块名
下面。
再来梳理一下,ARouter发现服务是这样的,如果有一个类HelloService继承IProvider,并且HelloService有path的注解的话
@Route(path = "/module1/hello")
public class HelloService implements IProvider {
@Override
public void init(Context context) {
}
}
ARouter会生成这样的类文件,module1是模块名,可以看到往传进来的一个Map中放入了一个以HelloService为key,对应的RouteMeta为value的键值对。RouteMeta可以理解为跳转的元信息,可以通过RouteMeta获取跳转的path,group以及对应的目标Class等:
public class ARouter$$Providers$$module1 implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.example.module1.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloService.class, "/module1/hello", "module1", null, -1, -2147483648));
}
}
public class ARouter$$Providers$$module1 implements IProviderGroup {
@Override
public void loadInto(Map<String, RouteMeta> providers) {
providers.put("com.example.module1.IUserService", RouteMeta.build(RouteType.PROVIDER, UserServiceImpl1.class, "/u/1", "u", null, -1, -2147483648));
providers.put("com.example.module1.IUserService", RouteMeta.build(RouteType.PROVIDER, UserServiceImpl2.class, "/u/2", "u", null, -1, -2147483648));
}
}
同样实现IUserService会使它们有相同的key,后面put的元素会覆盖掉之前put的元素。如果接口不止一个实现,字母表排在后面的接口会覆盖掉排在前面的接口。
为什么不能用抽象类继承IProvider然后实现抽象类而只能用接口继承IProvider然后实现该接口
这是因为ARouter在RouteProcessor的parseRoutes方法中只处理了接口的情况。
如果我们注解相同的path会怎么样?即有一个SecondActivity使用/a/b的path,而另一个ThirdActivity也使用/a/b的path。
(1)如果SecondActivity和ThirdActivity在同一个module下:RouteProcessor有一个成员变量groupMapprivate Map
key是string,即group的名字,value是一个Set,当试图放入一个Set中已有的元素时,会放入不了,并且不会抛异常,如果我们在同一个module中注解相同的path,那么排在字母表后面的元素会无效,ThirdActivity由于没有被添加到Set中因此不会再生成的文件中出现
(2)如果SecondActivity和ThirdActivity在不同的module下:由于apt框架是分module编译,并且每个module都会生成ARouter$$Root$module_name
,ARouter$$Group$$group_name
等文件,那么它们都会生成ARouter$$Group$$a
的文件,那么在合并到dex的时候肯定会出错,这时候根本就编译过不了
初始化代码ARouter.init(this)
init过程就是把所有注解的信息加载内存中,并且完成所有拦截器的初始化。
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方法以及_ARouter.afterInit方法,真正调用的还是_ARouter类的方法
看_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;
}
最终调用了LogisticsCenter的init方法,传入线程池executor。并且拿到主线的looper给了mHandler,看LogisticsCenter的init方法:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
//如果通过插件获取路由表信息,则该方法registerByPlugin=false
loadRouterMap();
if (registerByPlugin) {
} else {
Set<String> routerMap;
// 如果是debuggable=true或者有新的版本
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// 加载前缀为com.alibaba.android.arouter.routes的class类,放到set集合里面
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
//将过滤到的class类放到sp中,方便下次直接取
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 {
//直接从sp中取过滤到的class类
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
for (String className : routerMap) {
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// 将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// 将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的class类放到Warehouse.interceptorsIndex中
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// 将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的class类放到Warehouse.providersIndex中
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
init方法中做了几件事:
ClassUtils.getFileNameByPackageName
(此处用到了线程池、CountDownLatch面试高频考点)获取到apk中前缀为com.alibaba.android.arouter.routes
的类,这里面主要是通过判断是不是支持MultiDex,如果不支持MultiDex,扫描所有的dex文件,然后压缩成zip文件,然后通过DexFile.loadDex转化成DexFile对象,如果支持MultiDex,直接new DexFile,然后循环DexFile拿里面的class文件,然后过滤出com.alibaba.android.arouter.routes
前缀的class并返回。(方法做的就是找到app的dex,然后遍历出其中的属于com.alibaba.android.arouter.routes
包下的所有类名,打包成集合返回)拿到所有生成类名的集合后,通过反射实例化对象并调用方法,将注解的一些元素添加到static集合中
class Warehouse {
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
static Map<String, RouteMeta> routes = new HashMap<>();
// Cache provider
static Map<Class, IProvider> providers = new HashMap<>();
static Map<String, RouteMeta> providersIndex = new HashMap<>();
// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List<IInterceptor> interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
_ARouter.afterInit方法里面会去拿InterceptorServiceImpl
static void afterInit() {
// Trigger interceptor init, use byName.
interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
ARouter.getInstance().build("/arouter/service/interceptor")会返回PostCard对象,并给PostCard的group和path属性赋值为arouter、/arouter/service/interceptor
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);
}
}
PathReplaceService,它是继承IProvider的接口,它是预留给用户实现路径动态变化功能。extractGroup方法截取路径中的第一段作为分组名。build方法会创建并返回一个Postcard(明信片)对象,将path,group和bundle传入Postcard中。
navigation有很多重载的方法,最终都会走_Arouter.navigation,其中navigation里面也有两种形式获取到路由表类,先看activity常规的形式
(省略provider部分逻辑和_navigation部分代码,后面再说)
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
//这也是个路由表,通过另外一种形式获取PretreatmentService的实例
PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
//如果onPretreatment返回false就是自己处理路由逻辑,不往下走了
if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
return null;
}
try {
//最终会走这里
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
logger.warning(Consts.TAG, ex.getMessage());
...
if (null != callback) {
callback.onLost(postcard);
} else {
// 获取DegradeService的实例
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
//省略provider部分逻辑和_navigation部分代码,后面再说
return null;
}
首先获取PretreatmentService类型的路由表,怎么得到InterceptorService实例的,我们直接看LogisticsCenter.completion:
public synchronized static void completion(Postcard postcard) {
//第一次进来是拿不到RouteMeta信息的,因为routes是空的
//先去Warehouse的routes这个Map去找有没有相应的RouteMeta,如果没有的话,就去Warehouse的groupsIndex加载该group,然后这个时候routes这个map肯定有相应的RouteMeta了。
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
//我们传过来的postcard的group是arouter、path是/arouter/service/interceptor
//我们在groupIndex中找对应的groupMeta,其实看到这的时候,我们默认是没有root为arouter的组,只能去arouter默认提供的root中找
Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.
if (null == groupMeta) {
} else {
try {
//反射拿到ARouter$$Group$$arouter
IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
//所以最终把InterceptorServiceImpl放到了Warehouse.routes中
iGroupInstance.loadInto(Warehouse.routes);
//用完groupsIndex对应的IRouteGroup信息后,从map中移除掉,下次就直接从routes中去拿了
Warehouse.groupsIndex.remove(postcard.getGroup());
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
//继续走一遍completion,下次会走下面的else
completion(postcard);
}
} else {
//对postCard属性赋值
postcard.setDestination(routeMeta.getDestination());
postcard.setType(routeMeta.getType());
postcard.setPriority(routeMeta.getPriority());
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
//默认uri为空
if (null != rawUri) {
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
setValue(postcard,
params.getValue(),
params.getKey(),
resultMap.get(params.getKey()));
}
// Save params name which need auto inject.
postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{
}));
}
// Save raw uri
postcard.withString(ARouter.RAW_URI, rawUri.toString());
}
switch (routeMeta.getType()) {
//由于InterceptorServiceImpl是provider类型的
case PROVIDER:
Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
//拿对应的provider
//从Warehouse的providers这个map中去获取class对应的对象,如果有的话,就直接使用,如果没有的话,就通过反射创建一个实例,并添加到Warehouse的providers这个map中。所以每次通过ARouter的接口的方式发现服务,每次获取的都是同一个对象。
IProvider instance = Warehouse.providers.get(providerMeta);
if (null == instance) {
IProvider provider;
try {
//反射创建InterceptorServiceImpl
provider = providerMeta.getConstructor().newInstance();
//调用InterceptorServiceImpl的init方法
provider.init(mContext);
Warehouse.providers.put(providerMeta, provider);
instance = provider;
} catch (Exception e) {
throw new HandlerException("Init provider failed! " + e.getMessage());
}
}
//给postcard赋值
postcard.setProvider(instance);
postcard.greenChannel();
break;
case FRAGMENT:
postcard.greenChannel();
default:
break;
}
}
}
completion方法中,使用postcard的path,去path与跳转目标的map中获取routeMeta对象,从而获取其中的destination。postCard封装了跳转的destination目标,如果postCard的type为activity,则创建intent,将postCard中的bundle传入intent的extras,调用startActivity进行跳转。如果type为fragment,则通过postCard拿到目标fragment类,通过反射获取该Fragment实例。
回到_ARouter的navigation方法的下半部分代码,
//如果是`InterceptorServiceImpl`类型的postcard.isGreenChannel()是true,除非是activity或fragment类型的
if (!postcard.isGreenChannel()) {
interceptorService.doInterceptions(postcard, new InterceptorCallback() {
@Override
public void onContinue(Postcard postcard) {
_navigation(context, postcard, requestCode, callback);
}
@Override
public void onInterrupt(Throwable exception) {
if (null != callback) {
callback.onInterrupt(postcard);
}
logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
}
});
} else {
return _navigation(context, postcard, requestCode, callback);
}
在_ARouter的navigation跳转之前使用doInterceptions去做拦截处理:
public interface InterceptorService extends IProvider {
void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
看_navigation方法,实际上的跳转还是通过intent
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = null == context ? mContext : context;
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();
}
return null;
}