【Android】ARouter源码解析

本篇文章主要讲解了 ARouter 框架的源码分析,包括其初始化过程、核心方法等。

初始化

在使用ARouter的时候我们都会先进行初始化:

java 复制代码
ARouter.init(this);

我们看下 init() 源码:

java 复制代码
public static void init(Application application) {
    // 检查 ARouter 是否已经初始化,避免重复初始化
    if (!hasInit) {
        // 获取 logger,并记录初始化开始的日志
        logger = _ARouter.logger;
        _ARouter.logger.info(Consts.TAG, "ARouter init start.");
        
        // 执行 ARouter 的初始化方法,并将初始化结果赋值给 hasInit
        hasInit = _ARouter.init(application);

        // 如果初始化成功,执行后续的初始化步骤
        if (hasInit) {
            _ARouter.afterInit();
        }

        // 记录初始化完成的日志
        _ARouter.logger.info(Consts.TAG, "ARouter init over.");
    }
}

真正调用的还是里面的 _ARouter.init()

java 复制代码
protected static synchronized boolean init(Application application) {
    // 将传入的 Application 实例赋值给 mContext
    mContext = application;
    // 初始化 LogisticsCenter,通常是用来管理路由路径和目标组件的调度
    LogisticsCenter.init(mContext, executor);
    // 记录初始化成功的日志
    logger.info(Consts.TAG, "ARouter init success!");
    // 设置初始化标志,标记 ARouter 已初始化
    hasInit = true;
    // 创建一个 Handler 用于在主线程中处理消息
    mHandler = new Handler(Looper.getMainLooper());
    // 返回初始化成功
    return true;
}

最终调用了LogisticsCenter的 init() 方法:

java 复制代码
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    // 保存上下文和线程池实例
    mContext = context;
    executor = tpe;

    try {
        // 记录初始化开始时间
        long startInit = System.currentTimeMillis();
        
        // 加载路由映射
        loadRouterMap();

        // 判断是否使用插件进行自动注册
        if (registerByPlugin) {
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            Set<String> routerMap;

            // 如果是调试模式或者是新版本安装,则重新构建路由映射
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                
                // 获取路由类文件名(这些类是由 arouter-compiler 插件生成的)
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                
                // 如果有新的路由映射,保存到 SharedPreferences 中
                if (!routerMap.isEmpty()) {
                    context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE)
                           .edit()
                           .putStringSet(AROUTER_SP_KEY_MAP, routerMap)
                           .apply();
                }

                // 更新应用版本信息
                PackageUtils.updateVersion(context);
            } 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>()));
            }

            // 打印路由映射加载完成的日志
            logger.info(TAG, "Find router map finished, map size = " + routerMap.size() + ", cost " 
                              + (System.currentTimeMillis() - startInit) + " ms.");
            startInit = System.currentTimeMillis();

            // 遍历所有路由映射,判断并加载对应的组件
            for (String className : routerMap) {
                // 判断是否是根元素类,若是则加载到 groupsIndex
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                }
                // 判断是否是拦截器类,若是则加载到 interceptorsIndex
                else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                }
                // 判断是否是提供者类,若是则加载到 providersIndex
                else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    ((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) {
        // 捕获异常并抛出自定义的 HandlerException
        throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
    }
}

上面方法主要实现了:

  • 通过loadRouterMap方法判断是不是通过arouter-register自动加载路由表,如果是通过自动加载的则registerByPlugin=true,这里我们先不管通过arouter-register自动加载的方式
  • 紧接着通过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并返回。
  • 拿到了需要的class类后,放到sp里面,方便下次不去扫描apk拿class,更新版本号
  • 将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中
  • 将com.alibaba.android.arouter.routes.ARouter$$Interceptors前缀的class类放到Warehouse.interceptorsIndex中
  • 将com.alibaba.android.arouter.routes.ARouter$$Providers前缀的class类放到Warehouse.providersIndex中

_ARouter.afterInit

java 复制代码
static void afterInit() {
        // Trigger interceptor init, use byName.
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }

ARouter.build

下面我们来看ARouter的build方法:

java 复制代码
public Postcard build(String path) {
        return _ARouter.getInstance().build(path);
    }

返回的是Postcard类型的对象,我们看一下里面的 _ARouter.build()

java 复制代码
protected Postcard build(String path, String group, Boolean afterReplace) {
    // 检查路径(path)和分组(group)是否为空,如果为空则抛出异常
    if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        // 如果 afterReplace 为 false,则进行路径替换
        if (!afterReplace) {
            // 获取 PathReplaceService 服务实例,该服务用于路径的替换操作
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            
            // 如果 PathReplaceService 服务实例不为空,则执行路径替换操作
            if (null != pService) {
                path = pService.forString(path);  // 调用替换方法替换路径
            }
        }

        // 构建并返回一个 Postcard 对象,使用处理后的 path 和 group
        return new Postcard(path, group);
    }
}
  • build 方法 :根据传入的路径 path 和分组 group 创建一个 Postcard 对象。
  • 路径替换 :如果 afterReplacefalse,则调用 PathReplaceService 服务对路径进行替换。
  • 异常处理 :如果 pathgroup 为空,抛出自定义的 HandlerException 异常。
  1. afterReplacefalse :如果 afterReplace 参数为 false,则会进行路径替换。这意味着在构建 Postcard 对象之前,会通过 PathReplaceService 服务来替换路径。这个服务允许开发者自定义路径替换逻辑,例如,根据配置或环境变量动态调整路径。这样做的好处是可以灵活地适应不同的部署环境或业务需求,而无需硬编码路径。
  2. afterReplacetrue :如果 afterReplace 参数为 true,则不会进行路径替换。这意味着直接使用原始的路径来构建 Postcard 对象。这通常用于那些不需要动态路径替换的场景,或者路径已经在其他地方被处理过,不需要再次替换。

示例代码

假设你实现了一个 PathReplaceService,它的作用是根据不同的环境替换路径中的某些部分:

java 复制代码
public class PathReplaceServiceImpl implements PathReplaceService {
    @Override
    public String forString(String path) {
        // 假设在生产环境中替换 "/test/" 为 "/prod/"
        if (BuildConfig.DEBUG) {
            return path;  // 开发环境不做替换
        } else {
            return path.replace("/test/", "/prod/");  // 生产环境进行路径替换
        }
    }
}

然后在调用 ARouter 时使用这个服务:

java 复制代码
PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
if (null != pService) {
    path = pService.forString(path);  // 使用服务进行路径替换
}

Postcard类

Postcard 类是 ARouter 框架中的一个核心组件,它表示一个路由的实体,包含了路由的所有信息,比如目标路径、分组、传递的数据、动画效果、标志位等。

java 复制代码
public final class Postcard extends RouteMeta {
    // Base
    private Uri uri;
    private Object tag;             // A tag prepare for some thing wrong. inner params, DO NOT USE!
    private Bundle mBundle;         // Data to transform
    private int flags = 0;         // 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;
    private Context context;        // May application or activity, check instance type before use it.
    private String action;

Postcard 类继承自 RouteMeta,是路由的实际载体,包含导航需要的额外信息。

RouteMeta:

java 复制代码
public class RouteMeta {
    // 路由类型,指示目标是 Activity、Fragment 还是 Provider 等
    private RouteType type;  

    // 原始类型元素,表示目标路由的原始类型(如 Activity 或 Fragment)
    private Element rawType;  

    // 目标页面的 Class 类型,表示路由请求的具体目标类
    private Class<?> destination;  

    // 路由路径,唯一标识一个路由,如 "/app/MainActivity"
    private String path;  

    // 路由分组,常用于模块化开发,避免路径冲突
    private String group;  

    // 路由的优先级,用于在多个路由匹配时,确定优先使用哪个路由
    private int priority;  

    // 额外的信息,通常用于存放一些配置信息
    private int extra;  

    // 路由参数类型的映射,存储参数名与其类型的对应关系
    private Map<String, Integer> paramsType;  

    // 路由的名称,通常用于调试或日志中,便于标识路由
    private String name;  

    // 注入的配置,存储需要自动注入的字段信息
    private Map<String, Autowired> injectConfig;  
}

PostCard.navigation

navigation有很多重载的方法,最终都会走_Arouter.navigation

java 复制代码
/**
 * 导航到 postcard 中路径指定的路由。
 *
 * @param context 上下文,通常是 Activity 等。
 */
public Object navigation(Context context) {
    return navigation(context, null);
}

/**
 * 导航到 postcard 中路径指定的路由。
 *
 * @param context 上下文,通常是 Activity 等。
 * @param callback 导航回调接口。
 */
public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback);
}

/**
 * 导航到 postcard 中路径指定的路由。
 *
 * @param mContext 上下文,通常是 Activity 等。
 * @param requestCode `startActivityForResult` 的请求码。
 */
public void navigation(Activity mContext, int requestCode) {
    navigation(mContext, requestCode, null);
}

/**
 * 导航到 postcard 中路径指定的路由。
 *
 * @param mContext 上下文,通常是 Activity 等。
 * @param requestCode `startActivityForResult` 的请求码。
 * @param callback 导航回调接口。
 */
public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {
    ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}

这几个方法会调用ARouter.navigation:

java 复制代码
 public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {
        return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
    }

接着调用_Arouter.navigation:

java 复制代码
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    // 获取预处理服务实例
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);

    // 如果预处理服务存在并且返回的结果为 false,则导航失败,取消导航
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        // 预处理失败,导航被取消
        return null;
    }

    // 设置 postcard 的上下文,如果传入的 context 为 null,则使用 mContext
    postcard.setContext(null == context ? mContext : context);

    try {
        // 完成路由的物流处理
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());

        // 如果启用了调试模式,则显示友好的提示信息给用户
        if (debuggable()) {
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    // 弹出提示框,告知没有找到匹配的路由
                    Toast.makeText(mContext, "没有找到匹配的路由!\n" +
                            " 路径 = [" + postcard.getPath() + "]\n" +
                            " 分组 = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
            });
        }

        // 如果有回调函数,调用 onLost 方法
        if (null != callback) {
            callback.onLost(postcard);
        } else {
            // 如果没有回调,则使用全局的降级服务
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }

        return null;
    }

    // 如果有回调函数,调用 onFound 方法
    if (null != callback) {
        callback.onFound(postcard);
    }

    // 如果不是绿色通道,则执行拦截器,可能会导致 ANR 问题
    if (!postcard.isGreenChannel()) {
        // 执行拦截器
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
            /**
             * 继续处理
             *
             * @param postcard 路由元数据
             */
            @Override
            public void onContinue(Postcard postcard) {
                // 调用实际的导航方法
                _navigation(postcard, requestCode, callback);
            }

            /**
             * 中断处理,管道将在此方法被调用时销毁。
             *
             * @param exception 中断原因
             */
            @Override
            public void onInterrupt(Throwable exception) {
                // 如果有回调,调用 onInterrupt 方法
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }

                // 记录导航失败信息
                logger.info(Consts.TAG, "导航失败,因拦截器中止 : " + exception.getMessage());
            }
        });
    } else {
        // 如果是绿色通道,直接执行导航
        return _navigation(postcard, requestCode, callback);
    }

    return null;
}

最后会执行_navigation:

java 复制代码
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    // 获取当前的上下文
    final Context currentContext = postcard.getContext();

    // 根据 Postcard 的类型执行不同的导航操作
    switch (postcard.getType()) {
        case ACTIVITY:
            // 如果是 Activity 类型,构建 Intent 对象
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());  // 设置 extras 数据

            // 设置 flags
            int flags = postcard.getFlags();
            if (0 != flags) {
                intent.setFlags(flags);  // 如果有 flags,设置到 intent 中
            }

            // 如果当前上下文不是 Activity,需要设置 FLAG_ACTIVITY_NEW_TASK
            if (!(currentContext instanceof Activity)) {
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }

            // 设置 Action
            String action = postcard.getAction();
            if (!TextUtils.isEmpty(action)) {
                intent.setAction(action);  // 如果有 action,设置到 intent 中
            }

            // 在主线程中启动 Activity
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;

        case PROVIDER:
            // 如果是 Provider 类型,直接返回对应的 Provider
            return postcard.getProvider();

        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
            // 如果是 Broadcast、ContentProvider 或 Fragment 类型
            Class<?> fragmentMeta = postcard.getDestination();
            try {
                // 通过反射创建 Fragment 实例
                Object instance = fragmentMeta.getConstructor().newInstance();
                if (instance instanceof Fragment) {
                    ((Fragment) instance).setArguments(postcard.getExtras());  // 设置 extras 数据
                } else if (instance instanceof android.support.v4.app.Fragment) {
                    ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                }

                // 返回创建的 Fragment 实例
                return instance;
            } catch (Exception ex) {
                logger.error(Consts.TAG, "获取 Fragment 实例出错, " + TextUtils.formatStackTrace(ex.getStackTrace()));
            }

        case METHOD:
        case SERVICE:
        default:
            // 如果是 METHOD 或 SERVICE 类型,或者未知类型,返回 null
            return null;
    }

    return null;
}

参考:阿里ARouter全面全面全面解析(使用介绍+源码分析+设计思路)_arouter init-CSDN博客

已经到底啦!!

相关推荐
曲幽2 小时前
Termux里的二进制和脚本,到底怎么运行才不踩坑?Termux-service 保活妙招!
android·termux·nohup·services·wake-lock
plainGeekDev2 小时前
单例模式 → object 声明
android·java·kotlin
程序员陆业聪3 小时前
读者点单·03|Compose 与传统 View 混用的 12 个真实坑
android
程序员陆业聪3 小时前
读者点单·02|Android 启动优化实战:Trace 抓取→Application 编排→冷启动全流程拆解
android
Coffeeee3 小时前
帮你快速理解AI Agent之我想招个Android实习生
android·人工智能·agent
用户298698530143 小时前
Java 实现 Word 文档文本与图片提取的方法
java·后端
SimonKing4 小时前
铁子,IntelliJ IDEA 2026.1.3来了,升不升?
java·后端·程序员
恋猫de小郭4 小时前
苹果 AirPods 协议,Android 也可以使用完整版 AirPods 能力
android·前端·flutter
黄林晴5 小时前
告别无效重建:Gradle 9.6.0 解决 CI 构建缓存失效痛点告别无效重建:Gradle 9.6.0 解决 CI 建筑缓存失效痛点
android·gradle