阅读 1520

Arouter全面讲解二

继续上一篇的Arouter讲解说起,上一篇主要介绍了Arouter使用,这篇主要围绕Arouter源码的设计,以及通过源码我们能学习到什么,以及如何应对面试过程中Arouter的问题

目的

  • 源码分析
  • Arouter路由跳转的设计
  • Arouter拦截器的设计
  • Arouter的服务怎么设计的
  • Arouter的注解属性怎么获取的
  • Arouter自动加载路由表
  • 思考源码的设计

源码分析

在使用Arouter过程中,我们的module依赖了arouter-apiarouter-compiler,在编译期arouter-compiler通过扫描项目中用到的Route、Autowired、Interceptor等注解来生成对应的class文件,大家如果想学习学习编译期扫描注解生成class文件可以学习apt相关的技术,或者看Arouter官网arouter-compiler模块怎么扫描注解生成class文件的。

比如我在login模块的LoginActivity中定义如下的注解:

@Route(path = "/login/loginActivity")
class LoginActivity : AppCompatActivity() {
}
复制代码

结果在module的build目录下生成了ARouter$$Root$$loginclass文件,它是实现IRouteRoot接口:

public class ARouter$$Root$$login implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("login", ARouter$$Group$$login.class);
  }
}
复制代码

而此处的ARouter$$Group$$login类它是实现了IRouteGroup接口:

public class ARouter$$Group$$login implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/login/loginActivity", RouteMeta.build(RouteType.ACTIVITY, LoginActivity.class, "/login/loginactivity", "login", null, -1, -2147483648));
  }
}
复制代码

里面把loginActivity的信息通过RouteMeta存到了传进来的map中了。 其实我们的ARouter$$Group$$组名类不只是存放了activity的RouteMeta信息,还会有IProvider类型。

上一节我们使用的JsonServiceImpl它是实现了SerializationService接口,最终是实现了IProvider接口,和activity使用的注解是一样,只不过用的group是yourservicegroupname,还有DegradeServiceImpl类也是一样的,它是实现了DegradeService接口,最终是实现了IProvider接口,group也是yourservicegroupname,都会在ARouter$$Group$$yourservicegroupname类中保存了一个RouteMeta信息;如果用Interceptor注解的话,会生成对应的ARouter$$Interceptors$$模块名的class类,上一节定义的LoginInterceptor最终是被ARouter$$Interceptors$$app管理的:

public class ARouter$$Interceptors$$app implements IInterceptorGroup {
  @Override
  public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
    interceptors.put(8, LoginInterceptor.class);
  }
}
复制代码

IInterceptor就没有被包装成RouteMeta对象,上面在ARouter$$Group$$yourservicegroupname中定义的IProvider信息还会在ARouter$$Providers$$app被定义了一遍:

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.alibaba.android.arouter.facade.service.DegradeService", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl.class, "/yourservicegroupname/DegradeServiceImpl", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/yourservicegroupname/pathReplaceService", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.PretreatmentService", RouteMeta.build(RouteType.PROVIDER, PretreatmentServiceImpl.class, "/yourservicegroupname/pretreatmentService", "yourservicegroupname", null, -1, -2147483648));
  }
}
复制代码

其实都是我们在app模块中定义的IProvider类型的service,那为什么在ARouter$$Group$$组名定义了IProvider类型,那为什么还需要在ARouter$$Providers$$模块名中还要定义一遍呢,看官莫急,听我细细道来。

小节

所以在apt阶段,生成的class类有Arouter$$Root$$模块名,模块名是在gradle中配置的arg("AROUTER_MODULE_NAME", "${project.getName()}")属性,把所有组的信息放到传进来的map中,这个组是通过我们在Route注解的path属性拆分的,比如定义/login/loginActivity,会认为组名是login,Arouter$$Root$$组名放的是该组下,所有的路由表信息,包括route、provider注解通过RouteMeta包装对应的class类信息,provider注解会放在Arouter$$Providers$$模块名下面。

初始化

我们在上一面使用部分,初始化部分代码如下:

ARouter.init(this)
复制代码

就一句,那下面追随源码看下:

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.afterInit方法,其实我们的入口虽然是ARouter类的,但是真正调用的还是_ARouter类的方法,废话不多说,直接看_ARouter.init方法:

_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;
}
复制代码

最终还是调用了LogisticsCenter的init方法,并且拿到主线的looper给了mHandler,顺着看LogisticsCenter的init方法:

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    mContext = context;
    executor = tpe;

    try {
        //如果通过插件获取路由表信息,则该方法registerByPlugin=false
        loadRouterMap();
        if (registerByPlugin) {
        } else {
            Set<String> routerMap;

            // 如果是debuggable=true或者有新的版本
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                // 加载前缀为com.alibaba.android.arouter.routes的class类,放到set集合里面
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                //将过滤到的class类放到sp中,方便下次直接取
                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 {
                //直接从sp中取过滤到的class类
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
            }


            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // 将com.alibaba.android.arouter.routes.ARouter$$Root前缀的class类放到Warehouse.groupsIndex中
                    ((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前缀的class类放到Warehouse.interceptorsIndex中
                    ((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前缀的class类放到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() + "]");
    }
}
复制代码

在上面init方法中做了几件事:

  • 通过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

该方法里面会去拿InterceptorServiceImpl,怎么去拿的,以及里面做了些啥,我们一一来看:

interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
复制代码

ARouter.build

ARouter.getInstance().build("/arouter/service/interceptor")会返回PostCard对象,并给PostCardgrouppath属性赋值为arouter/arouter/service/interceptor

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);
    }
}
复制代码

先判断有没有设置PathReplaceService类型的路由,如果有会调用forString方法返回新的path,这也就是我们的path替换的IProvider,在第一篇文章我们定义过PathReplaceServiceImpl类:

@Route(path = "/yourservicegroupname/pathReplaceService") // 必须标明注解
class PathReplaceServiceImpl : PathReplaceService {

    override fun forString(path: String): String {
        if (path == "/login/loginActivity") {
            return "/share/shareActivity"
        }
        return path // 按照一定的规则处理之后返回处理后的结果
    }

    override fun forUri(uri: Uri?): Uri? {
        return null // 按照一定的规则处理之后返回处理后的结果
    }

    override fun init(context: Context?) {
    }
}
复制代码

如果有该PathReplaceService会替换掉我们的/login/loginActivity/share/shareActivity,这里先不管怎么拿到PathReplaceServiceImpl,通过extractGroup方法拿到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;
    }
}
复制代码

很简单,按照第一个'/'和第二个'/'的字符作为group信息,所以这也是为什么我们在定义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);
    }
}
复制代码

所以build过程把传过来的path构造出Postcard对象,给path和group赋值。

PostCard.navigation

navigation有很多重载的方法,最终都会走_Arouter.navigation,其中navigation里面也有两种形式获取到路由表类,我们先介绍activity常规的形式:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    //这也是个路由表,通过另外一种形式获取PretreatmentService的实例
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
    //如果onPretreatment返回false就是自己处理路由逻辑,不往下走了
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        return null;
    }

    try {
        //最终会走这里
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        ...
        if (null != callback) {
            callback.onLost(postcard);
        } else {
            // 获取DegradeService的实例
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }

        return null;
    }
    //省略provider部分逻辑和_navigation部分代码

    return null;
}
复制代码

由于navigation代码比较长,我把代码分块来说,上面首先获取PretreatmentService类型的路由表,我们先只说上面传入/arouter/service/interceptor,怎么得到InterceptorService实例的,我们直接看LogisticsCenter.completion

public synchronized static void completion(Postcard postcard) {
    //第一次进来是拿不到RouteMeta信息的,因为routes是空的
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        //我们传过来的postcard的group是arouter、path是/arouter/service/interceptor
        //我们在groupIndex中找对应的groupMeta,其实看到这的时候,我们默认是没有root为arouter的组,只能去arouter默认提供的root中找
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
        if (null == groupMeta) {
        } else {
            try {
                //反射拿到ARouter$$Group$$arouter
                IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                //所以最终把InterceptorServiceImpl放到了Warehouse.routes中
                iGroupInstance.loadInto(Warehouse.routes);
                //用完groupsIndex对应的IRouteGroup信息后,从map中移除掉,下次就直接从routes中去拿了
                Warehouse.groupsIndex.remove(postcard.getGroup());

            } catch (Exception e) {
                throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
            }
            //继续走一遍completion,下次会走下面的else
            completion(postcard);
        }
    } else {
        //对postCard属性赋值
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        Uri rawUri = postcard.getUri();
        //默认uri为空
        if (null != rawUri) {  
            Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);
            Map<String, Integer> paramsType = routeMeta.getParamsType();

            if (MapUtils.isNotEmpty(paramsType)) {
                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());
        }

        switch (routeMeta.getType()) {
            //由于InterceptorServiceImpl是provider类型的
            case PROVIDER:
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                //拿对应的provider
                IProvider instance = Warehouse.providers.get(providerMeta);
                if (null == instance) {
                    IProvider provider;
                    try {
                        //反射创建InterceptorServiceImpl
                        provider = providerMeta.getConstructor().newInstance();
                        //调用InterceptorServiceImpl的init方法
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                //给postcard赋值
                postcard.setProvider(instance);
                postcard.greenChannel();
                break;
            case FRAGMENT:
                postcard.greenChannel();
            default:
                break;
        }
    }
}
复制代码

上面代码还是很清晰的,首先从Warehouse.routes去拿对应的RouteMeta信息,如果没有,就先去Warehouse.groupsIndex中拿,而此时的postCard的group是arouter,我们从下面图看下,拿到的是ARouter$$Group$$arouter的class:

所以此时的iGroupInstance是ARouter$$Group$$arouter,通过反射创建ARouter$$Group$$arouter,紧接着把Warehouse.routes传进它的loadInto方法:

所以我们最终能确定把AutowiredServiceImplInterceptorServiceImplRouteMeta放进了Warehouse.routes的map中。注意:上面用完了Warehouse.groupsIndex中对应的group信息后,会从Warehouse.groupsIndex中移除该group的信息

最后又走一遍completion方法,所以会走else分支,由于我们还在获取InterceptorServiceImpl过程中,它的RouteType=PROVIDER,所以providerMetaInterceptorServiceImpl类型的,然后去Warehouse.providers拿,此时是空的,所以通过反射创建InterceptorServiceImpl对象,创建完调用InterceptorServiceImpl调用init方法:

@Override
public void init(final Context context) {
    LogisticsCenter.executor.execute(new Runnable() {
        @Override
        public void run() {
            if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                //遍历我们自己代码里面定义的interceptorsIndex
                for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                    Class<? extends IInterceptor> interceptorClass = entry.getValue();
                    try {
                        //拿到对应的iInterceptor后
                        IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                        //调用init
                        iInterceptor.init(context);
                        //把iInterceptor放到interceptors中
                        Warehouse.interceptors.add(iInterceptor);
                    } catch (Exception ex) {
                        throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                    }
                }

                interceptorHasInit = true;

                logger.info(TAG, "ARouter interceptors init over.");

                synchronized (interceptorInitLock) {
                    interceptorInitLock.notifyAll();
                }
            }
        }
    });
}
复制代码

上面获取interceptorsIndex,还记得上一节我们定义的LoginInterceptor吗?它会放到interceptorsIndex的map里面,所以这里是拿到所有的IInterceptor,反射创建每一个IInterceptor,调用init方法,添加到Warehouse.interceptors中。

到这里,我们已经清楚了Warehouse.routes存放了AutowiredServiceImplInterceptorServiceImpl类型的RouteMetaWarehouse.providers存放了InterceptorServiceImplWarehouse.interceptors存放了所有的IInterceptor

创建完InterceptorServiceImpl,我们一下子要回到_ARouternavigation方法的下半部分代码,上面没有贴出代码:

//如果是`InterceptorServiceImpl`类型的postcard.isGreenChannel()是true,除非是activity或fragment类型的
if (!postcard.isGreenChannel()) {
    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);
}
复制代码

所以上面代码我们直接看_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 PROVIDER:
            return postcard.getProvider();
       
    }

    return null;
}
复制代码

这里先把其他类型给省略掉了,如果是PROVIDER类型的,直接把postCard的provider直接返回,所以我们上面怎么拿到的InterceptorServiceImpl就迎刃而解了。

PostCard. T navigation(Class<? extends T> service)

上面分析的是通过path获取到路由表的实例,还有另外通过传进来的class类型也可以获取,我们就拿上一节用到的JsonServiceImpl怎么拿到的,在PostCard中可以通过withObject传值,其实归功于JsonServiceImpl,可以看下面代码:

public Postcard withObject(@Nullable String key, @Nullable Object value) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    mBundle.putString(key, serializationService.object2Json(value));
    return this;
}
复制代码

所以直接看_Arouter.navigation(Class<? extends T> service) 方法:

protected <T> T navigation(Class<? extends T> service) {
    try {
        Postcard postcard = LogisticsCenter.buildProvider(service.getName());
        if (null == postcard) {
            // No service, or this service in old version.
            postcard = LogisticsCenter.buildProvider(service.getSimpleName());
        }
        if (null == postcard) {
            return null;
        }
        LogisticsCenter.completion(postcard);
        return (T) postcard.getProvider();
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        return null;
    }
}
复制代码

直接看LogisticsCenter.buildProvider方法:

public static Postcard buildProvider(String serviceName) {
    RouteMeta meta = Warehouse.providersIndex.get(serviceName);
    if (null == meta) {
        return null;
    } else {
        return new Postcard(meta.getPath(), meta.getGroup());
    }
}
复制代码

此处是从providersIndex中去拿对应的RouteMeta信息,而providersIndex是在初始化sdk中通过加载ARouter$$Providers$$模块名的loadInto方法,好吧,为了大家能顺着阅读,我把上面的代码重新复制了一份:

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.alibaba.android.arouter.facade.service.DegradeService", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl.class, "/yourservicegroupname/DegradeServiceImpl", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/yourservicegroupname/json", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/yourservicegroupname/pathReplaceService", "yourservicegroupname", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.PretreatmentService", RouteMeta.build(RouteType.PROVIDER, PretreatmentServiceImpl.class, "/yourservicegroupname/pretreatmentService", "yourservicegroupname", null, -1, -2147483648));
  }
}
复制代码

看到了没,SerializationService作为的key时候,指向的是JsonServiceImplRouteMeta,所以在 navigation方法传class对象的时候,是在providersIndex中先去找,没找到,最终通过navigation传path去找,这也是为什么在ARouter$$Group$$组名ARouter$$Providers$$模块名中都定义了provider的routeMeta信息,一种通过path来找provider,一种通过class来找provider

Arouter路由跳转的设计

由于activity路由的postCard的isGreenChannel()为false,因此在_Arouter.navigation方法中会走如下代码:

上面已经分析了interceptorServiceinterceptorServiceImpl,因此看doInterceptions方法:

@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
   //省略了拦截器的流程,直接看callback.onContinue
   callback.onContinue(postcard);
    
}
复制代码

如果默认没有拦截器,直接会走callback.onContinue,回到_Arouter的navigation,会走_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:
            // Build intent
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;
     
    }
    return null;
}
复制代码

所以最终也是通过routeMeta的destination作为目标activity的class跳转。

所以activity最终跳转也是先获取routeMeta,最终完成跳转。

Arouter拦截器的设计

这个我们直接看InterceptorServiceImpldoInterceptions方法:

@Override
public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
    //如果有interceptors
    if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
        //判断拦截器有没有初始化成功
        checkInterceptorsInitStatus();

        if (!interceptorHasInit) {
            callback.onInterrupt(new HandlerException("Interceptors initialization takes too much time."));
            return;
        }

        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                try {
                    _execute(0, interceptorCounter, postcard);
                    //拦截器如果超时会回调callback.onInterrupt
                    interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                    if (interceptorCounter.getCount() > 0) {
                        callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                    } else if (null != postcard.getTag()) {
                        callback.onInterrupt(new HandlerException(postcard.getTag().toString()));
                    } else {
                        callback.onContinue(postcard);
                    }
                } catch (Exception e) {
                    callback.onInterrupt(e);
                }
            }
        });
    }
}
复制代码

创建CancelableCountDownLatch作为所有拦截器处理完成的标志,处理IInterceptor通过触发process方法,如果想继续处理下一个拦截通过触发InterceptorCallback的onContinue方法,如果想拦截掉路由的处理,通过触发InterceptorCallback的onInterrupt方法。如果所有的拦截都不拦截,则会回调到doInterceptions方法的callback.onContinue(postcard);这一句,而这一句最终回到了_Arouter._navigation方法,走正常的路由处理了。

Arouter的服务怎么设计的

上一节我们通过定义了HelloServiceImpl的服务:

@Route(path = "/common/hello", name = "测试服务")
class HelloServiceImpl : HelloService {
    override fun sayHello(name: String): String {
        Log.d("HelloServiceImpl", "hello, $name")
        return "hello, $name"
    }

    override fun init(context: Context) {}
}
复制代码

然后我们可以通过传服务的path或class类都可以获取:

helloService2 =ARouter.getInstance().build("/common/hello").navigation() as HelloService
helloService3 = ARouter.getInstance().navigation(HelloService::class.java)
复制代码

这也就是我们上面说的Iprovider可以通过两种形式获取,他们分别定义在了ARouter$$Group$$组名ARouter$$Providers$$模块名中。

Arouter的注解属性怎么获取的

我们在定义属性的时候通过Autowired注解赋值,比如我上一节在shareActivity中定义如下属性:

@Autowired(name = "username")
lateinit var username: String
@Autowired
lateinit var testBean: TestBean
@Autowired(name ="listBean" )
lateinit var listBean: List<TestBean>
复制代码

会在build下生成如下代码:

public class ShareActivity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    ShareActivity substitute = (ShareActivity)target;
    substitute.username = substitute.getIntent().getExtras() == null ? substitute.username : substitute.getIntent().getExtras().getString("username", substitute.username);
    if (null != serializationService) {
      substitute.testBean = serializationService.parseObject(substitute.getIntent().getStringExtra("testBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<TestBean>(){}.getType());
    } else {
    }
    if (null != serializationService) {
      substitute.listBean = serializationService.parseObject(substitute.getIntent().getStringExtra("listBean"), new com.alibaba.android.arouter.facade.model.TypeWrapper<List<TestBean>>(){}.getType());
    } else { 
    }
  }
}

复制代码

可以看到在inject方法中,获取了基本类型和对象类型,如果是基本类型,直接给属性赋值,如果是对象类型,先获取SerializationService对应的实例,所以我们想定义对象类型的属性,需要实例化SerializationService类型,那什么时候调用的inject方法,在定义属性的类中有这么一句:

ARouter.getInstance().inject(this)
复制代码

最终到_Arouter.inject方法:

static void inject(Object thiz) {
    AutowiredService autowiredService = ((AutowiredService) ARouter.getInstance().build("/arouter/service/autowired").navigation());
    if (null != autowiredService) {
        autowiredService.autowire(thiz);
    }
}
复制代码

这里拿的AutowiredServiceAutowiredServiceImpl,这跟InterceptorServiceImpl获取方式是一样的,这里就不介绍了,直接看AutowiredServiceImpldoInject方法:

private void doInject(Object instance, Class<?> parent) {
    Class<?> clazz = null == parent ? instance.getClass() : parent;
    ISyringe syringe = getSyringe(clazz);
    //上面就拿到了ShareActivity$$ARouter$$Autowired类,调用inject方法
    if (null != syringe) {
        syringe.inject(instance);
    }
    Class<?> superClazz = clazz.getSuperclass();
    if (null != superClazz && !superClazz.getName().startsWith("android")) {
        doInject(instance, superClazz);
    }
}
复制代码

最终在这里调用了ShareActivity$$ARouter$$Autowired的inject方法。

Arouter自动加载路由表

自动加载路由表是通过arouter-register来实现的,主要通过在编译期给LogisticsCenterloadRouterMap方法插入register方法调用的代码:

我们可以通过反编译工具查看下apk下面的该类:

反编译出来的代码确实在loadRouterMap方法处插入了register方法调用的代码,并且把registerByPlugin置为true,所以最终不会通过扫描dex文件来加载路由表类装载到map中。

那大家想想和普通的通过扫描dex文件加载class有什么区别呢,我们在上面普通加载dex文件可以看到在初始化Arouter sdk的时候是非常慢的,因为它得扫描dex文件,然后加载dex文件里面的所有class,然后过滤出arouter需要的class文件,这还不是算慢得,如果虚拟机不支持MultiDex还会更慢,它会通过压缩所有的dex文件,然后压缩成zip,然后通过DexFile.loadDex转话成dex文件的集合,我们知道在DexFile.loadDex过程中会把普通的dex文件抓话成odex,这个过程是很慢的,关于dex文件转成odex做了些啥大家可以查查,具体我也不是很清楚,哈哈哈。最后通过遍历dex文件,拿到里面的class文件,最后过滤拿到Arouter需要的class,在我们项目中亲测arouter普通初始化是花了4秒多,所以我们可以看到Arouter自动加载路由表的插件对启动优化还是有很大改善的,Arouter自动加载路由表的插件是使用的通过gradle插装技术在编译期插入代码来达到自动加载路由表信息。

关于字节码插装技术我也不是很懂,所以我也会去恶补相关知识去了。

源码总结

  • 在初始化阶段把所有的root、interceptor、provider信息分别存储到groupsIndex、interceptorsIndex、providersIndex中
  • 然后初始化interceptorServiceImpl实例,顺便将interceptor加入到interceptors中。
  • 在加载路由的时候,先去routes中取,如果没取到则去groupsIndex中拿group信息,再拿对应的metaMeta信息,将属性封装到postCard中,最后通过判断metaType是那种类型,做相应类型的处理。
  • navigation有两种形式获取Iprovider,通过传class或path,如果传的是class则去interceptorsIndex找对应的Iprovder,如果是path则去groupsIndex中找,找到后,最终会保存在providers中。

思考源码的设计

  • 关于这块的思考大家有什么想说的呢,Arouter通过定义两级的path,一级是group、二级是path,如果没有定义两级的path直接抛异常,这块改成一级的path会有什么问题吗?

  • 首先通过group找到对应组的class对象,里面包装的该组下面所有的routeMeta信息,下次如果遇到相同group的路由,则直接去找routeMeta信息,这么设计有什么好处?

  • Arouter通过分组包装路由表,有快速查询路由的效率,所以我们可以尽量让Iprovider和Activity、fragment可以放到不同饿组里面,方便我们能快速拿到对应的routeMeta信息,是不是这样呢?

    如果你还有什么要问的,欢迎评论区留言,当做互相学习!!!

文章分类
Android
文章标签