Arouter是一款Alibaba出品的优秀的路由框架,它通过Apt技术生成保存路径(路由path)和被注解(@Router)的组件类的映射关系的类,利用这些保存了映射关系的类,Arouter根据用户的请求(Postcard)寻找到要跳转的目标地址(class),使用Intent跳转。
使用: 1 在gradle文件添加依赖,如果是java则添加
在公共的modlue加上
api 'com.alibaba:arouter-api:1.5.2'
在各个需跳转modlue里面加上:
如果是kotlin: kapt 'com.alibaba:arouter-compiler:1.5.2' android { defaultConfig { kapt { arguments { arg("AROUTER_MODULE_NAME", project.getName()) } } } }
然后在Application的onCreate里面初始化:
定义path,也就是凭证。
根据path启动对应的Activity,也就是获取。
其中Arouter.init(this)就是注入的过程,注入的key就是通过@Route(path)定义的path,然后就拿着path去调用navigation()来获取并启动对应的Activity了。
原理解析: 编译时干的事-生成中间代码。
APT: Annotation Processing Tool注解处理器,是一种处理注解的工具,它是用来在编译时扫描和处理注解的,注解处理器以Java代码(或者编译过的字节码)作为输入,生成JAVA文件作为输出。Arouter中通过注解生成相关路由信息类。
所有当我们在gradle中添加了Arouter的依赖后,那么在编译时就会在对应的module的
下生成com.alibaba.android.arouter.routes目录,Arouter生成的代码都在这里,比如:
这些是在login模块生成的build中生成的,也就是说这些代码对于app模块来说,是不可达的。
上述就是编译所做的事。下面来看运行时所做的一些工作。
运行时做的工作 现在我们已经在
可能as版本不一样生成的目录不一样。
下面开始来分析代码流程:
最终调用了这个方法,我们分析下:
/**
* LogisticsCenter init, load all metas in memory. Demand initialization
*/
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
mContext = context;
executor = tpe;
try {
long startInit = System.currentTimeMillis();
//load by plugin first
loadRouterMap();
if (registerByPlugin) {
logger.info(TAG, "Load router map by arouter-auto-register plugin.");
} else {
Set<String> routerMap;
// 如果是debugable或者更新了APP的版本
if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
// 这里会获取所有"com.alibaba.android.arouter.rotues"目录下的class文件
routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
if (!routerMap.isEmpty()) {//这里缓存到sp里面去,方便下次获取
context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
}
//将app的版本号缓存到sp,方便下次使用
PackageUtils.updateVersion(context); // Save new version name when router map update finishes.
} else {
logger.info(TAG, "Load router map from cache.");
//如果app版本号没更新,并且没有开启debug模式,则从缓存中取出之前缓存的所有class。
routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
}
logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " + (System.currentTimeMillis() - startInit) + " ms.");
startInit = System.currentTimeMillis();
//遍历刚刚拿到的所有类名,并且反射调用它们的loadInto()方法,那么login模块中的那些生成的类,它们的loadinto()就被调用了,并且注入到参数里面了。
for (String className : routerMap) {
//拼接的字符串其实就是"com.alibaba.android.arouter.routes.ARouter&&Root",这个就是编译时生成的那个"ARouter$$Root$$app"的前缀
if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
// This one of root elements, load root.
//这就调用了它的loadInto(map),也就是map.put("login", ARouter$$Group$$login.class);这个键值对注入了Warehouse.groupIndex里面。
((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex); //这个是"com.alibaba.android.arouter.routes.ARouter$$Interceptors"
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
// Load interceptorMeta
((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex); //这个是"com.alibaba.android.arouter.routes.ARouter$$Providers"
} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
// Load providerIndex
((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
}
}
}
logger.info(TAG, "Load root element finished, cost " + (System.currentTimeMillis() - startInit) + " ms.");
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() + "]");
}
}
上述一共做了几步操作: 1 获取所有"com.alibaba.android.arouter.routes"目录下的类名,这一步有个缓存操作。 2 遍历所有获取到的类名,然后调用它们的loadInto(map)方法。 3 调用loadInto(map)的结果就是以键值对的方式将它们存入Warehouse.groupsIndex里面。
下面来看下Warehouse的代码:
class Warehouse {
// Cache route and metas
//注释1
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();
}
}
上面注释1处:
正好相对应。
接下来我们来看下获取所有"com.alibaba.android.arouter.routes"目录下的class文件路径的逻辑。
/**
* 通过指定包名,扫描包下面包含的所有的ClassName
*
* @param context U know
* @param packageName 包名
* @return 所有class的集合
*/
//我们传进来的packageName是"com.alibaba.android.arouter.routes"
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {
final Set<String> classNames = new HashSet<>();
//获取所有dex文件的路径。下面会详细分析
List<String> paths = getSourcePaths(context);
final CountDownLatch parserCtl = new CountDownLatch(paths.size());
//遍历所有dex文件的路径
for (final String path : paths) {
DefaultPoolExecutor.getInstance().execute(new Runnable() {
@Override
public void run() {
DexFile dexfile = null;
try { //是否是".zip"文件
if (path.endsWith(EXTRACTED_SUFFIX)) {
//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache" //如果是.zip文件,就调用loadDex来加载
dexfile = DexFile.loadDex(path, path + ".tmp", 0);
} else {
//否则,就直接根据路径创建即可
dexfile = new DexFile(path);
}
//遍历dexfile下面的元素
Enumeration<String> dexEntries = dexfile.entries();
while (dexEntries.hasMoreElements()) {
String className = dexEntries.nextElement();
//如果是以"com.alibaba.android.arouter.routes"开头,就添加
if (className.startsWith(packageName)) {
classNames.add(className);
}
}
} catch (Throwable ignore) {
Log.e("ARouter", "Scan map file in dex files made error.", ignore);
} finally {
if (null != dexfile) {
try {
dexfile.close();
} catch (Throwable ignore) {
}
}
parserCtl.countDown();
}
}
});
}
parserCtl.await();
Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
上述代码:
1 获取所有Dex文件路径,并且遍历创建DexFile 2 遍历DexFile,并且将所有以"com.alibaba.android.arouter.routes"文件开头的添加,然后返回。
我们又知道,Arouter在编译时生成的文件都是以"com.alibaba.android.arouter.routes"为前缀的,所以这个函数的结果就是获取所有Arouter编译时生成的文件名。 然后我们来看,怎么获取所有DexFile文件路径:
/**
* get all the dex path
*
* @param context the application context
* @return all the dex path
* @throws PackageManager.NameNotFoundException
* @throws IOException
*/
public static List<String> getSourcePaths(Context context) throws PackageManager.NameNotFoundException, IOException {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
List<String> sourcePaths = new ArrayList<>();
//添加apk的默认路径,可以理解为apk文件的路径
sourcePaths.add(applicationInfo.sourceDir); //add the default apk path
//the prefix of extracted file, ie: test.classes
//EXTRACTED_NAME_EXT就是.classed,所以这个结果类似于"test.classes"
String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
// 如果VM已经支持了MultiDex,就不要去Secondary Folder加载 Classesx.zip了,那里已经么有了
// 通过是否存在sp中的multidex.version是不准确的,因为从低版本升级上来的用户,是包含这个sp配置的
if (!isVMMultidexCapable()) {
//the total dex numbers
//获取所有Dex文件的总数
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);
//遍历获取路径
for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
//for each dex file, ie: test.classes2.zip, test.classes3.zip...
//EXTRACTED_SUFFIX就是".zip",所以fileName就类似于test.classes2.zip,test.classes3.zip这样。
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile()) {
//添加路径
sourcePaths.add(extractedFile.getAbsolutePath());
//we ignore the verify zip part
} else {
throw new IOException("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
}
}
}
if (ARouter.debuggable()) { // Search instant run support only debuggable
sourcePaths.addAll(tryLoadInstantRunDexFile(applicationInfo));
}
return sourcePaths;
}
上述代码的逻辑就是获取APP对应的所有的Dex文件的路径,android代码打包出来就是一堆Dex文件,可以看成是.class文件的集合,我们写代码到打包的过程就是.java->.class->.dex这样的过程,现在我们需要.dex->.class这样获取我们需要的.class文件。
现在来总结一下运行时的做的一些事。
1 首先调用了Arouter.init(this)方法
2 接着调用了ClassUtils.getFileNameByPackageName()来获取所有"com.alibaba.android.arouter.routes"目录下的dex文件的路径
3 然后遍历这些dex文件获取所有的class文件的完整类名
4 然后遍历所有类名,获取指定前缀的类,通过反射调用它们的loadInto(map)方法,把参数都注入到Warehouse的成员变量里面了。
通俗来说就是app包名->获取.dex->获取.class->找指定前缀的.class->反射调用方法->存入Warehouse中,这个过程就是"注入"过程,Warehouse里面保存了需要的key和.class。接着来看获取的过程。
调用时候做的事
ARouter.getInstance().build(RoutePath.PATH_ACTIVITY_LOGIN).navigation();
下面来一步一步分析:
/**
* Extract the default group from path.
*/
private String extractGroup(String path) {
if (TextUtils.isEmpty(path) || !path.startsWith("/")) {
throw new HandlerException(Consts.TAG + "Extract the default group failed, the path must be start with '/' and contain more than 2 '/'!");
}
try {
//获取group名字,结果就是"login"
String defaultGroup = path.substring(1, path.indexOf("/", 1));
if (TextUtils.isEmpty(defaultGroup)) {
throw new HandlerException(Consts.TAG + "Extract the default group failed! There's nothing between 2 '/'!");
} else {
return defaultGroup;
}
} catch (Exception e) {
logger.warning(Consts.TAG, "Failed to extract default group! " + e.getMessage());
return null;
}
}
/**
* Build postcard by path and group
*/
//这里我们已经知道参数是("/login/LoginActivity","login")了
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);
}
}
//最后创建一个Postcard类。
return new Postcard(path, group);
}
}
public Postcard(String path, String group, Uri uri, Bundle bundle) {
//保存了path,就是"/login/LoginActivity"
setPath(path);
//保存了group,就是"login"
setGroup(group);
//uri是Null
setUri(uri);
//创建个Bundle()
this.mBundle = (null == bundle ? new Bundle() : bundle);
}
现在可以知道,Arouter.getInstance().build(path);最终是创建个Postcard,保存了path和group,然后我们看下Postcard和navigation()函数:
public Object navigation() {
return navigation(null);
}
public Object navigation(Context context) {
return navigation(context, null);
}
public Object navigation(Context context, NavigationCallback callback) {
return ARouter.getInstance().navigation(context, this, -1, callback);
}
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
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;
}
// Set context to postcard.
postcard.setContext(null == context ? mContext : context);
try {
//重要函数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();
}
});
}
if (null != callback) {
callback.onLost(postcard);
} else {
// No callback for this invoke, then we use the global degrade service.
DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
if (null != degradeService) {
degradeService.onLost(context, postcard);
}
}
return null;
}
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 {
//重要函数2
return _navigation(postcard, requestCode, callback);
}
return null;
}
重要函数1:
/**
* Completion the postcard by route metas
*
* @param postcard Incomplete postcard, should complete by this method.
*/
public synchronized static void completion(Postcard postcard) {
if (null == postcard) {
throw new NoRouteFoundException(TAG + "No postcard!");
}
//先从Warehouse.routes里面获取RouteMeta,此时我们知道是null的
RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
if (null == routeMeta) {
// Maybe its does't exist, or didn't load.
if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
} else {
// Load route and cache it into memory, then delete from metas.
try {
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
//重要函数:3
addRouteGroupDynamic(postcard.getGroup(), null);
if (ARouter.debuggable()) {
logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] has already been loaded, trigger by [%s]", postcard.getGroup(), postcard.getPath()));
}
} catch (Exception e) {
throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
}
//又调用了自己
completion(postcard); // Reload
}
} else {
//这里就是设置一些参数
//atlas.put("/login/LoginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/login/loginactivity", "login", null, -1, -2147483648));
}这里面的一些参数
//LoginActivity.class
postcard.setDestination(routeMeta.getDestination());
//RouteType.ACTIVITY
postcard.setType(routeMeta.getType());
//-1
postcard.setPriority(routeMeta.getPriority());
//-2147483648
postcard.setExtra(routeMeta.getExtra());
Uri rawUri = postcard.getUri();
if (null != rawUri) { // Try to set params into bundle.
Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
Map<String, Integer> paramsType = routeMeta.getParamsType();
if (MapUtils.isNotEmpty(paramsType)) {
// Set value by its type, just for params which annotation by @Param
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());
}
//这里就是根据类型执行对应的逻辑,我们类型是RouteType.ACTIVITY,下面没有。
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;
}
}
}
重要函数3:
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.
//反射调用loadInto方法
atlas.put("/login/LoginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/login/loginactivity", "login", null, -1, -2147483648));
} // 直接put了,key是"/login/LoginActivity",跟Postcard的path一样,这下就放在Warehouse.routes里面了,下次就能拿到了。
Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
Warehouse.groupsIndex.remove(groupName);
}
// cover old group.
if (null != group) {
group.loadInto(Warehouse.routes);
}
}
重要函数1执行了两次一共:
第一次:我们从Warehouse.groupIndex取出注入时保存的数据,然后loadInto()相关数据到Warehouse.routes里面去
第二次:就是给一些参数赋值,Destination和Type等等。
现在我们的Warehouse.routes有数据了,并且参数postcard有destination和type了,接着执行重要函数2:
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
final Context currentContext = postcard.getContext();
switch (postcard.getType()) {
case ACTIVITY: //这里就是我们的类型
// Build intent
//创建Intent,postcard.getDestination()就是LoginActivity.class
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
//当我们的context不是Activity类型时,添加一个Flag。
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.
//切换到UI线程去启动Activity。
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去执行对应逻辑,然后执行到了Activity分之,于是就取出destination,也就是LoginActivity.class来启动。
当我们通过path去navigation(),此path="/login/LoginActivity",此时会根据这个path构造一个postCard,其中group="login"(以"/"分割得到的),然后以group(也就是"login")从Warehouse的groupIndex里面获取值,就得到了"Arouterlogin.class"这个class对象,接着使用反射创建了一个实例并调用loadInto()函数,于是执行到了下面的loadInto方法。
public class ARouter$$Group$$login implements IRouteGroup {
@Override
public void loadInto(Map<String, RouteMeta> atlas) {
// "/login/LoginActivity"就是我们通过@Route指定的path,后面RouteMeta保存了要启动的组件类型,以及对应的.class文件
atlas.put("/login/LoginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/login/loginactivity", "login", null, -1, -2147483648));
}
}
此后,我们的Warehouse的routes里面就有值了。 接着根据postCard的类型去进行switch-case,当case到RouteType.ACTIVITY(还有其他类型PROVIDER)时,就进行Activity的启动,此时我们有了对应Activity的class。
这个接口是我们如果需要暴露给其他模块调用接口时候需要继承的。
总结:
1 在编译时通过Apt技术来给代码含有@Route(path)注解的类生成中间代码 2 Arouter.init(context)初始化时,就是进行注入操作,key就是path。 3 ARouter.getInstance().build(RoutePath.PATH_ACTIVITY_LOGIN).navigation();时候用path来进行获取操作,最终获取到启动的Activity的class对象。