ARouter源碼分析

組件化被越來越多的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)致無法直接訪問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代碼的庫javapoet唁奢。

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

那我們就從api開始分析麻掸。

源碼分析

init

按照官方說明,我們找到Arouter的入口赐纱,也就是初始化的地方:

if (isDebug()) {           // 這兩行必須寫在init之前脊奋,否則這些配置在init過程中將無效
    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)說有哪些好處呢?看看這個(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è)線程池。我們接著來看下去除日志等無關(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插件來代替這個(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í)做的很簡單喉祭,僅僅是在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);
}

我們來看下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è)定義的路由無法找到肖粮。具體可以看我提的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ù)官方的說明蚊夫,IProvider接口是用來暴露服務(wù)诉字,并且在初始化的時(shí)候會(huì)被調(diào)用init(Context context)方法。具體的服務(wù)有其實(shí)現(xiàn)提供知纷,那我們就來看下它的實(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é)來說乍桂,init過程就是把所有注解的信息加載內(nèi)存中冲杀,并且完成所有攔截器的初始化。

navigation

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

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

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

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ì)說明:

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方法承璃,我們先來看下方法的前半部分:

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í)就無法返回目標(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)在我們來分析下這個(gè)過程控轿,攔截功能是通過ARouter提供的interceptorService實(shí)現(xiàn)的,并且之前我們?cè)趇nit章節(jié)分析它的初始化,接下來看看具體是如何攔截的:

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ū)別

簡單來說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í)很簡單擂红,核心內(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無法攜帶的類型昵骤,則會(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修飾的屬性無法通過這種方式賦值变秦,并且在賦值時(shí)會(huì)拋出異常成榜,被AutowiredServiceImpl的autowire方法中的try-catch捕獲,存入不需要注入的集合中伴栓,最終導(dǎo)致同一個(gè)類中的其他非private屬性也無法注入伦连。

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

原文鏈接:http://www.reibang.com/p/bc4c34c6a06c

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末饺窿,一起剝皮案震驚了整個(gè)濱河市歧焦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌肚医,老刑警劉巖绢馍,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肠套,居然都是意外死亡舰涌,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門你稚,熙熙樓的掌柜王于貴愁眉苦臉地迎上來瓷耙,“玉大人,你說我怎么就攤上這事刁赖「橥矗” “怎么了?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵宇弛,是天一觀的道長鸡典。 經(jīng)常有香客問我,道長枪芒,這世上最難降的妖魔是什么彻况? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任谁尸,我火速辦了婚禮,結(jié)果婚禮上疗垛,老公的妹妹穿的比我還像新娘症汹。我一直安慰自己,他們只是感情好贷腕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布背镇。 她就那樣靜靜地躺著,像睡著了一般泽裳。 火紅的嫁衣襯著肌膚如雪瞒斩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天涮总,我揣著相機(jī)與錄音胸囱,去河邊找鬼。 笑死瀑梗,一個(gè)胖子當(dāng)著我的面吹牛烹笔,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播抛丽,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼谤职,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了亿鲜?” 一聲冷哼從身側(cè)響起允蜈,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蒿柳,沒想到半個(gè)月后饶套,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡垒探,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年妓蛮,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片圾叼。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡仔引,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出褐奥,到底是詐尸還是另有隱情,我是刑警寧澤翘簇,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布撬码,位于F島的核電站,受9級(jí)特大地震影響版保,放射性物質(zhì)發(fā)生泄漏呜笑。R本人自食惡果不足惜夫否,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叫胁。 院中可真熱鬧凰慈,春花似錦、人聲如沸驼鹅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽输钩。三九已至豺型,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間买乃,已是汗流浹背姻氨。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留剪验,地道東北人瓷胧。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像措译,于是被迫代替她去往敵國和親败玉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355