ARouter源码分析

组件化被越来越多的Android项目采用,而作为组件化的基础——路由也是重中之重。本篇文章将详细的分析阿里巴巴开源的路由框架ARouter。从源码的角度解释为什么这样使用,以及避免做什么,让你使用地更加顺滑。

项目地址

ARouter

项目结构

我们把项目clone到本地,打开ARouter项目,可以看到分为如下几个Module:

image

其中app、module-java、module-kotlin是演示demo相关的,也就是使用的示例。

arouter-register是1.3.0版本新添加的gradle插件的代码,用于路由表的自动注册,可以缩短初始化时间,解决应用加固导致无法直接访问dex文件初始化失败的问题。(本篇文章不涉及这个模块)

arouter-annotation包含了注解类以及携带数据的bean;

arouter-compiler包含了注解处理类,通过java的Annotation Processor Tool按照定义的Processor生成所需要的类。
以demo为例子,当运行项目后会在build文件下生成arouter相关的代码:

image

我们先不用去关心生成代码的细节,只需要知道按照用法的提示添加诸如Route等注解之后,arouter-compiler中的注解处理类会自动帮我们生成需要的代码(如上图所示)。对源码的分析也只需要知道生成的类的做了什么就够了。

本篇文章也不讨论生成代码细节,如果想了解apt的生成过程,可以参考:Java注解处理器 以及更方便生成的Java代码的库javapoet

arouter-api提供了给我们使用的api,以实现路由功能。

那我们就从api开始分析。

源码分析

init

按照官方说明,我们找到Arouter的入口,也就是初始化的地方:

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

我们直接进入ARouter.init方法:

    /**
     * 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.");
        }
    }

变量hasInit用于保证初始化代码只执行一次;
logger是一个日志工具类;

注意_ARouter类的下划线,Arouter是对外暴露api的类,而_ARouter是真正的实现类。为什么这样设计那?当然是为了解耦啦。具体点说有哪些好处呢?看看这个init方法,除了对实现类的调用,还有日志的打印,有没有装饰模式的感觉?可以增加一些额外的功能。其次,对比_ARouter类与Arouter类的方法,可以看到明显Arouter类的方法少。要知道Arouter是对外暴露的,我们可以有选择暴露用户需要的方法,而把一些方法隐藏在内部。相比于用private修饰,这种方式灵活性更强。

接着我们进入实现类看下:

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(mContext, executor);,executor是一个线程池。我们接着来看下去除日志等无关代码后的LogisticsCenter.init方法:

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

    Set<String> routerMap;//生成类的类名集合
    // 如果是debug模式或者是新版本,从apt生成的包中加载类
    if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
        routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
        if (!routerMap.isEmpty()) {//加入sp缓存
            context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
        }

        PackageUtils.updateVersion(context); //更新版本
    } else {//否则从缓存读取类名
        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)) {
            ((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);
        }
    }
    ...
}

这里体现了debuggable模式的作用,如果没有开启debuggable,并且调试的时候肯定不会更改版本号,因此只会从缓存中读取类信息,所以新添加的路由不会加载到内存中。

ROUTE_ROOT_PAKCAGE是一个常量:

public static final String ROUTE_ROOT_PAKCAGE = "com.alibaba.android.arouter.routes";

可以看到与上面demo生成类的截图的包名相同。而ClassUtils.getFileNameByPackageName方法做的就是找到app的dex,然后遍历出其中的属于com.alibaba.android.arouter.routes包下的所有类名,打包成集合返回。可以想象遍历整个dex查找指定类名的工作量有多大,因此才会有开头提到的1.3.0版本新增gradle插件来代替这个过程。

拿到所有生成类名的集合后,通过反射实例化对象并调用方法,将注解的一些元素添加到static集合中:

class Warehouse {
    // Cache route and metas
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>();
    static Map<String, RouteMeta> routes = new HashMap<>();

    // Cache provider
    static Map<Class, IProvider> providers = new HashMap<>();
    static Map<String, RouteMeta> providersIndex = new HashMap<>();

    // Cache interceptor
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]");
    static List<IInterceptor> interceptors = new ArrayList<>();
}

interceptor的map有些特别,是UniqueKeyTreeMap,其实做的很简单,仅仅是在key(优先级)相同时,抛出指定的异常。因此,记住不要出现优先级相同的interceptor。

生成的类有统一的命名规则,方便区分,分别实现对应的接口:

public interface IRouteRoot {
    void loadInto(Map<String, Class<? extends IRouteGroup>> routes);
}

public interface IInterceptorGroup {
    void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptor);
}

public interface IProviderGroup {
    void loadInto(Map<String, RouteMeta> providers);
}

public interface IRouteGroup {
    void loadInto(Map<String, RouteMeta> atlas);
}

我们来看下demo生成的实现类都做了什么:

public class ARouter$Root$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    //key为分组名,即路径的第一段,value为分组中所有的映射关系
    routes.put("service", ARouter$Group$service.class);
    routes.put("test", ARouter$Group$test.class);
  }
}

//将module中使用@Route注解的activity或Fragment添加到集合中,这里的方法会在之后调用
public class ARouter$Group$test implements IRouteGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> atlas) {
    atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("pac", 9); put("ch", 5); put("fl", 6); put("obj", 10); put("name", 8); put("dou", 7); put("boy", 0); put("objList", 10); put("map", 10); put("age", 3); put("url", 8); put("height", 3); }}, -1, -2147483648));
    atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test", new java.util.HashMap<String, Integer>(){{put("key1", 8); }}, -1, -2147483648));
    atlas.put("/test/activity3", RouteMeta.build(RouteType.ACTIVITY, Test3Activity.class, "/test/activity3", "test", new java.util.HashMap<String, Integer>(){{put("name", 8); put("boy", 0); put("age", 3); }}, -1, -2147483648));
    atlas.put("/test/activity4", RouteMeta.build(RouteType.ACTIVITY, Test4Activity.class, "/test/activity4", "test", null, -1, -2147483648));
    atlas.put("/test/fragment", RouteMeta.build(RouteType.FRAGMENT, BlankFragment.class, "/test/fragment", "test", null, -1, -2147483648));
    atlas.put("/test/webview", RouteMeta.build(RouteType.ACTIVITY, TestWebview.class, "/test/webview", "test", null, -1, -2147483648));
  }
}

IRouteRoot的实现将有@Route注解的module名添加到参数集合中,也就是groupsIndex。

这里会存在一个小陷阱,如果不同的module中存在相同的分组(即路径的第一段,如上面的“test”),则会在对应的module中生成不同的IRouteGroup的实现,然后在此处会执行分别执行routes.put("test", ARouter$Group$moduleA.class);,以及routes.put("test", ARouter$Group$moduleB.class);,但是因为key相同,因此前一个会被覆盖,导致前一个定义的路由无法找到。具体可以看我提的issue,官方的建议是路径分组与模块名相同,并且不同模块不要使用相同的分组。

public class ARouter$Interceptors$app implements IInterceptorGroup {
  @Override
  public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
    //key是优先级
    interceptors.put(7, Test1Interceptor.class);
  }
}

同样的IInterceptorGroup的实现将@Interceptor注解的类添加到参数集合中,也就是interceptorsIndex中。

public class ARouter$Providers$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    providers.put("com.alibaba.android.arouter.demo.testservice.HelloService", RouteMeta.build(RouteType.PROVIDER, HelloServiceImpl.class, "/service/hello", "service", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.facade.service.SerializationService", RouteMeta.build(RouteType.PROVIDER, JsonServiceImpl.class, "/service/json", "service", null, -1, -2147483648));
    providers.put("com.alibaba.android.arouter.demo.testservice.SingleService", RouteMeta.build(RouteType.PROVIDER, SingleService.class, "/service/single", "service", null, -1, -2147483648));
  }
}

IProviderGroup的实现将继承自IProvider的类添加到参数集合中,也就是providersIndex中。

RouteMeta是一个数据bean,封装了被注解类的一些信息

public class RouteMeta {
    private RouteType type;         // Type of route
    private Element rawType;        // Raw type of route
    private Class<?> destination;   // Destination
    private String path;            // Path of route
    private String group;           // Group of route
    private int priority = -1;      // The smaller the number, the higher the priority
    private int extra;              // Extra data
    private Map<String, Integer> paramsType;  // Param type

    public static RouteMeta build(RouteType type, Class<?> destination, String path, String group, Map<String, Integer> paramsType, int priority, int extra) {
        return new RouteMeta(type, null, destination, path, group, paramsType, priority, extra);
    }
    ...
}

其中paramsType是包含了所有注解了Autowired的属性的信息,key为属性名,value为属性类型,ARouter将可被intent传递的数据类型定义了对应的int类型: BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,STRING,PARCELABLE,OBJECT分别对应0,1,2,3...

RouteType是一个枚举,表示被注解类的路由类型:

public enum RouteType {
    ACTIVITY(0, "android.app.Activity"),
    SERVICE(1, "android.app.Service"),
    PROVIDER(2, "com.alibaba.android.arouter.facade.template.IProvider"),
    CONTENT_PROVIDER(-1, "android.app.ContentProvider"),
    BOARDCAST(-1, ""),
    METHOD(-1, ""),
    FRAGMENT(-1, "android.app.Fragment"),
    UNKNOWN(-1, "Unknown route type");

    int id;
    String className;

    RouteType(int id, String className) {
        this.id = id;
        this.className = className;
    }

    ...
}

可以看到ARouter虽然目前只支持Activity和Fragment,但是也预留了Service和Boardcast的类型,可能以后也会实现。

最后别忘了init中还有一个_ARouter.afterInit();方法:

static void afterInit() {
    // Trigger interceptor init, use byName.
    interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
}

它是一个管理拦截器的服务,实现了IProvider接口,并且用自己实现的的路由方式获取实例。嗯,没毛病!

根据官方的说明,IProvider接口是用来暴露服务,并且在初始化的时候会被调用init(Context context)方法。具体的服务有其实现提供,那我们就来看下它的实现做了些什么:

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
    private static boolean interceptorHasInit;
    private static final Object interceptorInitLock = new Object();

    @Override
    public void init(final Context context) {
       ... //省略子线程以及同步处理,下面的操作实际是在子线程处理的
        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() + "]");
                }
            }
        ...
    }
}

还记的在上一步初始化的时候将所有注解了Interceptor的类的信息存入了Warehouse.interceptorsIndex,这里就将这些类实例化,并调用iInterceptor.init(context);完成自定义的初始化内容,最后放入Warehouse.interceptors集合中。

总结来说,init过程就是把所有注解的信息加载内存中,并且完成所有拦截器的初始化。

navigation

完成初始化之后,就可以实现路由跳转了:

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

那我们就从这里开始分析:
getInstance()是获取ARouter类的单例方法,没什么好说的。

public Postcard build(String path) {
    return _ARouter.getInstance().build(path);
}

build方法调动了实现类_ARouter的build方法,继续看:

protected Postcard build(String path) {
    ...//省略判空
    PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
    if (null != pService) {
        path = pService.forString(path);
    }
    return build(path, extractGroup(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);
    }
}

这里出现一个PathReplaceService,它是继承IProvider的接口,它是预留给用户实现路径动态变化功能,官方有详细说明:

public interface PathReplaceService extends IProvider {
    String forString(String path);
    Uri forUri(Uri uri);
}

需要注意的是,build(String path)方法及build(String path, String group)方法中都尝试使用PathReplaceService完成路径动态变化,因此,在变化前需要做好判断,否则出现变换两次的情况(例如拼接字符)。

extractGroup方法截取路径中的第一段作为分组名。

build方法最终返回一个Postcard对象。它也是一个数据bean,继承自RouteMeta,附加一些跳转信息,如参数之类:

public final class Postcard extends RouteMeta {
    // Base
    private Uri uri;
    private Object tag;             // A tag prepare for some thing wrong.
    private Bundle mBundle;         // Data to transform
    private int flags = -1;         // Flags of route
    private int timeout = 300;      // Navigation timeout, TimeUnit.Second
    private IProvider provider;     // It will be set value, if this postcard was provider.
    private boolean greenChannel;
    private SerializationService serializationService;

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

    ...
}

最后调用该Postcard对象的navigation方法,层层调用,最终还是调用的_Arouter的navigation方法,我们先来看下方法的前半部分:

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        if (null != callback) {
            callback.onLost(postcard);
        } else {    // 交给全局降级策略
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }

    if (null != callback) {
        callback.onFound(postcard);
    }

    ...    //方法未结束,分到之后
}

大致可看出LogisticsCenter.completion(postcard);肯定是试图找到跳转的目标,如果找不到则让callback回调onLost,或者交给全局降级策略处理。找到则回调callback的onFound方法。

那我们看看LogisticsCenter.completion(postcard);是如何寻找的:

public synchronized static void completion(Postcard postcard) {
    ...
    //从集合中找路径对应的RouteMeta
    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 { // 加载具体分组的映射,并从groupsIndex集合中删除类信息
            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) {   // 如果是Uri跳转
            Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);//分割路径中的参数
            Map<String, Integer> paramsType = routeMeta.getParamsType();//获取Autowired注解的属性
            if (MapUtils.isNotEmpty(paramsType)) { //将属性对应的参数值放入Bundle
                for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                    setValue(postcard,
                            params.getValue(),
                            params.getKey(),
                            resultMap.get(params.getKey()));
                }

                // Bundle存入需要自动注入的属性信息
                postcard.getExtras().putStringArray(ARouter.AUTO_INJECT, paramsType.keySet().toArray(new String[]{}));
            }

            // 保存原始路径
            postcard.withString(ARouter.RAW_URI, rawUri.toString());
        }

        switch (routeMeta.getType()) {
            case PROVIDER:  //如果目标类型是provider,实例化对象并放入postcard
                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();    // Provider不经过拦截器处理
                break;
            case FRAGMENT:
                postcard.greenChannel();    // Fragment不仅过拦截器处理
            default:
                break;
        }
    }
}

可以看到主要功能是找到匹配的目标类,并将目标类的一些信息拷贝到postcard对象中。

我们继续看navigation方法的后半部分:

protected Object navigation(final Context context, final Postcard postcard, final int request

Code, final NavigationCallback callback) {
    ...
    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);
                }
            }
        });
    } else { //绿色通道
        return _navigation(context, postcard, requestCode, callback);
    }

    return null;
}

_navigation方法是最终处理,并且有返回值,如果路由目标是Fragment或者IProvider的话。拦截器的处理可能会耗时,因此会放到子线程处理,通过回调完成继续操作,但此时就无法返回目标类(Fragment或IProvider),也就解释了为什么上面的completion方法中设置了绿色通道。

我们先不考虑拦截过程,直接看_navigation方法:

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    //mContext是init传入的Application
    final Context currentContext = null == context ? mContext : context;

    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 (-1 != flags) {
                intent.setFlags(flags);
            } else if (!(currentContext instanceof Activity)) {    // Non activity, need less one flag.
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }

            //拦截过程在子线程,因此该方法可能仍在子线程,需要切换到主线程
            new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                    if (requestCode > 0) {  // Need start for result
                        ActivityCompat.startActivityForResult((Activity) currentContext, intent, requestCode, postcard.getOptionsBundle());
                    } else {
                        ActivityCompat.startActivity(currentContext, intent, postcard.getOptionsBundle());
                    }

                    if ((-1 != postcard.getEnterAnim() && -1 != postcard.getExitAnim()) && currentContext instanceof Activity) {    // Old version.
                        ((Activity) currentContext).overridePendingTransition(postcard.getEnterAnim(), postcard.getExitAnim());
                    }

                    if (null != callback) { // Navigation over.
                        callback.onArrival(postcard);
                    }
                }
            });

            break;
        case PROVIDER:
            //LogisticsCenter.completion方法中存入的
            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;
   }

navigation可以传入Context或者不传,不传的话默认就用Application的context,每开启一个Activity就会新开一个task。建议普通界面跳转都传入Activity,避免栈的混乱。

至此整个路由跳转的过程大致完成,整个过程可以分为:封装Postcard -> 查找信息集合,实例化目标类 -> 返回实例或者跳转。

Interceptor

前面我们跳过了拦截过程,现在我们来分析下这个过程,拦截功能是通过ARouter提供的interceptorService实现的,并且之前我们在init章节分析它的初始化,接下来看看具体是如何拦截的:

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

可以看到调用了doInterceptions方法,在看这个方法之前,我们先要了解一个类:CountDownLatch。相关资料:CountDownLatch理解一:与join的区别

简单来说CountDownLatch可以阻塞一个线程,知道内部计数器为0时,才继续执行阻塞的线程,计数器的初始值通过构造传入,通过调用countDown()方法减少一个计数。

拦截器的方法中,其中就使用CancelableCountDownLatch类,它继承自CountDownLatch,并扩展了cancel方法,用于直接将计数归0,放开阻塞:

public void cancel() {
    while (getCount() > 0) {
        countDown();
    }
}

我们回到拦截器的doInterceptions方法:

@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);
                    //阻塞线程直到超时,或者计数归0
                    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);
    }
}

/**
 * Excute interceptor
 *
 * @param index    current interceptor index
 * @param counter  interceptor counter
 * @param postcard routeMeta
 */
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) {
                // 如果放行,则计数减1,执行后一个拦截器
                counter.countDown();
                _excute(index + 1, counter, postcard); 
            }

            @Override
            public void onInterrupt(Throwable exception) {
                // 拦截,将exception存入postcard的tag字段,计数归零
                postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());
                counter.cancel();
            });
        }
    }

首先会创建一个与拦截器数量相同的CancelableCountDownLatch初始计数值,每放行一个拦截器就countDown,并交给后一个拦截器,如果拦截则清0计数,并将拦截的Throwable存入postcard的tag字段,interceptorCounter.await();阻塞直到计数归0或者阻塞超时(默认是300秒),最后通过interceptorCounter.getCount()判断是否是超时,还是拦截或者放行。

可以看到拦截的过程都是在子线程中处理,包括Interceptor的process也是在子线程调用的,因此,如果想要在拦截过程中展示dialog等都需要切换到主线程。

inject

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

同样的,我们从方法入口层层深入探究:

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

AutowiredService与InterceptorService类似,都是Arouter自己实现的继承IProvider接口的服务。我们找到AutowiredService的实现类:

@Route(path = "/arouter/service/autowired")
public class AutowiredServiceImpl implements AutowiredService {
    private LruCache<String, ISyringe> classCache;
    private List<String> blackList;

    @Override
    public void init(Context context) {
        classCache = new LruCache<>(66);
        blackList = new ArrayList<>();
    }

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

去掉缓存集合之后,其实很简单,核心内容就两句:

ISyringe autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
autowiredHelper.inject(instance);

ISyringe是一个接口:

public interface ISyringe {
    void inject(Object target);
}

它的实现是apt生成的,每一个带有@Autowired注解的类都会生成一个对应的ISyringe的实现,如demo中的:

//生成类的类名 = 目标类名+ "$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() + "!");
    }
  }
}

注意这里赋值的操作是直接调用“目标类对象.属性”的方式赋值,因此,private修饰的属性无法通过这种方式赋值,并且在赋值时会抛出异常,被AutowiredServiceImpl的autowire方法中的try-catch捕获,存入不需要注入的集合中,最终导致同一个类中的其他非private属性也无法注入。

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

原文链接:https://www.jianshu.com/p/bc4c34c6a06c

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350