ARouter的路由跳轉(zhuǎn)發(fā)生了什么事兒

在組件化的時(shí)候漾橙,組件與組件是高度解耦的远舅,但組件與組件之間需要通信,這不得不提到ARouter

說在前面

ARouter.png
// Cache route and metas
static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); // 添加注解后將映射關(guān)系添加到這個(gè)集合當(dāng)中
static Map<String, RouteMeta> routes = new HashMap<>(); // 跳轉(zhuǎn)某個(gè)路由后的緩存悟泵,包括常用的Activity,Framgent,清單文件的meta

// Cache provider
static Map<Class, IProvider> providers = new HashMap<>(); // 實(shí)現(xiàn)IProvider的注解后映射關(guān)系添加到這個(gè)集合中
static Map<String, RouteMeta> providersIndex = new HashMap<>(); // IProvider的緩存

// Cache interceptor
static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("More than one interceptors use same priority [%s]"); // 實(shí)現(xiàn)IInterceptor的注解映射關(guān)系添加到這個(gè)集合中
static List<IInterceptor> interceptors = new ArrayList<>(); // IInterceptor的緩存

分析目標(biāo)

若需要對Activity路由,只需要添加@Route闪水,如

@Route(path = "/test/activity2")

當(dāng)添加注解后糕非,會通過apt生成ARouter%Group%test的文件,里面包含有需要路由的相關(guān)的信息,包含一個(gè)RouteType枚舉類球榆,如

atlas.put("/test/activity2", RouteMeta.build(RouteType.ACTIVITY, Test2Activity.class, "/test/activity2", "test"));

RouteType是一個(gè)枚舉朽肥,枚舉了如下:

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

常見的有Activity,Fragment,接口實(shí)現(xiàn)類IProvider

案例分析

ARouter.init(getApplication())
ARouter.getInstance().build("/test/activity2").navigation();

這兩行代碼發(fā)生了什么事情

首先看init方法

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

hasInit默認(rèn)為false

protected static synchronized boolean init(Application application) {
    mContext = application;
    LogisticsCenter.init(mContext, executor);
    hasInit = true;
    mHandler = new Handler(Looper.getMainLooper());
    return true;
}

public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {
    if (registerByPlugin) {
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            ...
    }
}

我調(diào)試發(fā)現(xiàn)registerByPlugin = true持钉,只是打印了一個(gè)log
這里是將hasInit設(shè)置為true衡招,代表初始化成功了,并返回true

接著會調(diào)用_ARouter.afterInit() 這里是初始化一個(gè) PROVIDER每强,僅此而已

這里只是初始化了ARouter.init(getApplication()) 東西并不多

接著會路由跳轉(zhuǎn)始腾,在分析之前州刽,我先闡述下ARouter與_ARouter系,他們的關(guān)系是23種設(shè)計(jì)模式中的外觀模式浪箭,_ARouter是核心類穗椅,基本上所有的邏輯都在這里面,ARouter只是包了一層奶栖,做了下方法的傳遞

ARouter.getInstance().build("/test/activity2")

進(jìn)入到_ARouter.build()里

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

第5行是對path做處理匹表,由于沒有添加注解,pService=null,接著調(diào)用build()

protected Postcard build(String path, String group, Boolean afterReplace) {
    ...
    return new Postcard(path, group);
}

關(guān)鍵代碼只有 return new Postcard(path, group)宣鄙,而Postcard繼承的是RouteMeta,build()走完后桑孩,調(diào)用navigation()

public Object navigation(Context context, NavigationCallback callback) {
    return ARouter.getInstance().navigation(context, this, -1, callback);
}

由于ARouter和_ARouter是外觀設(shè)計(jì)模式,直接看_ARouter的navigation

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        // Pretreatment failed, navigation canceled.
        return null;
    }

    // Set context to postcard.
    postcard.setContext(null == context ? mContext : context);

    try {
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        ...
        return null;
    }

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

    if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
            @Override
            public void onContinue(Postcard postcard) {
                _navigation(postcard, requestCode, callback);
            }
            @Override
            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }

                logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
            }
        });
    } else {
        return _navigation(postcard, requestCode, callback);
    }

    return null;
}

第2框冀,3行代碼又是一個(gè)攔截流椒,由于沒有實(shí)現(xiàn),這里忽略明也,看第12行代碼LogisticsCenter.completion(postcard);

public synchronized static void completion(Postcard postcard) {
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        // Load route and cache it into memory, then delete from metas.
        addRouteGroupDynamic(postcard.getGroup(), null);

        completion(postcard);   // Reload
    } else {
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        switch (routeMeta.getType()) {
            case PROVIDER:  // if the route is provider, should find its instance
                // Its provider, so it must implement IProvider
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                IProvider instance = Warehouse.providers.get(providerMeta);
                if (null == instance) { // There's no instance of this provider
                    IProvider provider;
                    try {
                        provider = providerMeta.getConstructor().newInstance();
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        logger.error(TAG, "Init provider failed!", e);
                        throw new HandlerException("Init provider failed!");
                    }
                }
                postcard.setProvider(instance);
                postcard.greenChannel();    // Provider should skip all of interceptors
                break;
            case FRAGMENT:
                postcard.greenChannel();    // Fragment needn't interceptors
            default:
                break;
        }
    }
}

if 和 else里做的事情是宣虾,如果緩存中沒有指定的path(第一次調(diào)用group),則走if温数,去解析绣硝,else 則做賦值等操作,

public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) {
    if (Warehouse.groupsIndex.containsKey(groupName)){
        Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
        Warehouse.groupsIndex.remove(groupName);
    }

    // cover old group.
    if (null != group) {
        group.loadInto(Warehouse.routes);
    }
}

第3行是通過group在groupsIndex中獲取class撑刺,groupsIndex是@Route注解后添加到集合的鹉胖,通過反射獲取對象后,將相同的group放到routes集合里够傍,放到routes后再調(diào)用completion甫菠,則走 else

postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());

        switch (routeMeta.getType()) {
            case PROVIDER:  // if the route is provider, should find its instance
                // Its provider, so it must implement IProvider
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                IProvider instance = Warehouse.providers.get(providerMeta);
                if (null == instance) { // There's no instance of this provider
                    IProvider provider;
                    try {
                        provider = providerMeta.getConstructor().newInstance();
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider);
                        instance = provider;
                    } catch (Exception e) {
                        logger.error(TAG, "Init provider failed!", e);
                        throw new HandlerException("Init provider failed!");
                    }
                }
                // 將實(shí)例對象放在postcard中
                postcard.setProvider(instance); 
                postcard.greenChannel(); 
                break;
            case FRAGMENT:
                postcard.greenChannel();
            default:
                break;
        }

這里做了一些賦值操作,如果是PROVIDER且providers緩存集合里沒有時(shí)冕屯,通過返回獲取實(shí)例以后會調(diào)用init()方法寂诱,并將對象放到providers緩存集合中

至此,Postcard初始化完成

if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
            @Override
            public void onContinue(Postcard postcard) {
                _navigation(postcard, requestCode, callback);
            }
            @Override
            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }

                logger.info(Consts.TAG, "Navigation failed, termination by interceptor : " + exception.getMessage());
            }
        });
    } else {
        return _navigation(postcard, requestCode, callback);
    }

這里是調(diào)用了攔截器安聘,若注解了攔截器痰洒,會先走攔截器里實(shí)現(xiàn)的邏輯,在調(diào)用onContinue方法的_navigation方法

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = postcard.getContext();

    switch (postcard.getType()) {
        case ACTIVITY:
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());
            ...
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });

            break;
        case PROVIDER:
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
            Class<?> fragmentMeta = postcard.getDestination();
            try {
                Object instance = fragmentMeta.getConstructor().newInstance();
                if (instance instanceof Fragment) {
                    ((Fragment) instance).setArguments(postcard.getExtras());
                } else if (instance instanceof android.support.v4.app.Fragment) {
                    ((android.support.v4.app.Fragment) instance).setArguments(postcard.getExtras());
                }

                return instance;
            } catch (Exception ex) {
                logger.error(Consts.TAG, "Fetch fragment instance error, " + TextUtils.formatStackTrace(ex.getStackTrace()));
            }
        case METHOD:
        case SERVICE:
        default:
            return null;
    }

    return null;
}

若是ACTIVITY,則跳轉(zhuǎn)到activity,
若是PROVIDER,則返回IProvider對象褂痰,
若是BOARDCAST,CONTENT_PROVIDER泉粉,F(xiàn)RAGMENT,則返回Fragment對象

總結(jié)

若是路由的是Activity舍肠,則直接跳轉(zhuǎn)到指定的Activity,若是其他的搀继,則獲取實(shí)例后進(jìn)行其他邏輯的處理

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市翠语,隨后出現(xiàn)的幾起案子叽躯,更是在濱河造成了極大的恐慌,老刑警劉巖肌括,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件点骑,死亡現(xiàn)場離奇詭異,居然都是意外死亡谍夭,警方通過查閱死者的電腦和手機(jī)黑滴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來紧索,“玉大人袁辈,你說我怎么就攤上這事≈槠” “怎么了晚缩?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長媳危。 經(jīng)常有香客問我荞彼,道長,這世上最難降的妖魔是什么待笑? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任鸣皂,我火速辦了婚禮,結(jié)果婚禮上暮蹂,老公的妹妹穿的比我還像新娘寞缝。我一直安慰自己,他們只是感情好仰泻,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布第租。 她就那樣靜靜地躺著,像睡著了一般我纪。 火紅的嫁衣襯著肌膚如雪慎宾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天浅悉,我揣著相機(jī)與錄音趟据,去河邊找鬼。 笑死术健,一個(gè)胖子當(dāng)著我的面吹牛汹碱,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播荞估,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼咳促,長吁一口氣:“原來是場噩夢啊……” “哼稚新!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起跪腹,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤褂删,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后冲茸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體屯阀,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年轴术,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了难衰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡逗栽,死狀恐怖盖袭,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情彼宠,我是刑警寧澤苍凛,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站兵志,受9級特大地震影響醇蝴,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜想罕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一悠栓、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧按价,春花似錦惭适、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至框产,卻和暖如春凄杯,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秉宿。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工戒突, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人描睦。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓膊存,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子隔崎,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353

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