当前位置: 首页 > article >正文

Arouter详解・常见面试题

前言:ARouter是一个用于 Android App 进行组件化改造的路由框架 —— 支持模块间的路由、通信、解耦。

一、路由简介:

路由:就是通过互联的网络把信息从源地址传输到目的地址的活动。完成路由这个操作的实体设备就是 路由器(Router)。

二、原理解析:

2.1 使用步骤

使用ARouter在进行Activity跳转:初始化ARouter、添加注解@Route、发起路由跳转。

  • 步骤1:初始化ARouter
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化
  • 步骤2:注册Activity路由
// moduleA 在支持路由的页面上添加注解   路径注意至少需有两级,/xx/xx
@Route(path = "/test/activity1", name = "测试用 Activity")
public class Test1Activity extends BaseActivity {
    @Autowired
    int age = 10;
	protected void onCreate(Bundle savedInstanceState) {
		ARouter.getInstance().inject(this);
	}
}
  • 步骤3:通过path发起路由跳转
// moduleB 没有依赖 moduleA
ARouter.getInstance().build("/test/activity").navigation();

这样就使得 没有依赖moduleA的moduleB能跳转到moduleA的Activity了。
那么 ARouter 是如何做到只通过简单2步 就完成 解耦组件间的路由操作呢?我们通过源码一步步理解。

2.2 架构解析

ARouter的源码架构:
在这里插入图片描述

  • app:是ARouter提供的一个测试Demo
  • arouter-annotation:这个lib模块中声明了很多注解信息和一些枚举类
  • arouter-api:ARouter的核心api,转换过程的核心操作都在这个模块里面
  • arouter-compiler:APT处理器,自动生成路由表的过程就是在这里面实现的
  • arouter-gradle-plugin:这是一个编译期使用的Plugin插件,主要作用是用于编译器自动加载路由表,节省应用的启动时间。

基础概念:

(1)PostCard(明信片):ARouter就是使用PostCard这个类来存储寄件人和收件人信息的。

java
复制代码
public final class Postcard extends RouteMeta {
    // Base
    private Uri uri; //如果使用Uri方式发起luyou
    private Object tag;             // A tag prepare for some thing wrong. inner params, DO NOT USE!
    private Bundle mBundle;         // 需要传递的参数使用bundle存储
    private int flags = 0;         // 启动Activity的标志:如NEW_FALG
    private int timeout = 300;      // 路由超时
    private IProvider provider;     // 使用IProvider的方式跳转
    private boolean greenChannel;	//绿色通道,可以不经过拦截器
    private SerializationService serializationService; //序列化服务serializationService:需要传递Object自定义类型对象,就需要实现这个服务
    private Context context;        // May application or activity, check instance type before use it.
    private String action;			//Activity跳转的Action

    // Animation
    private Bundle optionsCompat;    // The transition animation of activity
    private int enterAnim = -1;
    private int exitAnim = -1;
	...
}

PostCard继承了RouteMeta

java
复制代码
public class RouteMeta {
    private RouteType type;         // 路由类型:如Activity,Fragment,Provider等
    private Element rawType;        // 路由原始类型,在编译时用来判断
    private Class<?> destination;   // 目的Class对象
    private String path;            // 路由注册的path
    private String group;           // 路由注册的group分组
    private int priority = -1;      // 路由执行优先级,priority越低,优先级越高,这个一般在拦截器中使用
    private int extra;              // Extra data
    private Map<String, Integer> paramsType;  //  参数类型,例如activity中使用@Autowired的参数类型
    private String name; //路由名字,用于生成javadoc

    private Map<String, Autowired> injectConfig;  // 参数配置(对应paramsType).

}

RouteMeta:主要存储的是一些目的对象的信息,这些对象是在路由注册的时候才会生成。

(2)Interceptor拦截器
ARouter中,所有的路由调用过程在到达目的地前都会先经过自定义的一系列拦截器,实现一些AOP切面编程。

java
复制代码
public interface IInterceptor extends IProvider {
    /**
     * The operation of this interceptor.
     *
     * @param postcard meta
     * @param callback cb
     */
    void process(Postcard postcard, InterceptorCallback callback);
}

IInterceptor是一个接口,继承了IProvider,所以其也是一个服务类型,只需要实现process方法就可以实现拦截操作。

(3)greenChannel:绿色通道
设置了绿色通道的跳转过程,可以不经过拦截器

(4)Warehouse:路由仓库
Warehouse意为仓库,用于存放被 @Route、@Interceptor标注的 路由相关的信息,也就是我们关注的destination等信息,它存着所有路由信息。

java
复制代码
class Warehouse {
    //所有IRouteGroup实现类的class对象,是在ARouter初始化中赋值,key是path第一级
    //(IRouteGroup实现类是编译时生成,代表一个组,即path第一级相同的所有路由,包括Activity和Provider服务)
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); 
    //所有路由元信息,是在completion中赋值,key是path
    //首次进行某个路由时就会加载整个group的路由,即IRouteGroup实现类中所有路由信息。包括Activity和Provider服务
    static Map<String, RouteMeta> routes = new HashMap<>();
    
    //所有服务provider实例,在completion中赋值,key是IProvider实现类的class
    static Map<Class, IProvider> providers = new HashMap<>();
    //所有provider服务的元信息(实现类的class对象),是在ARouter初始化中赋值,key是IProvider实现类的全类名。
    //主要用于使用IProvider实现类的class发起的获取服务的路由,例如ARouter.getInstance().navigation(HelloService.class)
    static Map<String, RouteMeta> providersIndex = new HashMap<>();
    
    //所有拦截器实现类的class对象,是在ARouter初始化时收集到,key是优先级
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");
    //所有拦截器实例,是在ARouter初始化完成后立即创建
    static List<IInterceptor> interceptors = new ArrayList<>();
...
}

2.3 源码分析:

下面我们分别来分析使用过程:
步骤1分析:ARouter.init(this)

/**
 * 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方法:

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

内部初始化了一些mContext,mHandler以及字段信息 最重要的是LogisticsCenter.init(mContext, executor):进入看看:

/**
 * LogisticsCenter init, load all metas in memory. Demand initialization
 */
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
	
	try {
		//使用AGP插件进行路由表的自动加载
		loadRouterMap();
		//如果registerByPlugin被设置为true,说明使用的是插件加载,直接跳过
		if (registerByPlugin) {
			logger.info(TAG, "Load router map by arouter-auto-register plugin.");
		} else {
			//如果是false,则调用下面步骤加载
			Set<String> routerMap;

			// 如果是debug模式或者是新版本的,则每次都会去加载routerMap,这会是一个耗时操作
			if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
				logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
				// These class was generated by arouter-compiler.
				routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
				if (!routerMap.isEmpty()) {
					context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
				}

				PackageUtils.updateVersion(context);    // Save new version name when router map update finishes.
			} 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>()));
			}
			//这里循环获取routerMap中的信息
			for (String className : routerMap) {
				//如果className = "com.alibaba.android.arouter.routes.ARouter$$Root"格式,则将路由组信息添加到Warehouse.groupsIndex中
				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);
				//如果className = "com.alibaba.android.arouter.routes.ARouter$$Interceptors"格式,则将拦截器信息添加到Warehouse.interceptorsIndex中
				} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
					// Load interceptorMeta
					((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
				//如果className = "com.alibaba.android.arouter.routes.ARouter$$Providers"格式,则将服务Provider信息添加到Warehouse.providersIndex中
				} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
					// Load providerIndex
					((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
				}
			}
		}
	} catch (Exception e) {
		throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
	}
}

ARouter的init操作总结:

  • 1.优先使用插件加载路由表信息到仓库中,如果没有使用插件,则使用包名com.alibaba.android.arouter.routes去dex文件中查找对应的类对象 查找到后,保存到sp文件中,非debug或者新版本的情况下,下次就直接使用sp文件中缓存的类信息即可。

  • 2.查找到对应的类文件后,使用反射调用对应的类的loadInto方法,将路由组,拦截器以及服务Provider信息加载到Warehouse仓库中

步骤2分析:注册Activity路由
我们注册的Activity,Provider等路由信息,在编译期被注解处理器处理生成对应的路由表:路由表在ARouter初始化的时候被加载到Warehouse中;

步骤3分析:通过path启动对应的Activity

ARouter.getInstance().build("/test/activity2").navigation();
  • getInstance(): 做了init检查并创建了一个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():这里创建了一个Postcard,传入path和group;
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), true);
	}
}
1.使用PathReplaceService,可以替换原path为新的path
继续看build方法:
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);
	}
}
  • navigation():执行_ARouter中的同名navigation方法,内容如下
    1.预处理服务
    2.完善PostCard信息
    3.如果是非绿色通道,则使用拦截器处理请求
    4.调用_navigation()处理

直接看 _navigation() 方法:

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
	final Context currentContext = postcard.getContext();

	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 (0 != flags) {
				intent.setFlags(flags);
			}

			// Non activity, need FLAG_ACTIVITY_NEW_TASK
			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.
			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来处理不同的请求了

  • 1.Activity,直接跳转
  • 2.Fragment,Provider,BroadcaseReceiver和ContentProvider,直接返回类的实例对象。

inject简介
通过@Autowired注解的属性,通过调用ARouter.getInstance().inject(this);可以实现自动注入。我们知道原生的Activity传递数据是通过Bundle携带的。因此,ARouter的数据传递肯定也是基于Bundle,并实现了自动赋值的功能。

//生成类的类名 = 目标类名+ "$$ARouter$$Autowired"(固定后缀)
public class BlankFragment$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    //强转成目标类
    BlankFragment substitute = (BlankFragment)target;
    //给每个注解了@Autowired的属性赋值
    //这里的key为属性名或者注解中给定的name
    substitute.name = substitute.getArguments().getString("name");
    // 如果存在Bundle无法携带的类型,则会通过SerializationService序列化成json的String传递,SerializationService没有提供默认实现,需要用户自己实现
    if (null != serializationService) {
      substitute.obj = serializationService.parseObject(substitute.getArguments().getString("obj"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestObj>(){}.getType());
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'BlankFragment' , then you should implement 'SerializationService' to support object auto inject!");
    }
    if (null == substitute.obj) {
      Log.e("ARouter::", "The field 'obj' is null, in class '" + BlankFragment.class.getName() + "!");
    }
  }
}

前面分析navigation的时候我们看到了将Uri参数存入Bundle的过程(或者由用户手动调用withXX存入bundle),此处则是将Bundle中的数据取出,并赋值给@Autowired注解的属性。

Arouter流程图简介
在这里插入图片描述

三、Arouter常见面试问题:

‌3.1 使用方法‌

在Android开发中,使用ARouter进行应用内跳转非常简单。首先,需要在要跳转的Activity上添加@Route注解。其次,在应用启动时初始化ARouter,通常是在Application的onCreate方法中完成。初始化时,可以根据需要开启日志和调试模式。最后,通过ARouter的getInstance().build(path).navigation()方法进行跳转,其中path是目标Activity的路由地址‌1。

3.2 简述原理‌

ARouter的原理主要包括两个步骤:注册子模块信息到路由表和寻址操作。

  • ‌注册子模块信息到路由表‌:这一步并不是手动完成的,而是采用编译器APT技术,在编译的时候扫描自定义注解,通过注解获取子模块信息,并自动注册到路由表中去‌。
  • 寻址操作‌:在需要跳转的时候,ARouter会根据提供的路由地址,在路由表中寻找到对应的子模块信息,并完成交互,实现跳转‌。

3.3、ARouter 通信示意图

3.4、ARouter 的底层实现原理

ARouter的底层实现原理主要包括:通过编译时注解和注解处理器APT生成路由表,记录页面与URL的映射关系;应用启动时,通过反射加载路由表到内存;当需要路由跳转时,根据URL在路由表中查找对应的页面信息,创建Intent并设置参数,最后通过startActivity实现页面跳转。这种方式实现了模块间的解耦,使得不同模块间可以相互独立、互不依赖地进行通信和跳转。


http://www.kler.cn/a/521338.html

相关文章:

  • deepseek各个版本及论文
  • WPS数据分析000007
  • ArcGIS安装动物家域分析插件HRT的方法
  • 为AI聊天工具添加一个知识系统 之72 详细设计之13 图灵机
  • Level DB --- TableBuilder
  • C 或 C++ 中用于表示常量的后缀:1ULL
  • C++从入门到实战(二)C++命名空间
  • 【信息系统项目管理师-选择真题】2016上半年综合知识答案和详解
  • 第三十一周学习周报
  • 计算机图形学试题整理(期末复习/闭or开卷/>100道试题/知识点)
  • 塔罗牌(基础):大阿卡那牌
  • 2025美赛数学建模C题:奥运金牌榜,完整论文代码模型目前已经更新
  • 用C++编写一个2048的小游戏
  • 【2024年华为OD机试】(A卷,100分)- 识图谱新词挖掘 (JavaScriptJava PythonC/C++)
  • python的设计模式
  • 【miniconda】:langraph的windows构建
  • windows蓝牙驱动开发-生成和发送蓝牙请求块 (BRB)
  • 10 外观(Facade)模式
  • 基于先验领域知识的归纳式多实例多标签学习用于牙周病分类| 文献速递 -医学影像人工智能进展
  • 【Django DRF Apps】【文件上传】【断点上传】从零搭建一个普通文件上传,断点续传的App应用