【Android】ARouter的使用及源码解析

文章目录

  • 简介
    • 介绍
    • 作用
  • 原理
    • 关系
  • 使用
    • 添加依赖和配置
    • 初始化SDK
    • 添加注解在目标界面
    • 跳转界面不带参
    • 跳转界面含参
    • 处理返回结果
  • 源码
  • 基本流程
    • getInstance()
    • build()
    • navigation()
    • _navigation()
    • Warehouse
  • ARouter初始化
    • init
    • 帮助类
      • 根帮助类
      • 组帮助类
    • completion
  • 总结

简介

介绍

ARouter 是阿里巴巴开源的一款 Android 路由框架,专为组件化架构设计,用于模块之间的页面跳转和服务通信。

ARouter 是路由系统,给无依赖的双方提供通信和路由的能力

作用

  1. 页面路由(页面跳转)
    1. 通过简单的路由配置,实现模块间页面跳转,避免直接依赖具体类,降低耦合度。
    2. 支持跨模块的页面跳转,即使页面所属模块在不同的 APK 中。
  2. 服务发现与调用
    1. 提供服务注册与发现机制,可以实现模块间的接口调用。
    2. 通过依赖注入机制简化服务调用流程,提升开发效率。
  3. 动态传参
    1. 支持页面跳转时传递参数(基本类型、对象等)。
    2. 支持通过注解方式接收参数,省去解析逻辑。

原理

关系

img

  • 1.注册

B界面将类的信息,通过key-value的形式,注册到arouter中。

  • 2.查询

A界面将类信息与额外信息(传输参数、跳转动画等),通过key传递至arouter中,并查询对应需要跳转类的信息。

  • 3.结合

将A界面类信息、参数与B界面的类信息进行封装结合。

  • 4.跳转

将结合后的信息,使用startActivity实现跳转。

使用

添加依赖和配置

android {defaultConfig {...javaCompileOptions {annotationProcessorOptions {arguments = [AROUTER_MODULE_NAME: project.getName()]}}}
}dependencies {implementation 'com.alibaba:arouter-api:x.x.x'annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'...
}

gradle.properties 文件中添加如下行来启用 Jetifier:

主要用于自动将旧版的 Android 库(即基于 Support Library 的库)迁移为 AndroidX 兼容版本。

android.enableJetifier=true

初始化SDK

if (isDebug()) {           // 这两行必须写在init之前,否则这些配置在init过程中将无效ARouter.openLog();     // 打印日志ARouter.openDebug();   // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化

我们可以在Application中初始化

public class MyApplication extends Application {@Overridepublic void onCreate() {super.onCreate();// 仅在调试模式下开启日志和调试模式if(BuildConfig.DEBUG){ARouter.openLog();ARouter.openDebug();}ARouter.init(this);} 
}

添加注解在目标界面

// 在支持路由的页面上添加注解
// 路径注意至少需有两级,/xx/xx
@Route(path = "/test/second")
public class SecondActivity extends AppCompatActivity {

跳转界面不带参

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void startActivity(View view) {ARouter.getInstance().build("/test/SecondActivity").navigation();}
}

这里跳转放在onCreate里报错:可能是因为ARouter 未初始化完成,初始化放在onCrete里就好了

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 仅在调试模式下开启日志和调试模式if(BuildConfig.DEBUG){ARouter.openLog();ARouter.openDebug();}ARouter.init(getApplication());ARouter.getInstance().build("/test/SecondActivity").navigation();}
}

跳转界面含参

// 1
ARouter.getInstance().build("/test/SecondActivity").withString("key1", "123").withBoolean("key2", false).navigation();// 2
@Route(path = "/test/SecondActivity")
public class SecondActivity extends AppCompatActivity {public String key1;@Autowired(name = "key2")public boolean aBoolean;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_second);ARouter.getInstance().inject(this);Log.d("SecondActivityTag", key1 + " " + aBoolean);}
}

处理返回结果

如果需要在跳转到新页面并返回结果,可以使用 ARouter.getInstance().build() 方法构建路由请求时,调用 navigation() 方法的重载版本,传入一个 NavigationCallback 回调接口来处理返回结果

ARouter.getInstance().build("/main/selectActivity").navigation(this, new NavigationCallback() {@Overridepublic void onFound(Postcard postcard) {// 找到目标页面}@Overridepublic void onLost(Postcard postcard) {// 找不到目标页面}@Overridepublic void onArrival(Postcard postcard) {// 到达目标页面}@Overridepublic void onInterrupt(Postcard postcard) {// 路由被拦截}});

源码

基本流程

getInstance()

ARouter.getInstance().build("/test/SecondActivity").navigation();

双检锁,获取ARouter类的单例

public static ARouter getInstance() {if (!hasInit) {throw new InitException("ARouter::Init::Invoke init(context) first!");} else {if (instance == null) {synchronized (ARouter.class) {if (instance == null) {instance = new ARouter();}}}return instance;}
}

build()

这里使用了门面模式。外部调用者只需与 ARouter 交互,获取路由对象,不需要了解 _ARouter 内部如何管理单例、如何创建 Postcard 等。

build返回一个Postcard,

// ARouter.java
public Postcard build(String path) {return _ARouter.getInstance().build(path);
}
// _ARouter.java
protected Postcard build(String path) {if (TextUtils.isEmpty(path)) {throw new HandlerException(Consts.TAG + "Parameter is invalid!");} else {// 预处理替换: 有需求来对 path 做统一的预处理就实现 PathReplaceService PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {path = pService.forString(path);}// extractGroup()方法提取path的组名return build(path, extractGroup(path), true);}
}

这里返回new出的一个Postcard对象,包含他的path和组名

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);}}return new Postcard(path, group);}
}
  • Postcard 类

Postcard 类继承自 RouteMeta,是路由的实际载体,包含导航需要的额外信息。它不仅继承了 RouteMeta 的基本字段,还添加了与导航操作密切相关的属性。

public final class Postcard extends RouteMeta {private Uri uri;private Object tag;private Bundle mBundle;private int flags = 0; private int timeout = 300; private IProvider provider;     private boolean greenChannel;   private SerializationService serializationService;private Context context;       private String action;          
}

上文创建Postcard对象时会通过set方法设置到父类RouteMeta的属性中

image-20241121210159228

  • RouteMeta 类

RouteMeta 是一个数据模型类,用于描述路由的元信息。该类中的属性用于存储路由目标、路径、分组及相关的附加信息。

public class RouteMeta {private RouteType type;         // 路由类型 (枚举类型,标识不同的路由类型, 例如 ACTIVITY、SERVICE 等)private Element rawType;        // 原始类型 (可能是路由的注解元素,用于生成代码时保留元信息)private Class<?> destination;   // 路由目标 (路由的目标类,例如某个 Activity 或 Fragment)private String path;            // 路由路径 (唯一标识路由的字符串,例如 "/app/home")private String group;           // 路由分组 (用于管理路由分组,例如 "app"、"user")private int priority = -1;      // 路由优先级 (数值越小,优先级越高)private int extra;              // 额外信息 (存储扩展数据,通常用于业务需求)private Map<String, Integer> paramsType;  // 参数类型映射 (参数名与参数类型的映射,例如 {"id": String.class})private String name;            // 路由名称 (可选字段,用于描述路由)
}

navigation()

导航

image-20241121210654863

由上至下调用,然后调用到ARouter的navigation方法

image-20241121210929136

该方法完成了从调用导航到实际路由跳转的逻辑处理

//_ARouter.java
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {// 1.确保 Postcard 能绑定一个有效的 Contextpostcard.setContext(null == context ? mContext : context);try {// 2.根据路由路径完善 Postcard 的信息LogisticsCenter.completion(postcard);} catch (NoRouteFoundException ex) {logger.warning(Consts.TAG, ex.getMessage());// 调试功能if (debuggable()) {runInMainThread(new Runnable() {@Overridepublic void run() {Toast.makeText(mContext, "There's no route matched!\n" +" Path = [" + postcard.getPath() + "]\n" +" Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();}});}// 2.1 捕获到异常:Postcard 路径不存在// 存在回调接口,就通知调用 onLost() 方法。if (null != callback) {callback.onLost(postcard);} else {// 不存在回调接口,执行全局降级服务,调用onLost方法,当路由丢失时可以做一些事情。DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);if (null != degradeService) {degradeService.onLost(context, postcard);}}return null;}// 2.2 没捕获到异常,回调onFound方法if (null != callback) {callback.onFound(postcard);}// 3.1 不是绿色通道,拦截器进行处理// 拦截器:扩展,允许开发者在导航过程中插入自定义逻辑,比如权限检查、登录验证或其他操作。// onContinue():继续路由操作。// onInterrupt():终止路由操作,并通知回调接口 onInterrupt()。if (!postcard.isGreenChannel()) {  interceptorService.doInterceptions(postcard, new InterceptorCallback() {@Overridepublic void onContinue(Postcard postcard) {_navigation(postcard, requestCode, callback);}@Overridepublic void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());}});} else {// 如果 isGreenChannel() 返回 true,则跳过拦截器,直接导航。// 开发模式下跳过非必要检查,加速导航return _navigation(postcard, requestCode, callback);}return null;
}

总结:该方法完善了postcard的信息,路径验证,拦截器逻辑

最后调用的_navigation将执行跳转逻辑

_navigation()

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {final Context currentContext = postcard.getContext();// 根据路由类型switch (postcard.getType()) {// 1.跳转到活动case ACTIVITY:// 通过getDestination()方法拿到目标页面的 Classfinal Intent intent = new Intent(currentContext, postcard.getDestination());intent.putExtras(postcard.getExtras());// 设置标志位(可选)int flags = postcard.getFlags();if (0 != flags) {intent.setFlags(flags);}// 如果当前上下文不是一个 Activity,则添加 FLAG_ACTIVITY_NEW_TASK,确保能够从非 Activity 环境启动目标页面。if (!(currentContext instanceof Activity)) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}// 设置 ActionsString action = postcard.getAction();if (!TextUtils.isEmpty(action)) {intent.setAction(action);}//  在主线程启动 ActivityrunInMainThread(new Runnable() {@Overridepublic void run() {startActivity(requestCode, currentContext, intent, postcard, callback);}});break;// 想要获取的服务,即IProvider的实现类。case PROVIDER:return postcard.getProvider();// 下面三个都是通过反射创建实例case BOARDCAST:case CONTENT_PROVIDER:case FRAGMENT:// 通过getDestination得到目标类对象Class<?> fragmentMeta = postcard.getDestination();try {// 通过反射构造Object instance = fragmentMeta.getConstructor().newInstance();// 类型判断,适配不同类型的fragmentif (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;
}

Warehouse

我们在navigation方法中调用了LogisticsCenter.completion(postcard);,来完善了postcard的信息

completion里出现了Warehouse,我们看看是什么

image-20241121223437888

Warehouse

用于存储路由表和相关的映射数据结构。它是 ARouter 的“仓库”,负责管理路由信息的加载、存储和查询。

主要职责是作为数据中心,保存路由信息、拦截器、服务等的映射关系。它将这些信息加载到内存中,方便 ARouter 在运行时快速查找目标页面或功能。

// Warehouse.java
class Warehouse {static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); static Map<String, RouteMeta> routes = new HashMap<>();static Map<Class, IProvider> providers = new HashMap<>();static Map<String, RouteMeta> providersIndex = new HashMap<>();static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");static List<IInterceptor> interceptors = new ArrayList<>();static void clear() {routes.clear();groupsIndex.clear();providers.clear();providersIndex.clear();interceptors.clear();interceptorsIndex.clear();}
}

在 ARouter 中,路由表、拦截器表、服务表分别用来管理路由路径、拦截器以及服务的相关信息,从而实现模块化开发、路径导航、拦截器链、以及服务的统一管理。以下是关于各部分的详细分析:

  1. 路由表

groupsIndex

  • 存储路由组名具体路由组类的映射。通过这种分组方式,可以有效减少路由加载的时间和内存占用。

  • public static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    
    • Key:路由组名(如 "user"),表示属于哪个功能模块。
    • Value:实现了 IRouteGroup 接口的类,用于提供具体的路由信息。
  • 当访问一个路由路径时,ARouter 首先查找该路径所属的组(groupsIndex 中的 Key)。

    通过路由组类(Value)加载组内所有路由信息到 routes

routes

  • 存储完整路由路径到**路由元信息(RouteMeta)**的映射。

  • public static Map<String, RouteMeta> routes = new HashMap<>();
    
    • Key:完整路径(如 /user/profile)。
    • ValueRouteMeta 对象,包含目标页面类、参数、类型等信息。
  • groupsIndex 确定路由组后,通过组加载具体的路由信息到 routes 中。

    最终通过路径快速定位到 RouteMeta

  1. 拦截器表

interceptorsIndex

  • 管理拦截器的优先级和对应的实现类,方便构建拦截器链。

  • public static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new HashMap<>();
    
    • Key:拦截器优先级(Priority),数字越小优先级越高。
    • Value:拦截器类(IInterceptor 的实现类)。
  • 允许开发者在路由跳转前拦截请求、检查参数、处理权限等。

interceptors

  • 缓存所有拦截器的实例。

  • static List<IInterceptor> interceptors = new ArrayList<>();
    
  • 路由跳转时,从 interceptors 依次执行所有拦截器。

  • 开发者可以实现拦截器逻辑,比如登录验证、动态权限申请等。

  1. 服务表

providers

  • 缓存服务实例(IProvider),用于提供功能模块的具体实现。

  • static Map<Class, IProvider> providers = new HashMap<>();
    
    • Key:服务接口的 Class 对象。
    • Value:服务接口的具体实现实例。
  • 在应用中实现全局服务调用。

    例如:网络管理器、日志管理器、用户管理等。

providersIndex

  • 存储服务实现类的元信息,用于动态加载服务。

  • public static Map<String, RouteMeta> providersIndex = new HashMap<>();
    
    • Key:服务类的全类名。
    • ValueRouteMeta 对象,包含服务实现类的信息。
  • 路由初始化时,将服务实现类的信息加载到 providersIndex

    在需要服务时,通过 providersIndex 获取元信息并实例化服务。

Warehouse 的数据是在 LogisticsCenter 类的 init() 方法中被加载的

ARouter初始化

init

ARouter.init()出发

// ARouter.java
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.java
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.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {// 保存上下文和线程池引用。mContext = context; executor = tpe;try {long startInit = System.currentTimeMillis(); // 尝试使用AGP加载路由表loadRouterMap(); if (registerByPlugin) {logger.info(TAG, "Load router map by arouter-auto-register plugin.");} else {Set<String> routerMap; // 路由表集合// 1. 如果是调试模式或是新安装应用,则重建路由表if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {logger.info(TAG, "Run with debug mode or new install, rebuild router map.");// 获取指定包名下的所有类名(编译时生成的所有帮助类)// ROUTE_ROOT_PAKCAGE: "com.alibaba.android.arouter.routes"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 {// 2. 从缓存中加载路由表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>()));}//遍历帮助类,区分是哪种帮助类,然后反射创建帮助类实例后,调用其loadInto方法来填充Warehouse相应的Mapfor (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {//类名开头:com.alibaba.android.arouter.routes.ARouter$$Root//填充Warehouse.groupsIndex,即所有IRouteGroup实现类的class对象((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//填充Warehouse.interceptorsIndex,即所有IInterceptor实现类的class对象((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//填充Warehouse.providersIndex,即所有provider的RouteMeta((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) {// 捕获异常并抛出 HandlerExceptionthrow new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");}
}

总结:

  1. 确定路由表的加载方式(插件自动注册(编译时)或手动扫描(运行时)。
  2. 判断是否需要重建路由表(调试模式或版本变化)。
  3. 加载路由表信息到内存(通过反射机制调用生成的类)。
  4. 提供异常处理和性能数据统计。

帮助类

根帮助类

路由组元信息的收集是通过根帮助类

用来实现对 WareHouse.groupsIndex 赋值

static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); 
  • 把path第一级相同的所以路由分到同一个组中。

key:组名

value:组帮助类

image-20241123202011347

这里loadInto方法的参数类型就是groupsIndex的类型

// LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {...for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {//类名开头:com.alibaba.android.arouter.routes.ARouter$$Root//填充Warehouse.groupsIndex,即所有IRouteGroup实现类的class对象((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);}...}
}

组帮助类

帮助WareHouse填充Warehouse.routes

 static Map<String, RouteMeta> routes = new HashMap<>();
package com.alibaba.android.arouter.routes;
public class ARouter$$Group$$test implements IRouteGroup {@Overridepublic void loadInto(Map<String, RouteMeta> atlas) {atlas.put("/test/SecondActivity", RouteMeta.build(RouteType.ACTIVITY, SecondActivity.class, "/test/secondactivity", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); put("key2", 0); }}, -1, -2147483648));}
}

可以看到每个路由的目标类class被放在了RouteMeta

completion

/*** 尝试完成 Postcard 的路由信息填充,包括加载动态分组路由和设置目标类。** @param postcard 路由信息的载体,目前只包含 path 和 group 属性*/
public synchronized static void completion(Postcard postcard) {// 检查 Postcard 是否为空if (null == postcard) {throw new NoRouteFoundException(TAG + "No postcard!");}// 尝试通过 path 获取路由元信息RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());if (null == routeMeta) {// 如果未获取到路由元信息,检查是否有该组的帮助类if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");} else {try {// 如果存在该组的帮助类,加载该组的所有路由if (ARouter.debuggable()) {logger.debug(TAG, String.format(Locale.getDefault(), "The group [%s] starts loading, trigger by [%s]", postcard.getGroup(), postcard.getPath()));}// 加载分组路由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 以重新获取路由元信息completion(postcard);return;}} else {// 如果获取到路由元信息,将其同步到 Postcard 中postcard.setDestination(routeMeta.getDestination());postcard.setType(routeMeta.getType());postcard.setPriority(routeMeta.getPriority());postcard.setExtra(routeMeta.getExtra());// 如果包含原始 URI,则解析参数并设置到 Postcard 中Uri rawUri = postcard.getUri();if (null != rawUri) {Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);Map<String, Integer> paramsType = routeMeta.getParamsType();if (MapUtils.isNotEmpty(paramsType)) {// 按类型设置参数值,仅处理使用 @Param 注解的参数for (Map.Entry<String, Integer> params : paramsType.entrySet()) {setValue(postcard, params.getValue(), params.getKey(), resultMap.get(params.getKey()));}// 保存需要自动注入的参数名postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));}// 保存原始 URIpostcard.withString(ARouter.RAW_URI, rawUri.toString());}// 根据路由类型进一步处理switch (routeMeta.getType()) {case PROVIDER: // 如果是服务类型的路由// 确认目标类实现了 IProvider 接口Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();IProvider instance = Warehouse.providers.get(providerMeta);if (null == instance) { // 如果服务实例尚未创建try {// 创建服务实例并初始化IProvider 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();       // 跳过拦截器break;case FRAGMENT: // 如果是 Fragment 类型的路由postcard.greenChannel();       // Fragment 也跳过拦截器default:break;}}
}

尝试获取路由元信息 → 检查并加载路由组 → 同步元信息 → 判断目标类并处理

总结

img

10018045-493c8e3b855f4332


参考:

  1. “终于懂了” 系列:组件化框架 ARouter 完全解析(一) 原理详解-腾讯云开发者社区-腾讯云 (tencent.com)
  2. ARouter源码解析(一)-腾讯云开发者社区-腾讯云 (tencent.com)
  3. ARouter/README_CN.md at master · alibaba/ARouter (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/62084.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

国内首家! 阿里云人工智能平台 PAI 通过 ITU 国际标准测评

近日&#xff0c;阿里云人工智能平台 PAI 顺利通过中国信通院组织的 ITU-T AICP-GA&#xff08;Technical Specification for Artificial Intelligence Cloud Platform&#xff1a;General Architecture&#xff09;国际标准和《智算工程平台能力要求》国内标准一致性测评&…

.NET9 - Swagger平替Scalar详解(四)

书接上回&#xff0c;上一章介绍了Swagger代替品Scalar&#xff0c;在使用中遇到不少问题&#xff0c;今天单独分享一下之前Swagger中常用的功能如何在Scalar中使用。 下面我们将围绕文档版本说明、接口分类、接口描述、参数描述、枚举类型、文件上传、JWT认证等方面详细讲解。…

【单点知识】基于PyTorch进行模型部署

文章目录 0. 前言1. 模型导出1.1 TorchScript1.1.1 使用 torch.jit.trace1.1.2 使用 torch.jit.script 1.2 ONNX1.2.1 导出为 ONNX 格式 1.3 导出后的模型加载1.3.1 加载 TorchScript 模型1.3.2 加载 ONNX 模型 2. 模型优化2.1 模型量化2.2 模型剪枝 3. 服务化部署3.1 Flask 部…

java基础知识(常用类)

目录 一、包装类(Wrapper) (1)包装类与基本数据的转换 (2)包装类与String类型的转换 (3)Integer类和Character类常用的方法 二、String类 (1)String类介绍 1)String 对象用于保存字符串,也就是一组字符序列 2)字符串常量对象是用双引号括起的字符序列。例如:&quo…

Servlet细节

目录 1 Servlet 是否符合线程安全&#xff1f; 2 Servlet对象的创建时间&#xff1f; 3 Servlet 绑定url 的写法 3.1 一个Servlet 可以绑定多个url 3.2 在web.xml 配置文件中 url-pattern写法 1 Servlet 是否符合线程安全&#xff1f; 答案&#xff1a;不安全 判断一个线程…

w~视觉~3D~合集3

我自己的原文哦~ https://blog.51cto.com/whaosoft/12538137 #SIF3D 通过两种创新的注意力机制——三元意图感知注意力&#xff08;TIA&#xff09;和场景语义一致性感知注意力&#xff08;SCA&#xff09;——来识别场景中的显著点云&#xff0c;并辅助运动轨迹和姿态的预测…

fastjson不出网打法—BCEL链

前言 众所周知fastjson公开的就三条链&#xff0c;一个是TemplatesImpl链&#xff0c;但是要求太苛刻了&#xff0c;JNDI的话需要服务器出网才行&#xff0c;BCEL链就是专门应对不出网的情况。 实验环境 fastjson1.2.4 jdk8u91 dbcp 9.0.20 什么是BCEL BCEL的全名应该是…

GitLab使用操作v1.0

1.前置条件 Gitlab 项目地址&#xff1a;http://******/req Gitlab账户信息&#xff1a;例如 001/******自己的分支名称&#xff1a;例如 001-master&#xff08;注&#xff1a;master只有项目创建者有权限更新&#xff0c;我们只能更新自己分支&#xff0c;然后创建合并请求&…

MATLAB GUI设计(基础)

一、目的和要求 1、熟悉和掌握MATLAB GUI的基本控件的使用及属性设置。 2、熟悉和掌握通过GUIDE创建MATLAB GUI的方法。 3、熟悉和掌握MATLAB GUI的菜单、对话框及文件管理框的设计。 4、熟悉和掌握MATLAB GUI的M文件编写。 5、了解通过程序创建MATLAB GUI的方法。 二、内…

RabbitMQ简单应用

概念 RabbitMQ 是一种流行的开源消息代理&#xff08;Message Broker&#xff09;软件&#xff0c;它实现了高级消息队列协议&#xff08;AMQP - Advanced Message Queuing Protocol&#xff09;。RabbitMQ 通过高效的消息传递机制&#xff0c;主要应用于分布式系统中解耦应用…

【es6】原生js在页面上画矩形及删除的实现方法

画一个矩形&#xff0c;可以选中高亮&#xff0c;删除自己效果的实现&#xff0c;后期会丰富下细节&#xff0c;拖动及拖动调整矩形大小 实现效果 代码实现 class Draw {constructor() {this.x 0this.y 0this.disX 0this.disY 0this.startX 0this.startY 0this.mouseDo…

【前端】JavaScript中的隐式声明及其不良影响分析

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 &#x1f4af;前言&#x1f4af;什么是隐式声明&#xff1f;&#x1f4af;隐式声明的常见情景1. 赋值给未声明的变量2. 非严格模式下的隐式声明3. 函数中的变量漏掉声明4. for 循环中的隐式声明5. 使用…

windows基础之病毒编写

声明&#xff01; 学习视频来自B站up主 泷羽sec 有兴趣的师傅可以关注一下&#xff0c;如涉及侵权马上删除文章&#xff0c;笔记只是方便各位师傅的学习和探讨&#xff0c;文章所提到的网站以及内容&#xff0c;只做学习交流&#xff0c;其他均与本人以及泷羽sec团队无关&#…

家校通小程序实战教程02口令管理

目录 1 创建数据源2 搭建后台功能3 生成口令4 调用API总结 我们的小程序上线之后&#xff0c;必然面临家长要加入的问题。微搭有登录验证的功能&#xff0c;但是手机验证的机制是&#xff0c;如果你未注册就给你自动注册一个账号&#xff0c;如果以注册了收到验证码就可以登录系…

Elasticsearch中的节点(比如共20个),其中的10个选了一个master,另外10个选了另一个master,怎么办?

大家好&#xff0c;我是锋哥。今天分享关于【Elasticsearch中的节点&#xff08;比如共20个&#xff09;&#xff0c;其中的10个选了一个master&#xff0c;另外10个选了另一个master&#xff0c;怎么办&#xff1f;】面试题。希望对大家有帮助&#xff1b; Elasticsearch中的节…

阿里发布 EchoMimicV2 :从数字脸扩展到数字人 可以通过图片+音频生成半身动画视频

EchoMimicV2 是由阿里蚂蚁集团推出的开源数字人项目&#xff0c;旨在生成高质量的数字人半身动画视频。以下是该项目的简介&#xff1a; 主要功能&#xff1a; 音频驱动的动画生成&#xff1a;EchoMimicV2 能够使用音频剪辑驱动人物的面部表情和身体动作&#xff0c;实现音频与…

【NLP高频面题 - 分布式训练】ZeRO1、ZeRO2、ZeRO3分别做了哪些优化?

【NLP高频面题 - 分布式训练】ZeRO1、ZeRO2、ZeRO3分别做了哪些优化&#xff1f; 重要性&#xff1a;★★ NLP Github 项目&#xff1a; NLP 项目实践&#xff1a;fasterai/nlp-project-practice 介绍&#xff1a;该仓库围绕着 NLP 任务模型的设计、训练、优化、部署和应用&am…

C#基础控制台程序

11.有一个54的矩阵&#xff0c;要求编程序求出其中值最大的那个元素的值&#xff0c;以及其所在的行号和列号。 12.从键盘输入一行字符&#xff0c;统计其中有多少个单词&#xff0c;单词之间用空格分隔开。 13.输入一个数&#xff0c;判断它是奇数还是偶数&#xff0c;如果…

三六零[601360]行情数据接口

1、三六零&#xff1a;实时行情 Restful API # 测试接口&#xff1a;可以复制到浏览器打开 https://tsanghi.com/api/fin/stock/XSHG/realtime?tokendemo&ticker601360获取股票实时行情&#xff08;开、高、低、收、量&#xff09;。 请求方式&#xff1a;GET。 Python示例…

eclipse-git项目提示NO-HEAD

1、出现该问题的过程 本人在用eclipse拉取git代码&#xff0c;刚拉取完&#xff0c;可能还没来得及跟本地的分支合并&#xff0c;电脑就卡动了。无奈只能重启电脑&#xff0c;打开eclipse&#xff0c;maven项目后面就出现了xxx NO-HEAD的提示。 2、问题解决 根据错误提示&am…