越来越多的项目引入ARouter库来配合组件化开发,引入ARouter基本上成了项目标配,那么熟悉ARouter源码就变得尤为重要了。
ARouter的优势:
- 支持多模块使用,支持组件化开发
- 使用注解,实现了映射关系自动注册与分布式路由管理
- 编译期间处理注解,并生成映射文件,没有使用反射,不影响运行时性能
- 映射关系按组分类、多级管理,按需初始化
- 灵活的降级策略,每次跳转都会回调跳转结果,避免StartActivity()一旦失败将会抛出异常
- 自定义拦截器,自定义拦截顺序,可以对路由进行拦截,比如登录判断和埋点处理
- 支持依赖注入,可单独作为依赖注入框架使用,从而实现跨模块API调用
- 支持直接解析标准URL进行跳转,并自动注入参数到目标页面中
- 支持获取Fragment
其中,编译期间处理注解使用了APT技术,引用了JavaPoet技术来自动生成。
APT是什么?
APT(Annotation Processing Tool)即注解处理器,是一种处理注解的工具,确切的说它是javac的一个工具,它用来在编译时扫描和处理注解。注解处理器以Java代码(或者编译过的字节码)作为输入,生成.java文件作为输出。
简单来说就是在编译期,通过注解生成.java文件。还有像EventBus,Butterknife,DataBinding等框架,也是使用了APT。
JavaPoet是什么?
JavaPoet是squaure公司推出的开源java代码生成框架,是一个用来生成 .java源文件的Java API。
ARouter源码主要组成部分:
- annotation: 定义路由表的结构,ARouter路由框架所使用的全部注解,及其相关类。
- compiler:创建路由表,注解编译处理器,引入“arouter-annotation”,在编译期把注解标注的相关目标类生成映射文件。
- api: 在运行期加载逻辑构建路由表,并实现路由控制。
源码分析
init
从ARouter的初始化出发开始进入源码:
fun initARouter() {
//配置在DEBUG模式下,打印ARouter的日志
if (BuildConfig.DEBUG) {
ARouter.openLog()
ARouter.openDebug()
}
ARouter.init(this)
}
必须在使用ARouter之前调用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.");
}
}
变量hasInit用于保证初始化代码只执行一次。真正去实现初始化是调用了_ARouter.init方法,让_ARouter作实现类。ARouter作为暴露给用户调用的类,真正实现功能是_ARouter,将内部的功能包装在了_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;
// It's not a good idea.
// if (Build.VERSION.SDK_INT > Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// application.registerActivityLifecycleCallbacks(new AutowiredLifecycleCallback());
// }
return true;
}
调用了LogisticsCenter.init方法,传入线程池executor。
LogisticsCenter.init方法:
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
// These class was generate by arouter-compiler.
List classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
//
for (String className : classFileNames) {
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);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
if (Warehouse.groupsIndex.size() == 0) {
logger.error(TAG, "No mapping files were found, check your configuration please!");
}
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "LogisticsCenter has already been loaded, GroupIndex[%d], InterceptorIndex[%d], ProviderIndex[%d]", Warehouse.groupsIndex.size(), Warehouse.interceptorsIndex.size(), Warehouse.providersIndex.size()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
}
}
ClassUtils.getFileNameByPackageName方法做的就是找到app的dex,然后遍历出其中的属于com.alibaba.android.arouter.routes包下的所有类名,打包成集合返回。
拿到所有生成类名的集合后,通过反射实例化对象并调用方法,将注解的一些元素添加到static集合中:
class Warehouse {
// Cache route and metas
static Map> groupsIndex = new HashMap<>();
static Map routes = new HashMap<>();
// Cache provider
static Map providers = new HashMap<>();
static Map providersIndex = new HashMap<>();
// Cache interceptor
static Map> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
static List interceptors = new ArrayList<>();
static void clear() {
routes.clear();
groupsIndex.clear();
providers.clear();
providersIndex.clear();
interceptors.clear();
interceptorsIndex.clear();
}
}
看各个加载类的接口:
public interface IRouteRoot {
void loadInto(Map> routes);
}
public interface IInterceptorGroup {
void loadInto(Map> interceptor);
}
public interface IProviderGroup {
void loadInto(Map providers);
}
public interface IRouteGroup {
void loadInto(Map atlas);
}
IRouteRoot的实现将有@Route注解的module名添加到参数集合中,也就是groupsIndex。
IInterceptorGroup的实现将@Interceptor注解的类添加到参数集合中,也就是interceptorsIndex中。
IProviderGroup的实现将继承自IProvider的类添加到参数集合中,也就是providersIndex中。
init总结:
init过程就是把所有注解的信息加载内存中,并且完成所有拦截器的初始化。
getInstance()
通过new ARouter()创建或者获取ARouter单例。
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));
}
}
这里出现一个PathReplaceService,它是继承IProvider的接口,它是预留给用户实现路径动态变化功能。extractGroup方法截取路径中的第一段作为分组名。
build方法会创建并返回一个Postcard(明信片)对象,将path,group和bundle传入Postcard中。
protected Postcard build(String path, String group) {
if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
throw new HandlerException(Consts.TAG + "Parameter is invalid!");
} else {
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
path = pService.forString(path);
}
return new Postcard(path, group);
}
}
Postcard类部分代码:
public final class Postcard extends RouteMeta {
// Base
private Uri uri;
private Object tag; // A tag prepare for some thing wrong.
private Bundle mBundle; // Data to transform
private int flags = -1; // Flags of route
private int timeout = 300; // Navigation timeout, TimeUnit.Second
private IProvider provider; // It will be set value, if this postcard was provider.
private boolean greenChannel;
private SerializationService serializationService;
// Animation
private Bundle optionsCompat; // The transition animation of activity
private int enterAnim;
private int exitAnim;
}
navigation
跳转语句最后调用的是navigation方法,该方法来自于build返回的Postcard类型的类中。
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
最终调用的是_ARouter中的navigation方法:
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
try {
LogisticsCenter.completion(postcard);
} catch (NoRouteFoundException ex) {
}
}
completion方法中,使用postcard的path,去path与跳转目标的map中获取routeMeta对象,从而获取其中的destination。
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
Interceptor
拦截功能是通过ARouter提供的interceptorService实现的。
public interface InterceptorService extends IProvider {
void doInterceptions(Postcard postcard, InterceptorCallback callback);
}
在_ARouter的navigation跳转之前使用doInterceptions去做拦截处理:
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);
}
}
}
最终调用_navigation方法进行跳转或者获取实例:
private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
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 (-1 != flags) {
intent.setFlags(flags);
} else if (!(currentContext instanceof Activity)) { // Non activity, need less one flag.
intent.setFlags(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()));
}
}
return null;
}
postCard封装了跳转的destination目标,如果postCard的type为activity,则创建intent,将postCard中的bundle传入intent的extras,调用startActivity进行跳转。如果type为fragment,则通过postCard拿到目标fragment类,通过反射获取该Fragment实例。
参考文章
可能是最详细的ARouter源码分析
ARouter源码解析
问题:
-
简要描述如何自己写ARouter路由框架
1.先从注解模块开始,我们先定义注册路由地址的注解
2.继承AbstractProcesser自定义注解处理器解析注解。
3.根据拿到的注解外部类的信息编译时自动生成代码,代码的作用是将注解的外部类的路由信息跟路径保存在一个map中。
4.跳转时通过传入的path路径根据map中匹配到的Activity或Fragment进行跳转。
-
ARouter将path和组件怎么绑定起来的?存储在什么容器里?
在组件上声明注解加入path,会在ARouter调用init初始化时,通过注解处理器,在build文件夹中自动生成绑定path和组件的类,ARouter每个组都会生成一个文件。
在该组的文件中,会将该组的各个path,对应的Activity.class/Fragment.class进行绑定,在map
public class ARouter$$Group$$order implements IRouteGroup {
@Override
public void loadInto(Map atlas) {
atlas.put("/order/AuctionBids", RouteMeta.build(RouteType.ACTIVITY, AuctionBidRecordActivity.class, "/order/auctionbids", "iorder", new java.util.HashMap(){{put("regionCode", 8); put("id", 8); put("status", 8); }}, -1, 1));
atlas.put("/order/AuctionDetail", RouteMeta.build(RouteType.ACTIVITY, AuctionDetailActivity.class, "/order/auctiondetail", "iorder", new java.util.HashMap(){{put("regionCode", 8); put("data", 11); put("id", 8); }}, -1, 1));
atlas.put("/order/Cooperation", RouteMeta.build(RouteType.ACTIVITY, CooperationActivity.class, "/order/cooperation", "iorder", null, -1, 1));
}
}