ARouter 源码解析:阿里推出的路由框架,面试安卓系统架构

本文详细解析了 ARouter 的初始化、Postcard 的创建、补全以及执行跳转的过程。ARouter 在初始化时,通过 ClassUtils.getFileNameByPackageName 解析指定包名下的 ClassName 并存入 SP。初始化 Warehouse 时,加载了 IRoutRoot、IInterceptorGroup 和 IProviderGroup 类的相关信息。Postcard 的创建涉及预处理、参数设置,最终通过 navigation 实现跳转。补全 Postcard 时,尝试从 Warehouse 获取 RouteMeta,找不到时则加载对应的 RouteGroup。执行跳转针对不同类型(如 Activity、Provider)进行相应操作,如启动 Activity 或返回 Provider 实例。
部署运行你感兴趣的模型镜像

ARouter 在使用前需要通过调用 Arouter.init 方法并传入 Application 进行初始化:

/**

  • 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,这个 _ARouter 类才是 ARouter 的核心类:

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

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {

mContext = context;

executor = tpe;

try {

long startInit = System.currentTimeMillis();

Set routerMap;

// 获取存储 ClassName 集合的 routerMap(debug 模式下每次都会拿最新的)

if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {

logger.info(TAG, “Run with debug mode or new install, rebuild router map.”);

// 根据指定的 packageName 获取 package 下的所有 ClassName

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();

}

} else {

logger.info(TAG, “Load router map from cache.”);

// release 模式下,已经缓存了 ClassName 列表

routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet()));

}

logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + “, cost " + (System.currentTimeMillis() - startInit) + " ms.”);

startInit = System.currentTimeMillis();

// 遍历 ClassName

for (String className : routerMap) {

if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {

// 发现是 Root,加载类构建对象后通过 loadInto 加载进 Warehouse.groupsIndex

((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);

} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {

// 发现是 Interceptor,加载类构建对象后通过 loadInto 加载进 Warehouse.interceptorsIndex

((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);

} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {

// 发现是 ProviderGroup,加载类构建对象后通过 loadInto 加载进 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() + “]”);

}

}

这里主要有如下几步:

  1. 获取 com.alibaba.android.arouter.routes 下存储 ClassName 的集合 routerMap

  2. 若为 debug 模式或之前没有解析过 routerMap,则通过 ClassUtils.getFileNameByPackageName 方法对指定 package 下的所有 ClassName 进行解析并存入 SP。

  3. 若并非 debug 模式,并且之前已经解析过,则直接从 SP 中取出。(debug 每次都需要更新,因为类会随着代码的修改而变动)

  4. 遍历 routerMap 中的 ClassName。

  5. 如果是 RouteRoot,则加载类构建对象后通过 loadInto 加载进 Warehouse.groupsIndex

  6. 如果是 InterceptorGroup,则加载类构建对象后通过 loadInto 加载进 Warehouse.interceptorsIndex

  7. 如果是 ProviderGroup,则加载类构建对象后通过 loadInto 加载进 Warehouse.providersIndex

解析 ClassName

我们先看看 ClassUtils.getFileNameByPackageName 是如何对指定 package 下的 ClassName 集合进行解析的:

public static Set getFileNameByPackageName(Context context, final String packageName) {

final Set classNames = new HashSet<>();

// 通过 getSourcePaths 方法获取 dex 文件 path 集合

List paths = getSourcePaths(context);

// 通过 CountDownLatch 对 path 的遍历处理进行控制

final CountDownLatch parserCtl = new CountDownLatch(paths.size());

// 遍历 path,通过 DefaultPoolExecutor 并发对 path 进行处理

for (final String path : paths) {

DefaultPoolExecutor.getInstance().execute(new Runnable() {

@Override

public void run() {

// 加载 path 对应的 dex 文件

DexFile dexfile = null;

try {

if (path.endsWith(EXTRACTED_SUFFIX)) {

// zip 结尾通过 DexFile.loadDex 进行加载

dexfile = DexFile.loadDex(path, path + “.tmp”, 0);

} else {

// 否则通过 new DexFile 加载

dexfile = new DexFile(path);

}

// 遍历 dex 中的 Entry

Enumeration dexEntries = dexfile.entries();

while (dexEntries.hasMoreElements()) {

// 如果是对应的 package 下的类,则添加其 className

String className = dexEntries.nextElement();

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();

}

}

});

}

// 所有 path 处理完成后,继续向下走

parserCtl.await();

Log.d(Consts.TAG, “Filter " + classNames.size() + " classes by packageName <” + packageName + “>”);

return classNames;

}

这里的步骤比较简单,主要是如下的步骤:

  1. 通过 getSourcePaths 方法获取 dex 文件的 path 集合。

  2. 创建了一个 CountDownLatch 控制 dex 文件的并行处理,以加快速度。

  3. 遍历 path 列表,通过 DefaultPoolExecutor 对 path 并行处理。

  4. 加载 path 对应的 dex 文件,并对其中的 Entry 进行遍历,若发现了对应 package 下的 ClassName,将其加入结果集合。

  5. 所有 dex 处理完成后,返回结果。

关于 getSourcePaths 如何获取到的 dex 集合这里就不纠结了,因为我们的关注点不在这里。

初始化 Warehouse

Warehouse 实际上就是仓库的意思,它存放了 ARouter 自动生成的类(RouteRootInterceptorGroupProviderGroup)的信息。

我们先看看 Warehouse 类究竟是怎样的:

class Warehouse {

// 保存 RouteGroup 对应的 class 以及 RouteMeta

static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();

static Map<String, RouteMeta> routes = new HashMap<>();

// 保存 Provider 以及 RouteMeta

static Map<Class, IProvider> providers = new HashMap<>();

static Map<String, RouteMeta> providersIndex = new HashMap<>();

// 保存 Interceptor 对应的 class 以及 Inteceptor

static Map<Integer, Class<? extends IInterceptor>> 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();

}

}

可以发现 Warehouse 就是一个纯粹用来存放信息的仓库类,它的数据的实际上是通过上面的几个自动生成的类在 loadInto 中对 Warehouse 主动填入数据实现的。

例如我们打开一个自动生成的 IRouteRoot 的实现类:

public class ARouter R o o t Root Roothomework implements IRouteRoot {

@Override

public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {

routes.put(“homework”, ARouter G r o u p Group Grouphomework.class);

}

}

可以看到,它在 groupsIndex 中对这个 RouteRoot 中的 IRouteGroup 进行了注册,也就是向 groupIndex 中注册了 Route Group 对应的 IRouteGroup 类。其他类也是一样,通过自动生成的代码将数据填入 Map 或 List 中。

可以发现,初始化过程主要完成了对自动生成的路由相关类 RouteRootInterceptorProviderGroup 的加载,对它们通过反射构造后将信息加载进了 Warehouse 类中。

路由跳转


Postcard 的创建

下面我们看看路由的跳转是如何实现的,我们先看到 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));

}

}

它首先通过 ARouter.navigation 获取到了 PathReplaceService,它需要用户进行实现,若没有实现会返回 null,若有实现则调用了它的 forString 方法传入了用户的 Route Path 进行路径的预处理。

最后转调到了 build(path, group)group 通过 extractGroup 得到:

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 {

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;

}

}

extractGroup 实际上就是对字符串处理,取出 Route Group 的名称部分。

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);

}

}

build(path, group) 方法同样也会尝试获取到 PathReplaceService 并对 path 进行预处理。之后通过 path 与 group 构建了一个 Postcard 类:

public Postcard(String path, String group) {

this(path, group, null, null);

}

public Postcard(String path, String group, Uri uri, Bundle bundle) {

setPath(path);

setGroup(group);

setUri(uri);

this.mBundle = (null == bundle ? new Bundle() : bundle);

}

这里最终调用到了 PostCard(path, group, uri, bundle),这里只是进行了一些参数的设置。

之后,如果我们调用 withIntwithDouble 等方法,就可以进行参数的设置。例如 withInt 方法:

public Postcard withInt(@Nullable String key, int value) {

mBundle.putInt(key, value);

return this;

}

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

它实际上就是在对 Bundle 中设置对应的 keyvalue

最后我们通过 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 void navigation(Activity mContext, int requestCode) {

navigation(mContext, requestCode, null);

}

public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {

ARouter.getInstance().navigation(mContext, this, requestCode, callback);

}

通过如上的 navigation 可以看到,实际上它们都是最终调用到 ARouter.navigation 方法,在没有传入 Context 时会使用 Application 初始化的 Context,并且可以通过 NavigationCallback 对 navigation 的过程进行监听。

public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {

return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);

}

ARouter 仍然只是将请求转发到了 _ARouter

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {

try {

// 通过 LogisticsCenter.completion 对 postcard 进行补全

LogisticsCenter.completion(postcard);

} catch (NoRouteFoundException ex) {

// …

}

if (null != callback) {

callback.onFound(postcard);

}

// 如果设置了 greenChannel,会跳过所有拦截器的执行

if (!postcard.isGreenChannel()) {

// 没有跳过拦截器,对 postcard 的所有拦截器进行执行

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);

}

return null;

}

上面的代码主要有以下步骤:

  1. 通过 LogisticsCenter.completion 对 postcard 进行补全。

  2. 如果 postcard 没有设置 greenChannel,则对 postcard 的拦截器进行执行,执行完成后调用 _navigation 方法真正实现跳转。

  3. 如果 postcard 设置了 greenChannel,则直接跳过所有拦截器,调用 _navigation 方法真正实现跳转。

Postcard 的补全

我们看看 LogisticsCenter.completion 是如何实现 postcard 的补全的:

public synchronized static void completion(Postcard postcard) {

if (null == postcard) {

throw new NoRouteFoundException(TAG + “No postcard!”);

}

// 通过 Warehouse.routes.get 尝试获取 RouteMeta

RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());

if (null == routeMeta) {

// 若 routeMeta 为 null,可能是并不存在,或是还没有加载进来

// 尝试获取 postcard 的 RouteGroup

Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup()); // Load route meta.

if (null == groupMeta) {

throw new NoRouteFoundException(TAG + “There is no route match the path [” + postcard.getPath() + “], in group [” + postcard.getGroup() + “]”);

} else {

// …

// 如果找到了对应的 RouteGroup,则将其加载进来并重新调用 completion 进行补全

IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();

iGroupInstance.loadInto(Warehouse.routes);

Warehouse.groupsIndex.remove(postcard.getGroup());

// …

completion(postcard); // Reload

}

} else {

// 如果找到了对应的 routeMeta,将它的信息设置进 postcard 中

postcard.setDestination(routeMeta.getDestination());

postcard.setType(routeMeta.getType());

postcard.setPriority(routeMeta.getPriority());

postcard.setExtra(routeMeta.getExtra());

Uri rawUri = postcard.getUri();

// 将 uri 中的参数设置进 bundle 中

if (null != rawUri) {

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());

}

// 对于 provider 和 fragment,进行特殊处理

switch (routeMeta.getType()) {

case PROVIDER:

// 如果是一个 provider,尝试从 Warehouse 中查找它的类并构造对象,然后将其设置到 provider

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) {

throw new HandlerException("Init provider failed! " + e.getMessage());

}

}

postcard.setProvider(instance);

// provider 和 fragment 都会跳过拦截器

postcard.greenChannel();

break;

case FRAGMENT:

// provider 和 fragment 都会跳过拦截器

postcard.greenChannel();

default:

break;

}

}

}

这个方法主要完成了对 postcard 的信息与 Warehouse 的信息进行结合,以补全 postcard 的信息,它的步骤如下:

  1. 通过 Warehouse.routes.get 根据 path 尝试获取 RouteMeta 对象。

  2. 若获取不到 RouteMeta 对象,可能是不存在或是还没有进行加载(第一次都未加载),尝试获取 RouteGroup 调用其 loadInto 方法将 RouteMeta 加载进 Warehouse,最后调用 completion 重新尝试补全 。

  3. 将 RouteMeta 的信息设置到 postcard 中,其中会将 rawUri 的参数设置进 Bundle

  4. 对于 Provider 和 Fragment 特殊处理,其中 Provider 会从 Warehouse 中加载并构造它的对象,然后设置到 postcardProvider 和 Fragment 都会跳过拦截器。

RouteGroup 的 loadInto 仍然是自动生成的,例如下面就是一些自动生成的代码:

public void loadInto(Map<String, RouteMeta> atlas) {

atlas.put("/homework/commit", RouteMeta.build(RouteType.ACTIVITY, HomeworkCommitActivity.class, “/homework/commit”, “homework”, null, -1, -2147483648));

// …

}

它包括了我们补全所需要的如 DestinationClasspath 等信息,在生成代码时自动根据注解进行生成。

执行跳转

我们看看 navigation 方法是如何实现的跳转:

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:

// 对 Activity,构造 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);

}

// 切换到主线程,根据是否需要 result 调用不同的 startActivity 方法

new Handler(Looper.getMainLooper()).post(new Runnable() {

@Override

public void run() {

if (requestCode > 0) { // Need start for result

ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());

} else {

ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());

}

if ((0 != postcard.getEnterAnim() || 0 != postcard.getExitAnim()) && currentContext instanceof Activity) { // Old version.

((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());

}

if (null != callback) { // Navigation over.

callback.onArrival(postcard);

}

}

});

break;

case PROVIDER:

// provider 直接返回对应的 provider

您可能感兴趣的与本文相关的镜像

Qwen3-8B

Qwen3-8B

文本生成
Qwen3

Qwen3 是 Qwen 系列中的最新一代大型语言模型,提供了一整套密集型和专家混合(MoE)模型。基于广泛的训练,Qwen3 在推理、指令执行、代理能力和多语言支持方面取得了突破性进展

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值