可能是最詳細(xì)的ARouter源碼分析

組件化被越來(lái)越多的Android項(xiàng)目采用,而作為組件化的基礎(chǔ)——路由也是重中之重夫偶。本篇文章將詳細(xì)的分析阿里巴巴開源的路由框架ARouter厕宗。從源碼的角度解釋為什么這樣使用,以及避免做什么奠货,讓你使用地更加順滑介褥。

項(xiàng)目地址

ARouter

項(xiàng)目結(jié)構(gòu)

我們把項(xiàng)目clone到本地,打開ARouter項(xiàng)目递惋,可以看到分為如下幾個(gè)Module:

image

其中app柔滔、module-java、module-kotlin是演示demo相關(guān)的萍虽,也就是使用的示例睛廊。

arouter-register是1.3.0版本新添加的gradle插件的代碼,用于路由表的自動(dòng)注冊(cè)贩挣,可以縮短初始化時(shí)間喉前,解決應(yīng)用加固導(dǎo)致無(wú)法直接訪問dex文件初始化失敗的問題。(本篇文章不涉及這個(gè)模塊)

arouter-annotation包含了注解類以及攜帶數(shù)據(jù)的bean王财;

arouter-compiler包含了注解處理類卵迂,通過java的Annotation Processor Tool按照定義的Processor生成所需要的類。
以demo為例子绒净,當(dāng)運(yùn)行項(xiàng)目后會(huì)在build文件下生成arouter相關(guān)的代碼:

image

我們先不用去關(guān)心生成代碼的細(xì)節(jié)见咒,只需要知道按照用法的提示添加諸如Route等注解之后,arouter-compiler中的注解處理類會(huì)自動(dòng)幫我們生成需要的代碼(如上圖所示)挂疆。對(duì)源碼的分析也只需要知道生成的類的做了什么就夠了改览。

本篇文章也不討論生成代碼細(xì)節(jié),如果想了解apt的生成過程缤言,可以參考:Java注解處理器 以及更方便生成的Java代碼的庫(kù)javapoet宝当。

arouter-api提供了給我們使用的api,以實(shí)現(xiàn)路由功能胆萧。

那我們就從api開始分析庆揩。

源碼分析

init

按照官方說(shuō)明,我們找到Arouter的入口跌穗,也就是初始化的地方:

if (isDebug()) {           // 這兩行必須寫在init之前订晌,否則這些配置在init過程中將無(wú)效
    ARouter.openLog();     // 打印日志
    ARouter.openDebug();   // 開啟調(diào)試模式(如果在InstantRun模式下運(yùn)行,必須開啟調(diào)試模式蚌吸!線上版本需要關(guān)閉,否則有安全風(fēng)險(xiǎn))
}
ARouter.init(mApplication); // 盡可能早锈拨,推薦在Application中初始化

我們直接進(jìn)入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用于保證初始化代碼只執(zhí)行一次;
logger是一個(gè)日志工具類羹唠;

注意_ARouter類的下劃線奕枢,Arouter是對(duì)外暴露api的類娄昆,而_ARouter是真正的實(shí)現(xiàn)類。為什么這樣設(shè)計(jì)那验辞?當(dāng)然是為了解耦啦稿黄。具體點(diǎn)說(shuō)有哪些好處呢喊衫?看看這個(gè)init方法跌造,除了對(duì)實(shí)現(xiàn)類的調(diào)用,還有日志的打印族购,有沒有裝飾模式的感覺壳贪?可以增加一些額外的功能。其次寝杖,對(duì)比_ARouter類與Arouter類的方法违施,可以看到明顯Arouter類的方法少。要知道Arouter是對(duì)外暴露的瑟幕,我們可以有選擇暴露用戶需要的方法磕蒲,而把一些方法隱藏在內(nèi)部。相比于用private修飾只盹,這種方式靈活性更強(qiáng)辣往。

接著我們進(jìn)入實(shí)現(xiàn)類看下:

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

明顯有價(jià)值的是LogisticsCenter.init(mContext, executor);,executor是一個(gè)線程池殖卑。我們接著來(lái)看下去除日志等無(wú)關(guān)代碼后的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>()));
    }
    
    //判斷類型,使用反射實(shí)例化對(duì)象孵稽,并調(diào)用方法
    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);
        }
    }
    ...
}

這里體現(xiàn)了debuggable模式的作用许起,如果沒有開啟debuggable,并且調(diào)試的時(shí)候肯定不會(huì)更改版本號(hào)菩鲜,因此只會(huì)從緩存中讀取類信息园细,所以新添加的路由不會(huì)加載到內(nèi)存中。

ROUTE_ROOT_PAKCAGE是一個(gè)常量:

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

可以看到與上面demo生成類的截圖的包名相同接校。而ClassUtils.getFileNameByPackageName方法做的就是找到app的dex猛频,然后遍歷出其中的屬于com.alibaba.android.arouter.routes包下的所有類名,打包成集合返回馅笙÷浊牵可以想象遍歷整個(gè)dex查找指定類名的工作量有多大,因此才會(huì)有開頭提到的1.3.0版本新增gradle插件來(lái)代替這個(gè)過程董习。

拿到所有生成類名的集合后烈和,通過反射實(shí)例化對(duì)象并調(diào)用方法,將注解的一些元素添加到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招刹,其實(shí)做的很簡(jiǎn)單恬试,僅僅是在key(優(yōu)先級(jí))相同時(shí),拋出指定的異常疯暑。因此训柴,記住不要出現(xiàn)優(yōu)先級(jí)相同的interceptor。

生成的類有統(tǒng)一的命名規(guī)則妇拯,方便區(qū)分幻馁,分別實(shí)現(xiàn)對(duì)應(yīng)的接口:

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

我們來(lái)看下demo生成的實(shí)現(xiàn)類都做了什么:

public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    //key為分組名,即路徑的第一段越锈,value為分組中所有的映射關(guān)系
    routes.put("service", ARouter$$Group$$service.class);
    routes.put("test", ARouter$$Group$$test.class);
  }
}

//將module中使用@Route注解的activity或Fragment添加到集合中仗嗦,這里的方法會(huì)在之后調(diào)用
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的實(shí)現(xiàn)將有@Route注解的module名添加到參數(shù)集合中,也就是groupsIndex甘凭。

這里會(huì)存在一個(gè)小陷阱稀拐,如果不同的module中存在相同的分組(即路徑的第一段,如上面的“test”)丹弱,則會(huì)在對(duì)應(yīng)的module中生成不同的IRouteGroup的實(shí)現(xiàn)德撬,然后在此處會(huì)執(zhí)行分別執(zhí)行routes.put("test", ARouter$$Group$$moduleA.class);,以及routes.put("test", ARouter$$Group$$moduleB.class);躲胳,但是因?yàn)閗ey相同蜓洪,因此前一個(gè)會(huì)被覆蓋,導(dǎo)致前一個(gè)定義的路由無(wú)法找到泛鸟。具體可以看我提的issue蝠咆,官方的建議是路徑分組與模塊名相同,并且不同模塊不要使用相同的分組北滥。

public class ARouter$$Interceptors$$app implements IInterceptorGroup {
  @Override
  public void loadInto(Map<Integer, Class<? extends IInterceptor>> interceptors) {
    //key是優(yōu)先級(jí)
    interceptors.put(7, Test1Interceptor.class);
  }
}

同樣的IInterceptorGroup的實(shí)現(xiàn)將@Interceptor注解的類添加到參數(shù)集合中刚操,也就是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的實(shí)現(xiàn)將繼承自IProvider的類添加到參數(shù)集合中再芋,也就是providersIndex中菊霜。

RouteMeta是一個(gè)數(shù)據(jù)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傳遞的數(shù)據(jù)類型定義了對(duì)應(yīng)的int類型: BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,STRING,PARCELABLE,OBJECT分別對(duì)應(yīng)0司训,1构捡,2,3...

RouteType是一個(gè)枚舉壳猜,表示被注解類的路由類型:

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勾徽,但是也預(yù)留了Service和Boardcast的類型,可能以后也會(huì)實(shí)現(xiàn)统扳。

最后別忘了init中還有一個(gè)_ARouter.afterInit();方法:

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

它是一個(gè)管理攔截器的服務(wù)喘帚,實(shí)現(xiàn)了IProvider接口畅姊,并且用自己實(shí)現(xiàn)的的路由方式獲取實(shí)例。嗯吹由,沒毛踩粑础!

根據(jù)官方的說(shuō)明倾鲫,IProvider接口是用來(lái)暴露服務(wù)粗合,并且在初始化的時(shí)候會(huì)被調(diào)用init(Context context)方法。具體的服務(wù)有其實(shí)現(xiàn)提供级乍,那我們就來(lái)看下它的實(shí)現(xiàn)做了些什么:

@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) {
       ... //省略子線程以及同步處理舌劳,下面的操作實(shí)際是在子線程處理的
        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() + "]");
                }
            }
        ...
    }
}

還記的在上一步初始化的時(shí)候?qū)⑺凶⒔饬薎nterceptor的類的信息存入了Warehouse.interceptorsIndex,這里就將這些類實(shí)例化玫荣,并調(diào)用iInterceptor.init(context);完成自定義的初始化內(nèi)容,最后放入Warehouse.interceptors集合中大诸。

總結(jié)來(lái)說(shuō)捅厂,init過程就是把所有注解的信息加載內(nèi)存中,并且完成所有攔截器的初始化资柔。

navigation

完成初始化之后焙贷,就可以實(shí)現(xiàn)路由跳轉(zhuǎn)了:

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

那我們就從這里開始分析:
getInstance()是獲取ARouter類的單例方法,沒什么好說(shuō)的贿堰。

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

build方法調(diào)動(dòng)了實(shí)現(xiàn)類_ARouter的build方法辙芍,繼續(xù)看:

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

這里出現(xiàn)一個(gè)PathReplaceService,它是繼承IProvider的接口羹与,它是預(yù)留給用戶實(shí)現(xiàn)路徑動(dòng)態(tài)變化功能故硅,官方有詳細(xì)說(shuō)明:

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

需要注意的是,build(String path)方法及build(String path, String group)方法中都嘗試使用PathReplaceService完成路徑動(dòng)態(tài)變化纵搁,因此吃衅,在變化前需要做好判斷,否則出現(xiàn)變換兩次的情況(例如拼接字符)腾誉。

extractGroup方法截取路徑中的第一段作為分組名徘层。

build方法最終返回一個(gè)Postcard對(duì)象。它也是一個(gè)數(shù)據(jù)bean利职,繼承自RouteMeta趣效,附加一些跳轉(zhuǎn)信息,如參數(shù)之類:

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

最后調(diào)用該P(yáng)ostcard對(duì)象的navigation方法猪贪,層層調(diào)用跷敬,最終還是調(diào)用的_Arouter的navigation方法,我們先來(lái)看下方法的前半部分:

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 {    // 交給全局降級(jí)策略
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }

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

    ...    //方法未結(jié)束哮伟,分到之后
}

大致可看出LogisticsCenter.completion(postcard);肯定是試圖找到跳轉(zhuǎn)的目標(biāo)干花,如果找不到則讓callback回調(diào)onLost妄帘,或者交給全局降級(jí)策略處理。找到則回調(diào)callback的onFound方法池凄。

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

public synchronized static void completion(Postcard postcard) {
    ...
    //從集合中找路徑對(duì)應(yīng)的RouteMeta
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {    // 內(nèi)存中沒有抡驼,可能是還沒有加載過
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // 找到分組
        if (null == groupMeta) {//分組也沒有,那就是沒有注冊(cè)
            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 { 
        //拷貝數(shù)據(jù)
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        Uri rawUri = postcard.getUri();
        if (null != rawUri) {   // 如果是Uri跳轉(zhuǎn)
            Map<String, String> resultMap = TextUtils.splitQueryParameters(rawUri);//分割路徑中的參數(shù)
            Map<String, Integer> paramsType = routeMeta.getParamsType();//獲取Autowired注解的屬性
            if (MapUtils.isNotEmpty(paramsType)) { //將屬性對(duì)應(yīng)的參數(shù)值放入Bundle
                for (Map.Entry<String, Integer> params : paramsType.entrySet()) {
                    setValue(postcard,
                            params.getValue(),
                            params.getKey(),
                            resultMap.get(params.getKey()));
                }

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

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

        switch (routeMeta.getType()) {
            case PROVIDER:  //如果目標(biāo)類型是provider致盟,實(shí)例化對(duì)象并放入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);//初始化在這里調(diào)用
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        throw new HandlerException("Init provider failed! " + e.getMessage());
                    }
                }
                postcard.setProvider(instance);
                postcard.greenChannel();    // Provider不經(jīng)過攔截器處理
                break;
            case FRAGMENT:
                postcard.greenChannel();    // Fragment不僅過攔截器處理
            default:
                break;
        }
    }
}

可以看到主要功能是找到匹配的目標(biāo)類,并將目標(biāo)類的一些信息拷貝到postcard對(duì)象中尤慰。

我們繼續(xù)看navigation方法的后半部分:

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

Code, final NavigationCallback callback) {
    ...
    if (!postcard.isGreenChannel()) {  //不是綠色通道馏锡,即經(jīng)過攔截器處理
        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方法是最終處理,并且有返回值伟端,如果路由目標(biāo)是Fragment或者IProvider的話杯道。攔截器的處理可能會(huì)耗時(shí),因此會(huì)放到子線程處理责蝠,通過回調(diào)完成繼續(xù)操作党巾,但此時(shí)就無(wú)法返回目標(biāo)類(Fragment或IProvider),也就解釋了為什么上面的completion方法中設(shè)置了綠色通道霜医。

我們先不考慮攔截過程齿拂,直接看_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或者不傳署海,不傳的話默認(rèn)就用Application的context,每開啟一個(gè)Activity就會(huì)新開一個(gè)task医男。建議普通界面跳轉(zhuǎn)都傳入Activity砸狞,避免棧的混亂。

至此整個(gè)路由跳轉(zhuǎn)的過程大致完成昨登,整個(gè)過程可以分為:封裝Postcard -> 查找信息集合趾代,實(shí)例化目標(biāo)類 -> 返回實(shí)例或者跳轉(zhuǎn)。

Interceptor

前面我們跳過了攔截過程丰辣,現(xiàn)在我們來(lái)分析下這個(gè)過程撒强,攔截功能是通過ARouter提供的interceptorService實(shí)現(xiàn)的,并且之前我們?cè)趇nit章節(jié)分析它的初始化笙什,接下來(lái)看看具體是如何攔截的:

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

可以看到調(diào)用了doInterceptions方法飘哨,在看這個(gè)方法之前,我們先要了解一個(gè)類:CountDownLatch琐凭。相關(guān)資料:CountDownLatch理解一:與join的區(qū)別

簡(jiǎn)單來(lái)說(shuō)CountDownLatch可以阻塞一個(gè)線程芽隆,知道內(nèi)部計(jì)數(shù)器為0時(shí),才繼續(xù)執(zhí)行阻塞的線程,計(jì)數(shù)器的初始值通過構(gòu)造傳入胚吁,通過調(diào)用countDown()方法減少一個(gè)計(jì)數(shù)牙躺。

攔截器的方法中,其中就使用CancelableCountDownLatch類腕扶,它繼承自CountDownLatch孽拷,并擴(kuò)展了cancel方法,用于直接將計(jì)數(shù)歸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);
                    //阻塞線程直到超時(shí)脓恕,或者計(jì)數(shù)歸0
                    interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                    if (interceptorCounter.getCount() > 0) { //攔截超時(shí)
                        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) {
    //有下一個(gè)攔截器
    if (index < Warehouse.interceptors.size()) {
        IInterceptor iInterceptor = Warehouse.interceptors.get(index);
        iInterceptor.process(postcard, new InterceptorCallback() {
            @Override
            public void onContinue(Postcard postcard) {
                // 如果放行,則計(jì)數(shù)減1窿侈,執(zhí)行后一個(gè)攔截器
                counter.countDown();
                _excute(index + 1, counter, postcard); 
            }

            @Override
            public void onInterrupt(Throwable exception) {
                // 攔截炼幔,將exception存入postcard的tag字段,計(jì)數(shù)歸零
                postcard.setTag(null == exception ? new HandlerException("No message.") : exception.getMessage());
                counter.cancel();
            });
        }
    }

首先會(huì)創(chuàng)建一個(gè)與攔截器數(shù)量相同的CancelableCountDownLatch初始計(jì)數(shù)值史简,每放行一個(gè)攔截器就countDown乃秀,并交給后一個(gè)攔截器,如果攔截則清0計(jì)數(shù)乘瓤,并將攔截的Throwable存入postcard的tag字段环形,interceptorCounter.await();阻塞直到計(jì)數(shù)歸0或者阻塞超時(shí)(默認(rèn)是300秒),最后通過interceptorCounter.getCount()判斷是否是超時(shí)衙傀,還是攔截或者放行。

可以看到攔截的過程都是在子線程中處理萨咕,包括Interceptor的process也是在子線程調(diào)用的统抬,因此,如果想要在攔截過程中展示dialog等都需要切換到主線程危队。

inject

通過@Autowired注解的屬性聪建,通過調(diào)用ARouter.getInstance().inject(this);可以實(shí)現(xiàn)自動(dòng)注入。我們知道原生的Activity傳遞數(shù)據(jù)是通過Bundle攜帶的茫陆。因此金麸,ARouter的數(shù)據(jù)傳遞肯定也是基于Bundle,并實(shí)現(xiàn)了自動(dòng)賦值的功能簿盅。

同樣的挥下,我們從方法入口層層深入探究:

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

AutowiredService與InterceptorService類似,都是Arouter自己實(shí)現(xiàn)的繼承IProvider接口的服務(wù)桨醋。我們找到AutowiredService的實(shí)現(xiàn)類:

@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.
        }
    }
}

去掉緩存集合之后棚瘟,其實(shí)很簡(jiǎn)單,核心內(nèi)容就兩句:

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

ISyringe是一個(gè)接口:

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

它的實(shí)現(xiàn)是apt生成的喜最,每一個(gè)帶有@Autowired注解的類都會(huì)生成一個(gè)對(duì)應(yīng)的ISyringe的實(shí)現(xiàn)偎蘸,如demo中的:

//生成類的類名 = 目標(biāo)類名+ "$$ARouter$$Autowired"(固定后綴)
public class BlankFragment$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);
    //強(qiáng)轉(zhuǎn)成目標(biāo)類
    BlankFragment substitute = (BlankFragment)target;
    //給每個(gè)注解了@Autowired的屬性賦值
    //這里的key為屬性名或者注解中給定的name
    substitute.name = substitute.getArguments().getString("name");
    // 如果存在Bundle無(wú)法攜帶的類型,則會(huì)通過SerializationService序列化成json的String傳遞,SerializationService沒有提供默認(rèn)實(shí)現(xiàn)迷雪,需要用戶自己實(shí)現(xiàn)
    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() + "!");
    }
  }
}

注意這里賦值的操作是直接調(diào)用“目標(biāo)類對(duì)象.屬性”的方式賦值,因此,private修飾的屬性無(wú)法通過這種方式賦值限书,并且在賦值時(shí)會(huì)拋出異常,被AutowiredServiceImpl的autowire方法中的try-catch捕獲章咧,存入不需要注入的集合中倦西,最終導(dǎo)致同一個(gè)類中的其他非private屬性也無(wú)法注入。

其注入原理基本與ButterKnife類似慧邮。前面分析navigation的時(shí)候我們看到了將Uri參數(shù)存入Bundle的過程(或者由用戶手動(dòng)調(diào)用withXX存入bundle)调限,此處則是將Bundle中的數(shù)據(jù)取出,并賦值給@Autowired注解的屬性误澳。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耻矮,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子忆谓,更是在濱河造成了極大的恐慌裆装,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件倡缠,死亡現(xiàn)場(chǎng)離奇詭異哨免,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昙沦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門琢唾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人盾饮,你說(shuō)我怎么就攤上這事采桃。” “怎么了丘损?”我有些...
    開封第一講書人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵普办,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我徘钥,道長(zhǎng)衔蹲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任呈础,我火速辦了婚禮舆驶,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘猪落。我一直安慰自己贞远,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開白布笨忌。 她就那樣靜靜地躺著蓝仲,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上袱结,一...
    開封第一講書人閱讀 49,007評(píng)論 1 284
  • 那天亮隙,我揣著相機(jī)與錄音,去河邊找鬼垢夹。 笑死溢吻,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的果元。 我是一名探鬼主播促王,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼而晒!你這毒婦竟也來(lái)了蝇狼?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤倡怎,失蹤者是張志新(化名)和其女友劉穎迅耘,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體监署,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颤专,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了钠乏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栖秕。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖晓避,靈堂內(nèi)的尸體忽然破棺而出累魔,到底是詐尸還是另有隱情,我是刑警寧澤够滑,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站吕世,受9級(jí)特大地震影響彰触,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜命辖,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一况毅、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧尔艇,春花似錦尔许、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春余佛,著一層夾襖步出監(jiān)牢的瞬間柠新,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工辉巡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恨憎,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓郊楣,卻偏偏與公主長(zhǎng)得像憔恳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子净蚤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • ARouter源碼解讀 以前看優(yōu)秀的開源項(xiàng)目钥组,看到了頁(yè)面路由框架ARouter,心想頁(yè)面路由是個(gè)啥東東塞栅,于是乎網(wǎng)上...
    陸元偉閱讀 509評(píng)論 0 1
  • 組件化 模塊化者铜、組件化與插件化 在項(xiàng)目發(fā)展到一定程度,隨著人員的增多放椰,代碼越來(lái)越臃腫作烟,這時(shí)候就必須進(jìn)行模塊化的拆分...
    silentleaf閱讀 4,943評(píng)論 2 12
  • 本文章用于記錄筆者學(xué)習(xí) ARouter 源碼的過程,僅供參考砾医,如有錯(cuò)誤之處還望悉心指出拿撩,一起交流學(xué)習(xí)。 ARout...
    DevLocke閱讀 13,945評(píng)論 6 52
  • 讀完一本書之后探赫,別人問你這本書講了什么,自己卻不能用簡(jiǎn)單的語(yǔ)言把書的主要內(nèi)容告訴他撬呢。 想要研究一個(gè)主題時(shí)伦吠,不知道如...
    Azhu讀書閱讀 273評(píng)論 0 1
  • 你又不是公主毛仪,憑什么要所有人替你著想,就算你是公主芯勘,其他你身邊的人也是公主啊箱靴,她們都堅(jiān)強(qiáng)的活著,自己照顧著自己的心...
    那些年留下的故事閱讀 306評(píng)論 0 0