文章目录

  • 1、简介
  • 2、ARouter 配置与基本用法
    • 2.1 依赖引入与配置
    • 2.2 基本用法
  • 3、ARouter 编译时原理分析
  • 4、ARouter 源码解析
    • 4.1 ARouter 源码主要代码结构
    • 4.2 ARouter 初始化流程
    • 4.3 ARouter 路由跳转流程
    • 4.4 ARouter 参数的传入与获取流程
  • 5、不同类型的 IProvider 实现
    • 5.1 PathReplaceService 接口实现
    • 5.2 PretreatmentService 接口实现
    • 5.3 DegradeService 接口实现
  • 6、总结

1、简介

  • ARouter 是阿里巴巴开源的 Android 平台中对页面、服务提供路由功能的中间件,通俗来讲就是一个用于帮助 Android App 进行组件化改造的框架,并且支持模块间的路由、通信、解耦等等。
  • 官方网站

2、ARouter 配置与基本用法

2.1 依赖引入与配置
// 根目录的 build.gradle
buildscript {dependencies {classpath // ARouterclasspath "com.alibaba:arouter-register:1.0.2"}
}// 模块的 build.gradle
plugins {id 'kotlin-kapt'
}
kapt {arguments {// 根据模块名来命名路由根节点arg("AROUTER_MODULE_NAME", project.getName())// 生成 Json 文件arg("AROUTER_GENERATE_DOC", "enable")}
}
dependencies {// ARouterimplementation 'com.alibaba:arouter-api:1.5.2'// 注意这里的 arouter-compiler 不能使用 1.5.2 版本,不然会编译不过kapt 'com.alibaba:arouter-compiler:1.2.2'
}
2.2 基本用法
@Route(path = "/home/main", group = "home")
class HomeMainActivity : AppCompatActivity() {@Autowired@JvmFieldvar name: String? = null@Autowired@JvmFieldvar age: Int? = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_trade_detail)ARouter.getInstance().inject(this)}
}// 注入参数和服务
ARouter.getInstance().inject(this)// 构建标准的路由请求
ARouter.getInstance().build("/home/main").navigation();// 构建标准的路由请求,并指定分组
ARouter.getInstance().build("/home/main", "ap").navigation();// 构建标准的路由请求,通过 Uri 直接解析
Uri uri;
ARouter.getInstance().build(uri).navigation();// 构建标准的路由请求,startActivityForResult
// navigation的 第一个参数必须是 Activity,第二个参数则 是RequestCode
ARouter.getInstance().build("/home/main", "ap").navigation(this, 5);// 直接传递 Bundle
Bundle params = new Bundle();
ARouter.getInstance().build("/home/main").with(params).navigation();// 指定Flag
ARouter.getInstance().build("/home/main").withFlags();.navigation();// 获取 Fragment
Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();// 对象传递
ARouter.getInstance().withObject("key", new TestObj("Jack", "Rose")).navigation();// 觉得接口不够多,可以直接拿出 Bundle  赋值
ARouter.getInstance().build("/home/main").getExtra();// 转场动画(常规方式)
ARouter.getInstance().build("/test/activity2").withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom).navigation(this);// 转场动画(API16+)
ActivityOptionsCompat compat = ActivityOptionsCompat.makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0);// ps. makeSceneTransitionAnimation 使用共享元素的时候,需要在navigation方法中传入当前ActivityARouter.getInstance().build("/test/activity2").withOptionsCompat(compat).navigation();// 使用绿色通道(跳过所有的拦截器)
ARouter.getInstance().build("/home/main").greenChannel().navigation();// 使用自己的日志工具打印日志
ARouter.setLogger();// 使用自己提供的线程池
ARouter.setExecutor();
  • 更多使用介绍请到官网查看。

3、ARouter 编译时原理分析

4、ARouter 源码解析

4.1 ARouter 源码主要代码结构
  • arouter-api:上层主要代码,包括入口类 ARouter,主要逻辑代码类 LogisticsCenter,相关辅助类 ClassUtils等。
  • arouter-annotation:ARouter 中主要支持的 annotation(包括 Autowired、Route、Interceptor)的定义,以及 RouteMeta 等基础 model bean 的定义。
  • arouter-compiler:ARouter 中 annotation 对应的 annotation processor 代码,即注解处理器,让这些注解代码起到对应的作用,Arouter 中主要是生成相关代码。(关于 annotation processor,详细了解可参考 Java 注解处理器)
  • arouter-gradle-plugin:一个 gradle 插件,目的是在 ARouter 中插入相关注册代码。(代替在 Init 时扫描 dex 文件获取到所有 route 相关类)
  • arouter-idea-plugin: Arouter 的导航插件,帮助两个组件之间关联的代码进行导航。不过目前支持有限,还有一个开源的导航插件,感兴趣的同学可以了解下,ArouterHelperKotlin。
4.2 ARouter 初始化流程

  • 当我们在 Application 中调用 ARouter.init(this); 进行初始化时:
// ARouter.java
/*** init 初始化,必须在使用路由器之前调用*/
public static void init(Application application) {if (!hasInit) {logger = _ARouter.logger;_ARouter.logger.info(Consts.TAG, "ARouter init start.");// 调用 _ARouter 的 init() 方法hasInit = _ARouter.init(application);// 如果初始化完成后if (hasInit) {// 获取 InterceptorService 实例_ARouter.afterInit();}_ARouter.logger.info(Consts.TAG, "ARouter init over.");}
}
// _ARouter.java
protected static synchronized boolean init(Application application) {mContext = application;// 调用 LogisticsCenter 的 init() 方法LogisticsCenter.init(mContext, executor);logger.info(Consts.TAG, "ARouter init success!");hasInit = true;mHandler = new Handler(Looper.getMainLooper());return true;
}static void afterInit() {// 获取 InterceptorService 实例interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}
// LogisticsCenter.java
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {mContext = context;executor = tpe;try {long startInit = System.currentTimeMillis();// 调用 loadRouterMap() 方法loadRouterMap();if (registerByPlugin) {// 如果通过插件注入,就什么也不做logger.info(TAG, "Load router map by arouter-auto-register plugin.");} else {// 如果没有通过插件注入,则扫通过描 apk 中的 dex 文件来实现Set<String> routerMap;// 每次可调试时,它将重建路由器映射if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {logger.info(TAG, "Run with debug mode or new install, rebuild router map.");// 这些类是由路由器编译器生成的// 通过指定包名 com.alibaba.android.arouter.routes,扫描 dex 文件包下面包含的所有的 ClassNamerouterMap = 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);} 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();// 将所有的 Class 文件进行循环并进行分组,然后通过反射的方式构造 Root、Interceptors// 、Providers 文件的实例化对象,并将其添加到 Warehouse 仓库中对应的集合里for (String className : routerMap) {if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {// 如果 Class 文件是管理模块路由组的 Root 文件,则反射构造 Root 文件的实例化对象,// 并且通过 loadInto() 方法将其加入到 Warehouse.groupsIndex 这个 HashMap 集合中来。// 注意:这里只是把当前模块下路由组的信息加载到内存当中,但是这一个个路由组的实例化对象,并没有被创建,它们会在被需要的时候才会去实例化。((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {// 同上,加载 Interceptors 文件到 Warehouse.interceptorsIndex 这个 UniqueKeyTreeMap 集合中来。((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);} else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {// 同上,加载 Providers 文件到 Warehouse.providersIndex 这个 HashMap 集合中来。((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() + "]");}
}/*** arouter-auto-register 插件将在此方法中生成代码调用此方法* 来注册所有的 Routers、Interceptors、Providers*/
private static void loadRouterMap() {registerByPlugin = false;// 通过 gradle 插件自动生成注册码:arouter-auto-register// 如下所示:// api 生成的 arouter Group// register("com.alibaba.android.arouter.routes.ARouter$$Root$$arouterapi");// register("com.alibaba.android.arouter.routes.ARouter$$Providers$$arouterapi");// module Java 模块// register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulejava");// register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulejava");// register("com.alibaba.android.arouter.routes.ARouter$$Interceptors$$modulejava");// module kotlin 模块// register("com.alibaba.android.arouter.routes.ARouter$$Root$$modulekotlin");// register("com.alibaba.android.arouter.routes.ARouter$$Providers$$modulekotlin");
}/*** 根据 class name 来注册* 牺牲一点效率来解决主 dex 文件过大的问题*/
private static void register(String className) {// 判断类名是否是空if (!TextUtils.isEmpty(className)) {try {// 根据类名获取ClassClass<?> clazz = Class.forName(className);// 创建实例Object obj = clazz.getConstructor().newInstance();// 将不同的文件分别添加到 Warehouse 仓库对应的集合中去if (obj instanceof IRouteRoot) {registerRouteRoot((IRouteRoot) obj);} else if (obj instanceof IProviderGroup) {registerProvider((IProviderGroup) obj);} else if (obj instanceof IInterceptorGroup) {registerInterceptor((IInterceptorGroup) obj);} else {logger.info(TAG, "register failed, class name: " + className+ " should implements one of IRouteRoot/IProviderGroup/IInterceptorGroup.");}} catch (Exception e) {logger.error(TAG, "register class error:" + className, e);}}
}
// ClassUtils.java
/*** 通过指定包名,扫描包下面包含的所有的ClassName** @param context     U know* @param packageName 包名* @return 所有class的集合*/
public static Set<String> getFileNameByPackageName(Context context, final String packageName) throws PackageManager.NameNotFoundException, IOException, InterruptedException {final Set<String> classNames = new HashSet<>();
// 根据传入的 context 获取到当前应用所有的 dex 文件路径集合
List<String> paths = getSourcePaths(context);
// 加入了同步锁,保证 ARouter 先初始化完全
final CountDownLatch parserCtl = new CountDownLatch(paths.size());for (final String path : paths) {DefaultPoolExecutor.getInstance().execute(new Runnable() {@Overridepublic void run() {DexFile dexfile = null;try {if (path.endsWith(EXTRACTED_SUFFIX)) {//NOT use new DexFile(path), because it will throw "permission error in /data/dalvik-cache"dexfile = DexFile.loadDex(path, path + ".tmp", 0);} else {dexfile = new DexFile(path);}Enumeration<String> dexEntries = dexfile.entries();// 通过循环过滤出使用满足条件的类while (dexEntries.hasMoreElements()) {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();}}});
}parserCtl.await();Log.d(Consts.TAG, "Filter " + classNames.size() + " classes by packageName <" + packageName + ">");
return classNames;
}
// Warehouse.java
/*** Warehouse 数据存储仓库*/
class Warehouse {// 路由组集合,key 为组名,value 为路由组的 Class(HashMap)static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();// 路由组按需加载完成后,存放到路由集合中(HashMap)static Map<String, RouteMeta> routes = new HashMap<>();// 每个服务的原始信息加载完成后存放到这里(HashMap)static Map<String, RouteMeta> providersIndex = new HashMap<>();// 服务集合,key 为服务的 Class,value 为服务的实例,需要时才会创建实例(HashMap)static Map<Class, IProvider> providers = new HashMap<>();// 拦截器的集合 (TreeMap)static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");// 拦截器按需加载完成后,存放到该拦截器集合中(ArrayList)static List<IInterceptor> interceptors = new ArrayList<>();/*** 清除所有集合的数据*/static void clear() {routes.clear();groupsIndex.clear();providers.clear();providersIndex.clear();interceptors.clear();interceptorsIndex.clear();}
}
4.3 ARouter 路由跳转流程
  • 当我们调用ARouter.getInstance().build("/home/main").navigation();进行路由跳转时:
// ARouter.java
public Postcard build(String path) {return _ARouter.getInstance().build(path);
}@Deprecated
public Postcard build(String path, String group) {return _ARouter.getInstance().build(path, group, false);
}public Postcard build(Uri url) {return _ARouter.getInstance().build(url);
}
// _ARouter.java
/*** 根据 path 构建一个 Postcard 对象*/
protected Postcard build(String path) {if (TextUtils.isEmpty(path)) {throw new HandlerException(Consts.TAG + "Parameter is invalid!");} else {// 获取 PathReplaceService 服务,对外提供修改路由 path 和 uri(只需要重写该类)PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {path = pService.forString(path);}return build(path, extractGroup(path), true);}
}/*** 根据 uri 构建一个 Postcard 对象*/
protected Postcard build(Uri uri) {if (null == uri || TextUtils.isEmpty(uri.toString())) {throw new HandlerException(Consts.TAG + "Parameter invalid!");} else {// 获取 PathReplaceService 服务,对外提供修改路由 path 和 uri(只需要重写该类)PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {uri = pService.forUri(uri);}// 创建一个 Postcard 对象return new Postcard(uri.getPath(), extractGroup(uri.getPath()), uri, null);}
}/*** 根据 path 和 group 构建一个 Postcard 对象*/
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 服务,对外提供修改路由 path 和 uri(只需要重写该类)PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);if (null != pService) {path = pService.forString(path);}}// 创建一个 Postcard 对象return new Postcard(path, group);}
}/*** 根据路径获取 Group*/
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;}
}protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {// 获取 PretreatmentService 路由预处理服务,对外提供修改路由能力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 {// 执行 completion// 填充 postcardLogisticsCenter.completion(postcard);} catch (NoRouteFoundException ex) {logger.warning(Consts.TAG, ex.getMessage());if (debuggable()) {// Show friendly tips for user.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();}});}if (null != callback) {// 执行失败回调callback.onLost(postcard);} else {// No callback for this invoke, then we use the global degrade service.// 如果上一步发送错误,且没有 callback 回调// 获取 DegradeService 全局降级服务,对外提供修改路由能力,出错策略自定义的机会DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);if (null != degradeService) {degradeService.onLost(context, postcard);}}return null;}if (null != callback) {// 执行成功回调callback.onFound(postcard);}// Provider 和 Fragment 不走拦截器if (!postcard.isGreenChannel()) {// 必须在异步线程中运行,也许拦截器花费太多时间导致 ANR。// 执行拦截器的 doInterceptions() 方法interceptorService.doInterceptions(postcard, new InterceptorCallback() {/*** Continue process 没有中断的回调** @param postcard route meta*/@Overridepublic void onContinue(Postcard postcard) {// 如果没有中断最终将执行该方法_navigation(postcard, requestCode, callback);}/*** 中断的回调* Interrupt process, pipeline will be destory when this method called.** @param exception Reson of interrupt.*/@Overridepublic void onInterrupt(Throwable exception) {if (null != callback) {callback.onInterrupt(postcard);}logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());}});} else {return _navigation(postcard, requestCode, callback);}return null;
}
// LogisticsCenter.java
public synchronized static void completion(Postcard postcard) {if (null == postcard) {throw new NoRouteFoundException(TAG + "No postcard!");}// 根据路径查看当前已经按组加载过的 RouteMeta 是否在内存中存在,从而获取 RouteMetaRouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());if (null == routeMeta) {// 首次调用 routeMeta 为 null// 如果 map 中不包含 postcard 的 group 则抛出异常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()));}// 将属于这个 group 中的所有路由都添加到 map 中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);}} else {// 设置目标postcard.setDestination(routeMeta.getDestination());// 设置类型postcard.setType(routeMeta.getType());// 设置优先级postcard.setPriority(routeMeta.getPriority());// 设置参数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 @Paramfor (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 uripostcard.withString(ARouter.RAW_URI, rawUri.toString());}switch (routeMeta.getType()) {case PROVIDER:// 如果是 Provider 类型,应该找到它的实例// 如果是 Provider 必须实现 IProvider 接口// 创建 Provider 实例Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();// 从缓存中获取 ProviderIProvider instance = Warehouse.providers.get(providerMeta);if (null == instance) {IProvider provider;try {// 如果为空,通过反射的方式创建 IProviderprovider = 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 和 Fragment 不走拦截器break;case FRAGMENT:postcard.greenChannel();// 默认 Provider 和 Fragment 不走拦截器default:break;}}
}
// _ARouter.java
private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {final Context currentContext = postcard.getContext();switch (postcard.getType()) {case ACTIVITY:// 目标页是 Activity// Build intent 创建 Intentfinal Intent intent = new Intent(currentContext, postcard.getDestination());// 设置 Intent 参数intent.putExtras(postcard.getExtras());// 设置 Activity 的 flagsint flags = postcard.getFlags();if (0 != flags) {intent.setFlags(flags);}// 如果 currentContext 为 Application 的 Context,// 那么将当前 Activity 的 flags 设置为 FLAG_ACTIVITY_NEW_TASK,// 即新启动一个栈空间if (!(currentContext instanceof Activity)) {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);}// 设置 ActionsString action = postcard.getAction();if (!TextUtils.isEmpty(action)) {intent.setAction(action);}// 最终在主线程中调用 startActivitu() 方法runInMainThread(new Runnable() {@Overridepublic void run() {startActivity(requestCode, currentContext, intent, postcard, callback);}});break;case PROVIDER:// 目标页是 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;
}
4.4 ARouter 参数的传入与获取流程
  • (1)当我们调用withString("name", "张三")或者withXXX("xxx", xxx)方法进行参数传入时:
// Postcard.java
private Bundle mBundle;public void navigation(Activity mContext, int requestCode, NavigationCallback callback) {ARouter.getInstance().navigation(mContext, this, requestCode, callback);
}public Postcard withString(@Nullable String key, @Nullable String value) {mBundle.putString(key, value);return this;
}public Postcard withBoolean(@Nullable String key, boolean value) {mBundle.putBoolean(key, value);return this;
}
...
// ARouter.java
public Object navigation(Context mContext, Postcard postcard, int requestCode, NavigationCallback callback) {return _ARouter.getInstance().navigation(mContext, postcard, requestCode, callback);
}
// _ARouter.java
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {...try {LogisticsCenter.completion(postcard);} catch (NoRouteFoundException ex) {...
}
// LogisticsCenter.java
public synchronized static void completion(Postcard postcard) {...if (MapUtils.isNotEmpty(paramsType)) {// Set value by its type, just for params which annotation by @Paramfor (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[]{}));}...
}private static void setValue(Postcard postcard, Integer typeDef, String key, String value) {if (TextUtils.isEmpty(key) || TextUtils.isEmpty(value)) {return;}try {if (null != typeDef) {if (typeDef == TypeKind.BOOLEAN.ordinal()) {postcard.withBoolean(key, Boolean.parseBoolean(value));} else if (typeDef == TypeKind.BYTE.ordinal()) {postcard.withByte(key, Byte.parseByte(value));} else if (typeDef == TypeKind.SHORT.ordinal()) {postcard.withShort(key, Short.parseShort(value));} else if (typeDef == TypeKind.INT.ordinal()) {postcard.withInt(key, Integer.parseInt(value));} else if (typeDef == TypeKind.LONG.ordinal()) {postcard.withLong(key, Long.parseLong(value));} else if (typeDef == TypeKind.FLOAT.ordinal()) {postcard.withFloat(key, Float.parseFloat(value));} else if (typeDef == TypeKind.DOUBLE.ordinal()) {postcard.withDouble(key, Double.parseDouble(value));} else if (typeDef == TypeKind.STRING.ordinal()) {postcard.withString(key, value);} else if (typeDef == TypeKind.PARCELABLE.ordinal()) {// TODO : How to description parcelable value with string?} else if (typeDef == TypeKind.OBJECT.ordinal()) {postcard.withString(key, value);} else {    // Compatible compiler sdk 1.0.3, in that version, the string type = 18postcard.withString(key, value);}} else {postcard.withString(key, value);}} catch (Throwable ex) {logger.warning(Consts.TAG, "LogisticsCenter setValue failed! " + ex.getMessage());}
}
  • 其最终的参数是传递到了 Postcard 类中的 mBundle 中。
  • (2)当我们在ActivityonCreate()方法中调用ARouter.getInstance().inject(this)注入参数和服务,通过@Autowired标记注解@Autowired @JvmField var name: String? = null,通过上述两者进行参数获取时:
// _ARouter.java
static void inject(Object thiz) {// 获取 AutowiredServiceAutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());if (null != autowiredService) {autowiredService.autowire(thiz);}
}
// AutowiredServiceImpl.java@Override
public void autowire(Object instance) {doInject(instance, null);
}/*** 递归注入** @param instance who call me.* @param parent   parent of me.*/
private void doInject(Object instance, Class<?> parent) {Class<?> clazz = null == parent ? instance.getClass() : parent;ISyringe syringe = getSyringe(clazz);if (null != syringe) {// 调用辅助类的 inject 方法syringe.inject(instance);}// 获取父类Class<?> superClazz = clazz.getSuperclass();// 父类不为空,且不是 framework 的类if (null != superClazz && !superClazz.getName().startsWith("android")) {doInject(instance, superClazz);}
}/*** 获取注解生成的辅助类** @param clazz Class<?>* @return ISyringe*/
private ISyringe getSyringe(Class<?> clazz) {String className = clazz.getName();try {if (!blackList.contains(className)) {ISyringe syringeHelper = classCache.get(className);if (null == syringeHelper) {  // No cache.// 类名$$ARouter$$Autowired(例如:KotlinTestActivity$$ARouter$$Autowired),反射syringeHelper = (ISyringe) Class.forName(clazz.getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();}classCache.put(className, syringeHelper);return syringeHelper;}} catch (Exception e) {blackList.add(className);    // This instance need not autowired.}return null;
}
// 生成的 KotlinTestActivity 辅助类
public class KotlinTestActivity$$ARouter$$Autowired implements ISyringe {private SerializationService serializationService;@Overridepublic void inject(Object target) {serializationService = ARouter.getInstance().navigation(SerializationService.class);KotlinTestActivity substitute = (KotlinTestActivity)target;//调用 KotlinTestActivity 的 getIntent() 获取参数substitute.name = substitute.getIntent().getStringExtra("name");substitute.age = substitute.getIntent().getIntExtra("age", substitute.age);}
}

5、不同类型的 IProvider 实现

5.1 PathReplaceService 接口实现
/*** PathReplaceServiceImpl** @Description: 跳转前第一次预处理*               实现 PathReplaceService 接口,并必须加以 @Route 注解*               path 地址只要保证正常的格式,内容是什么好像无所谓*               一般我们会使用 @Route(path = "/pathReplace/service") 为默认地址*/
@Route(path = "/pathReplace/service")
class PathReplaceServiceImpl : PathReplaceService {private var context: Context? = nulloverride fun init(context: Context?) {this.context = context}override fun forString(path: String): String {Log.d("PathReplaceServiceImpl", "forString: $path")return path}override fun forUri(uri: Uri): Uri {Log.d("PathReplaceServiceImpl", "forUri: $uri")return uri}
}
5.2 PretreatmentService 接口实现
/*** PretreatmentServiceImpl** @Description: 跳转前第二次预处理*               实现 PretreatmentService 接口,并必须加以 @Route 注解*               path 地址只要保证正常的格式,内容是什么好像无所谓*               一般我们会使用 @Route(path = "/pretreatment/service") 为默认地址*/
@Route(path = "/pretreatment/service")
class PretreatmentServiceImpl : PretreatmentService {private var context: Context? = nulloverride fun init(context: Context?) {this.context = context}override fun onPretreatment(context: Context?, postcard: Postcard): Boolean {this.context = context// 跳转前预处理,如果需要自行跳转,该方法返回 false 即可Log.d("PretreatmentServiceImpl", "onPretreatment: $postcard")return true}
}
5.3 DegradeService 接口实现
/*** DegradeServiceImpl** @Description: 全局降级服务(当参数跳转错误时)*               path 地址只要保证正常的格式,内容是什么好像无所谓*               一般我们会使用 @Route(path = "/degradeService/service") 为默认地址*/
@Route(path = "/degradeService/service")
class DegradeServiceImpl : DegradeService {private var context: Context? = nulloverride fun init(context: Context?) {this.context = context}override fun onLost(context: Context?, postcard: Postcard?) {Log.d("DegradeServiceImpl", "onLost: $postcard")}
}

6、总结

  • 初始化:ARouter 的路由、参数和拦截器都是通过注解来标注的,然后通过注解生成器分别生成不同的路由表,当我们在 Application 调用 ARouter 的 init() 方法进行初始化时,最终会调用 LogisticsCenter 的 init(xxx) 方法,默认通过反射加载这些路由表信息,并最终归类到 Warehouse 这个类中的不同集合里面进行存储。
  • 路由跳转:当我们调用 ARouter 的 build() 方法后,首先会获取一个路径替换服务,从而对外提供对路径修改等操作,接着根据路径生成一个 Postcard 对象,然后在调用 Postcard 的 navigation() 方法时,最终调用 _ARouter 的 navigation() 方法,在这个方法中,首先会处理预处理服务,然后调用 LogisticsCenter 的 completion(xxx) 方法填充 Postcard 中的信息,如果 LogisticsCenter 没有找到对应的路由信息的话,就会走降级策越,如果找到对应的路由信息,就判断是不是走绿色通道,所谓的绿色通道就是 Provider 和 Fragment 不走拦截器的一个方法,如果不走绿色通道,就交给拦截器链决定是否跳转或如何跳转,如果走绿色通道,就直接按照 Activit 和 Fragment 等不同类型进行跳转或实例化返回。
  • 参数传入 当我们通过 withXxx(xxx) 方法进行参数传入的时,其实就是在 build() 出一个 Postcard 对象后,然后调用 LogisticsCenter 的 setValue() 方法将其需要传入的参数最终存入 Postcard 中的 Bundle 里面。
  • 参数获取 当我们在 Activity 中调用 ARouter 的 inject(this) 方法去注入参数和服务时,其实就是通过对 @Autowirte 注解标记的变量,让其丢给 AutowiredService 的 doInject() 方法进行递归注入,最终还是通过 Activity 的 getIntent() 去进行参数获取。
  • ARouter 加载路由表信息有两种方式,一种是运行时反射,另外一种是编译时插入,默认为运行时反射。

ARouter 源码解析(1.5.2 版本)相关推荐

  1. ARouter 源码解析(零) 基本使用

    ARouter 源码解析(零) 基本使用 零.要解决的问题 在app的开发中,页面之间的相互跳转是最基本常用的功能.在Android中的跳转一般通过显式intent和隐式intent两种方式实现的,而 ...

  2. Android项目解耦--路由框架ARouter源码解析

    前言 上一篇文章Android项目解耦–路由框架ARouter的使用讲述了ARouter在项目中的使用,这边文章主要对ARouter的源码进行学习和分析. ARouter的结构 ARouter主要由三 ...

  3. Arouter源码解析(二)——ASM和JavaPoet

    Arouter 在不使用gradle插件的情况下,是使用Apt+JavaPoet 来处理,其中调用java文件里面的方法则是用了反射的技术.这应该也是初始化时间长的问题:apt的autoService ...

  4. TreeMap源码解析

    1.TreeMap介绍 TreeMap是一个通过红黑树实现有序的key-value集合. TreeMap继承AbstractMap,也即实现了Map,它是一个Map集合 TreeMap实现了Navig ...

  5. Spark ALS recommendForAll源码解析实战之Spark1.x vs Spark2.x

    文章目录 Spark ALS recommendForAll源码解析实战 1. 软件版本: 2. 本文要解决的问题 3. 源码分析实战 3.1 Spark2.2.2 ALS recommendForA ...

  6. 路由框架ARouter最全源码解析

    ARouter是2017年阿里巴巴开源的一款Android路由框架,官方定义: ARouter是Android平台中对页面,服务提供路由功能的中间件,提倡简单且够用 有下面几个优势: 1.直接解析UR ...

  7. vue从哪看组件版本_VUE源码解析之路

    Vue 是一个 MVVM 框架,一个数据响应式的组件系统,通过把页面抽象成一个个组件来增加复用性,降低复杂性,提高维护便利性.所以重要的事情说三遍: 页面一个视图区域抽象成组件,通用型工具抽出公共组件 ...

  8. LiveData 源码解析(2.4.1 版本)

    文章目录 1.LiveData 简介 2.LiveData 配置与基本用法 2.1 依赖引入与配置 2.2 基本用法 2.2.1 LiveData 简单使用 2.2.2 LiveData 扩展 2.2 ...

  9. [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表

    [源码解析] NVIDIA HugeCTR,GPU版本参数服务器- (5) 嵌入式hash表 文章目录 [源码解析] NVIDIA HugeCTR,GPU版本参数服务器--- (5) 嵌入式hash表 ...

最新文章

  1. 从人类交互通信发展简史看元宇宙发展趋势及商业价值
  2. OpenCV 凸包Convex Hull
  3. xcode 可以打开xmind_思维导图,原来Xmind这么强大
  4. 打开Office时总是提示“正在配置microsoft office解决方法
  5. 车辆信息管理系统(C语言大作业)
  6. 【IoT】 产品设计之拆机报告:天猫精灵之万能红外遥控器
  7. android自动切换输入法,一种动态切换Android系统输入法的弹出模式的方法
  8. Vlan(虚拟局域网配置)
  9. 麦芽糖-刀豆球蛋白A,maltose-ConcanavalinA,刀豆球蛋白A-PEG-麦芽糖
  10. DHCP地址池耗尽攻击
  11. 出走的门徒之四:丰元创投朱会灿:冒险的牧师
  12. 07 HSV和HSL和YUV
  13. 构建宽带城域网的基本技术与方案
  14. excel简单操作学习记录1-2021/2/6
  15. MMDetection 使用示例:从入门到出门
  16. 解析BroadcastReceiver的注册、发送与接收过程
  17. Codeforces Round 783 补题
  18. “智慧人才”信息化建设:优化人才服务环境 打造人才高地
  19. VC++VS2010工程转换为VS2005
  20. JMeter实用案例讲解:生成Mockup/Dummy JSON压测REST API

热门文章

  1. 下载的福音--Metalink
  2. CRC-8 校验算法的表计算
  3. Python装饰器——四两拨千斤还是杀鸡用牛刀?
  4. 对 VIIRS/NPP 夜光数据的解读
  5. window系统清理软件 哪个好
  6. windows 64位mysql5.7安装
  7. gin框架35--静态文件服务
  8. 【笔记】专访大象声科汪德亮:利用深度学习解决「鸡尾酒会问题 」
  9. HTML实现表白biu爱心特效 (程序员专属情人节表白网站)
  10. C#开源: 全局钩子+正则表达式=后台自动获取扫描枪数据