ARouter路由跳转到底是怎么实现的?

2,955 阅读12分钟

前言

相信绝大多数公司的项目都做了组件化。为了解耦,组件化势必要解决组件间的通信。其中阿里巴巴开源的Arouter很好的解决了组件间的通信,一直受到开发者的青睐。今天,我们来一步步揭开它的神秘面纱。

首先下载源代码,项目地址:

github.com/alibaba/ARo…

来讲一下项目结构:

  • app:项目主工程,演示代码
  • module-java:java演示代码
  • module-kotlin:kotlin演示代码
  • arouter-annotation:所有注解以及注解涉及到的类
  • arouter-compiler:注解处理器,APT
  • arouter-gradle-plugin:路由表自动注册插件
  • arouter-idea-plugin:路由导航插件,搜索ARouter Helper插件安装即可
  • arouter-api:所有的api,关键代码基本上在这边

第一步就是要生成注解类

@Route @Autowired Interceptor Provider都会生成如下面所示的对应注解类,java生成的注解类的位置在build-generated-sourse-apt中,kotlin生成的注解类的位置在build-generated-sourse-kapt
public class ARouter$$Group$$app implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/app/degrade1", RouteMeta.build(RouteType.PROVIDER, DegradeServiceImpl.class, "/app/degrade1", "app", null, -1, -2147483648));
    atlas.put("/app/main", RouteMeta.build(RouteType.ACTIVITY, MainActivity.class, "/app/main", "app", null, -1, -2147483648));
    atlas.put("/app/path", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/app/path", "app", null, -1, -2147483648));
  }
}

这里需要重点关注一下RouteMeta这个类,这个类存储了目标对象的所有信息。包括路由类型、目标对象类、path、group、参数、优先级、额外参数。

涉及到的知识点:

  • 1.注解,注解生成器apt
  • 2.javapoet
  • 3.auto-service

这里是我写的一个AptDemo,仅供参考:

github.com/liulingfeng…

关于AbstractProcessor的process多次执行可以通过下面方法处理

 @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) {
        if (annotations != null && annotations.size() > 0) {
           
        }
    }

下面正式讲解api

先整体感受一下整个流程

根据官方说明,首先在Application中调用如下api

 if(BuildConfig.DEBUG){
            ARouter.openLog();//打开日志
            ARouter.openDebug();//打开路由调试
        }
 ARouter.init(this);

进入Arouter.init(this)

public static void init(Application application) {
        if (!hasInit) {
            logger = _ARouter.logger;
            hasInit = _ARouter.init(application);

            if (hasInit) {
                _ARouter.afterInit();
            }
        }
    }

hasInit保证只初始化一次,内部调用了_ARouter.init(application),Arouter是门面, _Arouter是具体实现,典型的门面模式。初始化之后调用 _ARouter.afterInit初始化拦截器(这个后面细讲)。继续跟进 _ARouter.init

protected static synchronized boolean init(Application application) {
        mContext = application;
        LogisticsCenter.init(mContext, executor);
        logger.info(Consts.TAG, "ARouter init success!");
        hasInit = true;
        return true;
    }

一眼就看到关键代码在LogisticsCenter.init中,executor是一个自定义的线程池(实现了一种抛出错误的方式)。

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
        try {
            if (registerByPlugin) {
                logger.info(TAG, "Load router map by arouter-auto-register plugin.");
            } else {
                Set<String> routerMap;
                if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                    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);
                } else {
                for (String className : routerMap) {
                    if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                        ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                        ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                    } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                        ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                    }
                }
            }
        } catch (Exception e) {
            throw new HandlerException(TAG + "ARouter init logistics center exception! [" + e.getMessage() + "]");
        }
    }

代码比较长,我把它分解一下

  • 1.判断是不是用插件自动注册路由表,插件注册的方式另说
  • 2.从dex中加载指定路径(com.alibaba.android.arouter.routes)下的所有类的名字,其实就是注解生成类,然后根据版本号升级版本。非debuggable环境下从SharedPreferences缓存中读取(做的一个优化点)
  • 3.反射调用loadInto把Group、Interceptor、Provider的映射关系添加到集合中

看一下各种类型的注解生成类
Root(这里做了优化先加载各个group,用到的时候再加载各个group下的路由)

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

Interceptor

public class ARouter$$Interceptors$$app implements IInterceptorGroup {
  @Override
  public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
    interceptors.put(9, TestInterceptor2.class);
    interceptors.put(10, TestInterceptor.class);
  }
}

Provider

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.xls.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/yourservicegroupname/hello", "yourservicegroupname", null, -1, -2147483648));
  }
}

init工作总结及知识点

  • 1.把Group、Interceptor、Provider注解类的映射添加到Warehouse.groupsIndex、Warehouse.interceptorsIndex、Warehouse.providersIndex集合中
  • 2.实例化所有的Interceptor添加到Warehouse.interceptors中
  • 3.dex分析-多dex怎么查找-热修复的根本原理是什么
  • 4.线程池-线程池各个参数-线程池抛出错误的方法-如何保证线程池线程名字唯一性-原子类

顺便补充一下插件自动注册路由表

首先目光移到PluginLaunch,这是自定义插件的入口。
public class PluginLaunch implements Plugin<Project> {
    @Override
    public void apply(Project project) {
            def android = project.extensions.getByType(AppExtension)
            def transformImpl = new RegisterTransform(project)

            ArrayList<ScanSetting> list = new ArrayList<>(3)
            list.add(new ScanSetting('IRouteRoot'))
            list.add(new ScanSetting('IInterceptorGroup'))
            list.add(new ScanSetting('IProviderGroup'))
            RegisterTransform.registerList = list
            android.registerTransform(transformImpl)
        }
    }
}

这里完成了自定义Transform的注册以及添加需要过滤的接口到ScanSetting,最主要的代码自然是在RegisterTransform中。直奔RegisterTransform的transform方法,首先遍历jar。

inputs.each { TransformInput input ->
        input.jarInputs.each {
                if (ScanUtil.shouldProcessPreDexJar(src.absolutePath)) {
                    ScanUtil.scanJar(src, dest)
                }
                FileUtils.copyFile(src, dest)
            }

static void scanJar(File jarFile, File destFile) {
        if (jarFile) {
            def file = new JarFile(jarFile)
            Enumeration enumeration = file.entries()
            while (enumeration.hasMoreElements()) {
                JarEntry jarEntry = (JarEntry) enumeration.nextElement()
                String entryName = jarEntry.getName()
                if (entryName.startsWith("com/alibaba/android/arouter/routes/")) {
                    InputStream inputStream = file.getInputStream(jarEntry)
                    scanClass(inputStream)
                    inputStream.close()
                } else if ("com/alibaba/android/arouter/core/LogisticsCenter.class" == entryName) {
                    RegisterTransform.fileContainsInitClass = destFile
                }
            }
            file.close()
        }
    }

做到两步工作:1.把com/alibaba/android/arouter/routes包名下的交给scanClass处理(这个稍后会分析到) 2.找到LogisticsCenter.class类,对于这个类想必很熟悉吧。

接下来遍历directory

 input.directoryInputs.each { DirectoryInput directoryInput ->
                directoryInput.file.eachFileRecurse { File file ->
                    if(file.isFile() && ScanUtil.shouldProcessClass(path)){
                        ScanUtil.scanClass(file)
                    }
                }
            }
static void scanClass(InputStream inputStream) {
        ClassReader cr = new ClassReader(inputStream)
        ClassWriter cw = new ClassWriter(cr, 0)
        ScanClassVisitor cv = new ScanClassVisitor(Opcodes.ASM5, cw)
        cr.accept(cv, ClassReader.EXPAND_FRAMES)
        inputStream.close()
    }

把文件流丢给ScanClassVisitor

 static class ScanClassVisitor extends ClassVisitor {

        ScanClassVisitor(int api, ClassVisitor cv) {
            super(api, cv)
        }

        void visit(int version, int access, String name, String signature,
                   String superName, String[] interfaces) {
            super.visit(version, access, name, signature, superName, interfaces)
            RegisterTransform.registerList.each { ext ->
                if (ext.interfaceName && interfaces != null) {
                    interfaces.each { itName ->
                        if (itName == ext.interfaceName) {
                            ext.classList.add(name)
                        }
                    }
                }
            }
        }
    }

一看就明白,就是把所有实现了IRouteRoot、IInterceptorGroup、IProviderGroup接口的类存到集合中

接着看最后一步做了什么

if (fileContainsInitClass) {
            registerList.each { ext ->
                if (ext.classList.isEmpty()) {
                    Logger.e("No class implements found for interface:" + ext.interfaceName)
                } else {
                    RegisterCodeGenerator.insertInitCodeTo(ext)
                }
            }
        }

关键代码都在RegisterCodeGenerator这个类中,我只列关键代码。

private byte[] referHackWhenInit(InputStream inputStream) {
        ClassReader cr = new ClassReader(inputStream)
        ClassWriter cw = new ClassWriter(cr, 0)
        ClassVisitor cv = new MyClassVisitor(Opcodes.ASM5, cw)
        cr.accept(cv, ClassReader.EXPAND_FRAMES)
        return cw.toByteArray()
    }
    
MethodVisitor visitMethod(int access, String name, String desc,
                                  String signature, String[] exceptions) {
            MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions)
            if (name == "loadRouterMap") {
                mv = new RouteMethodVisitor(Opcodes.ASM5, mv)
            }
            return mv
        }

找到hook点loadRouterMap。hook点的设计特别巧妙,增强了代码的可读性。

void visitInsn(int opcode) {
            if ((opcode >= Opcodes.IRETURN && opcode <= Opcodes.RETURN)) {
                extension.classList.each { name ->
                    mv.visitMethodInsn(Opcodes.INVOKESTATIC
                            , "com/alibaba/android/arouter/core/LogisticsCenter"
                            , "register"
                            , "(Ljava/lang/String;)V"
                            , false)
                }
            }
            super.visitInsn(opcode)
        }

调用LogisticsCenter的register方法,我们来看一下register方法做了什么。

 private static void register(String className) {
        if (!TextUtils.isEmpty(className)) {
            try {
                Class<?> clazz = Class.forName(className);
                Object obj = clazz.getConstructor().newInstance();
                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);
            }
        }
    }

所有实现了IRouteRoot、IInterceptorGroup、IProviderGroup接口的类都加入了Warehouse相对应的集合中。至此自动注册工作完成。

插件注册涉及知识点

  • 1.自定义plugin、ASM

路由跳转

ARouter.getInstance().build("/home/test").withString("key3", "888")
                    .withLong("key1", 666L)
                    .navigation(this)

先看build,new一个Postcard对象并给Postcard设置path和group。Postcard构造方法中new了一个bundler对象。PathReplaceService提供了动态改path的方式,后面细讲。

protected Postcard build(String path, String group) {
        if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
            throw new HandlerException(Consts.TAG + "Parameter is invalid!");
        } else {
            PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
            if (null != pService) {
                path = pService.forString(path);
            }
            return new Postcard(path, group);
        }
    }

.withString("key3", "888").withLong("key1", 666L)把参数设置给当前Postcard的bundle中。

再看navigation方法

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        try {
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
            if (debuggable()) {
                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 {
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }

            return null;
        }
        return null;
    }

先看第一部分,重点落在LogisticsCenter.completion(postcard)。内部主要做的是实例化当前group下的具体Route添加到Warehouse.routes,如果没找到就降级处理,两种方式(1.设置NavigationCallback 2.实现DegradeService)

 public synchronized static void completion(Postcard postcard) {
        RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
        if (null == routeMeta) {
            Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());
            if (null == groupMeta) {
                throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
            } else {
                try {
                    IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                    iGroupInstance.loadInto(Warehouse.routes);
                    Warehouse.groupsIndex.remove(postcard.getGroup());
                } catch (Exception e) {
                    throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
                }
                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) {
                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()));
                    }
                    postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
                }

                postcard.withString(ARouter.RAW_URI, rawUri.toString());
            }

            switch (routeMeta.getType()) {
                case PROVIDER:  
                    Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                    IProvider instance = Warehouse.providers.get(providerMeta);
                    if (null == instance) {
                        IProvider provider;
                        try {
                            provider = providerMeta.getConstructor().newInstance();
                            provider.init(mContext);
                            Warehouse.providers.put(providerMeta, provider);
                            instance = provider;
                        } catch (Exception e) {
                            throw new HandlerException("Init provider failed! " + e.getMessage());
                        }
                    }
                    postcard.setProvider(instance);
                    postcard.greenChannel();
                    break;
                case FRAGMENT:
                    postcard.greenChannel();
                default:
                    break;
            }
        }
    }

分析一下这段代码

  • 1.判断Warehouse的routes中对应path的RouteMeta是否为空,看过注解生成类其实我们知道RouteMeta保存了类的具体信息
  • 2.在集合中找到对应的group分组,然后实例化对应分组下的具体Route添加到集合中
  • 3.把RouteMeta的各种信息设置给当前postcard对象
  • 4.uri跳转的处理,uri跳转和普通跳转唯一的区别就是参数的剥离,普通跳转是直接设置的而uri是通过在链接中剥离的,其中参数的数据类型是在Routemeta的paramsType中设置的
  • 5.根据跳转的类型不同做不同处理。如果是服务,直接实例化当前服务调用init方法并设置给postcard。设置绿色通道;如果是fragment,设置绿色通道。所谓绿色通道就是不被拦截器拦截。

第二个部分是处理拦截。我们稍后再讲
先看第三部分

 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:
                final Intent intent = new Intent(currentContext, postcard.getDestination());
                intent.putExtras(postcard.getExtras());

                int flags = postcard.getFlags();
                if (-1 != flags) {
                    intent.setFlags(flags);
                } else if (!(currentContext instanceof Activity)) {
                    intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }

                String action = postcard.getAction();
                if (!TextUtils.isEmpty(action)) {
                    intent.setAction(action);
                }

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

看到这里是不是很亲切,这不就是我们平时常写的startActivity(intent,class)吗?如果是fragment的话反射调用Fragment构造方法返回fragment对象。provider也是返回 Provider对象。至此跳转这一块基本上都搞清楚了。

分析一下拦截器是怎么实现的

之前讲了Aroute.init之后会将所有的拦截器实例化。我们看看_ARouter.afterInit()做了什么
static void afterInit() {
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }

使用自己的路由方法初始化interceptorService服务,没毛病。该服务的实现类是InterceptorServiceImpl,从前面的分析可以知道navigation会调用服务的init方法。看看init里面做了什么

 @Override
    public void init(final Context context) {
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                    for (Map.Entry<Integer, Class<? extends IInterceptor>> entry : Warehouse.interceptorsIndex.entrySet()) {
                        Class<? extends IInterceptor> interceptorClass = entry.getValue();
                        try {
                            IInterceptor iInterceptor = interceptorClass.getConstructor().newInstance();
                            iInterceptor.init(context);
                            Warehouse.interceptors.add(iInterceptor);
                        } catch (Exception ex) {
                            throw new HandlerException(TAG + "ARouter init interceptor error! name = [" + interceptorClass.getName() + "], reason = [" + ex.getMessage() + "]");
                        }
                    }

                    interceptorHasInit = true;
                }
            }
        });
    }

反射调用所有拦截器的构造函数实例化对象添加到Warehouse.interceptors并调用init方法,这里使用了object.wait和object.notifyAll保证子线程中的所有拦截器实例化完成。拦截的时机在前面已经提到过了,我们来看看具体的代码。

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);
                    }
                }
            });
 @Override
    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
        if (null != Warehouse.interceptors && Warehouse.interceptors.size() > 0) {
            LogisticsCenter.executor.execute(new Runnable() {
                @Override
                public void run() {
                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                    try {
                        _excute(0, interceptorCounter, postcard);
                        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);
                    }
                }
            });
        } else {
            callback.onContinue(postcard);
        }
    }
private static void _excute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
        if (index < Warehouse.interceptors.size()) {
            IInterceptor iInterceptor = Warehouse.interceptors.get(index);
            iInterceptor.process(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    counter.countDown();
                    _excute(index + 1, counter, postcard);
                }

                @Override
                public void onInterrupt(Throwable exception) {
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());    // save the exception message for backup.
                    counter.cancel();
                }
            });
        }
    }

使用CountDownLatch.await使得代码阻塞直到所有拦截器执行完成或者超时。拦截器process方法中需要调用callback.onContinue才能调用到counter.countDown()移交到下一个拦截器,这就解释了自定义的拦截器为什么一定要调用counter.countDown()

涉及知识点
  • 1.线程间通信
  • 2.CountDownLatch
  • 3.Object.wait/Object.notify

降级处理

两种方式:1.navigation的时候添加NavigationCallback回调 2.写一个类实现DegradeService别忘了添加@Route path可以随意 第一种比较简单我么不讲,讲一下第二种方式
@Route(path = "/app/degrade1")
class DegradeServiceImpl : DegradeService {
    override fun onLost(context: Context?, postcard: Postcard?) {
        Log.e("降级处理","自定义降级处理")
    }

    override fun init(context: Context?) {
    }
}

生成的注解类在ARouter?Providers?app中,也是init的时候就把映射关系添加到集合中。调用的地方是在navigation中,这段代码也间接的说明了NavigationCallback的优先级高于全局降级处理。

if (null != callback) {
                callback.onLost(postcard);
            } else {
                DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
                if (null != degradeService) {
                    degradeService.onLost(context, postcard);
                }
            }

关键代码是下面一段代码,诠释了服务的navigation是如何运行的

protected <T> T navigation(Class<? extends T> service) {
        try {
            Postcard postcard = LogisticsCenter.buildProvider(service.getName());
            if (null == postcard) {
                postcard = LogisticsCenter.buildProvider(service.getSimpleName());
            }

            LogisticsCenter.completion(postcard);
            return (T) postcard.getProvider();
        } catch (NoRouteFoundException ex) {
            logger.warning(Consts.TAG, ex.getMessage());
            return null;
        }
    }

buildProvider是根据service的名字从集合中找到对应的RouteMeta并把path和group设置给postcard,接下来也是给postcard设置其他各种参数,和上面分析的大同小异。

path动态改变

调用的方式和降级处理一模一样,时机是在build的时候。

参数自动获取(uri方式跳转参数必须加Autowired标记)

 @Autowired
 @JvmField
 var key3: String? = null
 @Autowired
 @JvmField
 var key1: Long = 0L
 
 ARouter.getInstance().inject(this)

从文档中可以知道,按照上面的方式就可以自动获取各个参数。关键代码肯定是在inject方法中,调用的还是服务。

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

看看AutowiredService的autowire方法

 @Override
    public void autowire(Object instance) {
        String className = instance.getClass().getName();
        try {
            if (!blackList.contains(className)) {
                ISyringe autowiredHelper = classCache.get(className);
                if (null == autowiredHelper) {
                    autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
                }
                autowiredHelper.inject(instance);
                classCache.put(className, autowiredHelper);
            }
        } catch (Exception ex) {
            blackList.add(className);
        }
    }

最关键的方法是XXclass_?ARouter?Autowired.inject,其实这个类还是在注解生成类中

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

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    TestOneActivity substitute = (TestOneActivity)target;
    substitute.key3 = substitute.getIntent().getStringExtra("girl");
    substitute.key1 = substitute.getIntent().getLongExtra("key1", substitute.key1);
  }
}

还是通过getIntent().getExtra方法获取的参数,然后把获取的参数设置给当前类。

分析完源码之后扪心自问一下下面问题是否能回答上来

  • 1.openLog和openDebug为什么要在init之前?
  • 2.非Debug环境如何升级路由表——即添加路由?
  • 3.为什么要自定义线程池?线程池抛出错误的方式有哪几种?
  • 4.activity的跳转是怎么实现的?
  • 5.fragment实例是怎么拿到的?为什么不允许拦截?
  • 6.服务是如何调用的?
  • 7.path能动态修改吗?在哪个时机修改的?
  • 8.uri方式是如何跳转的?
  • 9.路由跳转能否在子线程中?
  • 10.拦截器怎么实现的?初始化的时机?为什么要在process调用callback.onContinue()。各个拦截器之间的优先级是如何保证的(是在跳转的时候根据priority判断的吗)
  • 11.全局降级处理怎么实现的,和NavigationCallback谁优先级更高?
  • 12.如何对path进行预处理,让所有路由失效?
  • 13.实现多个类继承PathReplaceService、PretreatmentService实际会用哪个。

个人的一些思考,大家可以讨论一下

  • 1.Fragment未做onActivityResult回调支持,对Fragment的场景还是偏简单了。
  • 2.插件化是怎么实现路由表的升级的。
  • 3.自动注册路由表的plugin考虑做增量和并发编译处理,效率有待商榷。
  • 4.注解实现类的取名Group和path比较容易混淆。
  • 5.组件跳转结果无法拿到(造成url跳转发起方——比如前端,没有callback,无状态)