“終于懂了” 系列:組件化框架 ARouter 完全解析(一) 原理詳解

前言

在我之前的組件化文章《“終于懂了” 系列:Android組件化,全面掌握!》中,提到為了實(shí)現(xiàn)組件化要解決的幾個(gè)問題點(diǎn),其中 頁(yè)面跳轉(zhuǎn)組件間通信 的問題是使用了 ARouter 這個(gè)框架來解決的。ARouter確實(shí)是專門用于做組件化改造,官方是這么介紹的:

一個(gè)用于幫助 Android App 進(jìn)行組件化改造的框架 —— 支持模塊間的路由、通信、解耦

關(guān)于組件化的知識(shí)已在上面文章中全面介紹了。那么是時(shí)候?qū)?ARouter 這個(gè)強(qiáng)大的框架做一個(gè)解析了:它是如何做到 頁(yè)面跳轉(zhuǎn)、組件間通信 的?我們能從ARrouter中學(xué)到哪些東西?

而當(dāng)時(shí)也確實(shí)承諾了有機(jī)會(huì)要寫一篇ARouter的文章:

ARouter還有很多進(jìn)階用法,有機(jī)會(huì)我也針對(duì)ARouter寫一篇全面分析

由于內(nèi)容預(yù)計(jì)較多,分為三篇,分別介紹 ARouter的實(shí)現(xiàn)原理、框架使用到的相關(guān)通用技術(shù):

  • “終于懂了” 系列:組件化框架 ARouter 完全解析(一)原理全解
  • “終于懂了” 系列:組件化框架 ARouter 完全解析(二)APT—幫助類生成
  • “終于懂了” 系列:組件化框架 ARouter 完全解析(二)AGP/Transform—?jiǎng)討B(tài)代碼注入

本篇就先梳理ARouter的實(shí)現(xiàn)原理,看看它是如何做到跨組件跳轉(zhuǎn)頁(yè)面、獲取服務(wù)。

感謝繼續(xù)關(guān)注和支持~

一、路由認(rèn)知

ARouter從命名即可知,這是一個(gè)路由框架。那么路由是個(gè)啥呢?

路由routing)就是通過互聯(lián)的網(wǎng)絡(luò)把信息源地址傳輸?shù)?strong>目的地址的活動(dòng)。-- 百科
可見 路由 是個(gè)動(dòng)詞,這是網(wǎng)絡(luò)傳輸中的概念,完成路由這個(gè)操作的實(shí)體設(shè)備就是 路由器(Router)。

另外,生活中的 信件郵寄 也可以理解為一個(gè) 路由過程:郵局把信件從郵寄方 傳輸?shù)浇邮杖说氖稚稀J紫?郵寄方 和 接收人 是無法接觸的(無耦合依賴),只能通過 郵局這個(gè)第三方 完成郵寄;郵局根據(jù)信封上的地址,例如 “深圳市 深圳大學(xué)粵海校區(qū)”,決定分發(fā)到 開往深圳的車上,然后深圳的郵遞員找到 深圳大學(xué)粵海校區(qū) 對(duì)應(yīng)的 "南山區(qū)南海大道3688號(hào)”,最終找到接收人。

image.png

對(duì)應(yīng)地, ARouter 也是個(gè)“路由器”,也是個(gè)“郵政系統(tǒng)”。通行根據(jù)組件化介紹的,ARouter 幫助 無相互依賴的組件間 進(jìn)行跳轉(zhuǎn)和通信。

抽象一下,郵局、ARouter 都是 路由系統(tǒng) ——— 給 無依賴的雙方 提供 通信和路由的能力。

官方文檔 有詳細(xì)的引入和各種功能使用介紹,包括基礎(chǔ)使用步驟、參數(shù)解析、攔截器、服務(wù)獲取等進(jìn)階用法。這里不再搬運(yùn)。

二、原理解析

使用ARouter在進(jìn)行Activity跳轉(zhuǎn)非常簡(jiǎn)單:初始化ARouter、添加注解@Route、發(fā)起路由。

// 在module app中
//1.初始化SDK
ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化
// moduleA
// 2.在支持路由的頁(yè)面上添加注解(必選)
// 路徑注意至少需有兩級(jí),/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
    ...
}
// moduleB(沒有依賴 moduleA)
// 3.發(fā)起路由跳轉(zhuǎn)
ARouter.getInstance().build("/test/activity").navigation();

這樣就使得 沒有依賴moduleA的moduleB能跳轉(zhuǎn)到moduleA的Activity了。服務(wù)獲取也是類似簡(jiǎn)單的代碼就可實(shí)現(xiàn)。

那么 ARouter 是如何做到只通過簡(jiǎn)單2步 就完成 解耦組件間的路由操作呢?我們通過源碼一步步理解。

2.1 構(gòu)建PostCard

我們知道 想要跳轉(zhuǎn)Activity最終必定是走到了 startActivity(intent)方法,而intent是一般需要目標(biāo)Activity的Class。所以我們猜想 navigation()中應(yīng)該是有尋找目標(biāo)Activity的Class這一過程的。

下面就來跟蹤源碼分析這一過程。先看ARouter.getInstance():

public static ARouter getInstance() {
    if (!hasInit) { // 未初始化則報(bào)異常
        throw new InitException("ARouter::Init::Invoke init(context) first!");
    } else {
        if (instance == null) {
            synchronized (ARouter.class) {
                if (instance == null) {
                    instance = new ARouter();
                }
            }
        }
        return instance;
    }
}

獲取ARouter單實(shí)例,沒有初始化則報(bào)異常。再看它的build(string)方法:

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

這里是調(diào)用了 _ARouter 的同名方法,返回了 Postcard(意為明信片)。ARouter實(shí)際是使用了外觀模式(設(shè)計(jì)模式的一種),其所有方法都是調(diào)用了_ARouter的同名方法。 進(jìn)入_ARouter:

protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) { //path不能為空
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        //path替換,這是預(yù)處理
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        if (null != pService) {
            path = pService.forString(path);
        }
        return build(path, extractGroup(path), true);
    }
}

這里對(duì)path做了空校驗(yàn)和預(yù)處理替換。如果想重寫path,可以寫一個(gè)PathReplaceService實(shí)現(xiàn)類。接著又走到重載方法:

protected Postcard build(String path, String group, Boolean afterReplace) {
    if (TextUtils.isEmpty(path) || TextUtils.isEmpty(group)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        ...
        return new Postcard(path, group);
    }
}

其中參數(shù)group是通過extractGroup(path)獲取,也就是path的第一級(jí),即"/test/activity"中的"test"。group的作用是作為路由的默認(rèn)分組。

路由中的分組概念:

  • SDK中針對(duì)所有的路徑(/test/1、/test/2)進(jìn)行分組,分組只有在分組中的某一個(gè)路徑第一次被訪問的時(shí)候,該分組才會(huì)被初始化
  • 可以通過 @Route 注解主動(dòng)指定分組,否則使用路徑中第一段字符串(/*/)作為分組
  • 注意:一旦主動(dòng)指定分組之后,應(yīng)用內(nèi)路由需要使用 ARouter.getInstance().build(path, group) 進(jìn)行跳轉(zhuǎn),手動(dòng)指定分組,否則無法找到
    @Route(path = "/test/1", group = "app")

最后返回創(chuàng)建的Postcard實(shí)例。Postcard是明信片的意思,承載了一次跳轉(zhuǎn)/路由 需要的所有信息,它繼承自路由元信息 RouteMeta

public final class Postcard extends RouteMeta {

    // Base
    private Uri uri;                //使用Uri方式發(fā)起的路由
    private Object tag;             // A tag prepare for some thing wrong. inner params, DO NOT USE!
    private Bundle mBundle;         //啟動(dòng)activity的傳入的Bundle
    private int flags = 0;         // 啟動(dòng)activity的Flags
    private int timeout = 300;      // 路由超時(shí)時(shí)間
    private IProvider provider;     // 如果是獲取provider,就有值
    private boolean greenChannel;  //是綠色通道
    private SerializationService serializationService;
    private Context context;        //context
    private String action;          //啟動(dòng)activity的action
    
    // activity轉(zhuǎn)場(chǎng)動(dòng)畫相關(guān)
    private Bundle optionsCompat;
    private int enterAnim = -1;
    private int exitAnim = -1;
    
    public Postcard(String path, String group) {
        this(path, group, null, null);
    }

    public Postcard(String path, String group, Uri uri, Bundle bundle) {
        setPath(path);
        setGroup(group);
        setUri(uri);
        this.mBundle = (null == bundle ? new Bundle() : bundle);
    }
    ...
}
public class RouteMeta {
    private RouteType type;         // 路由類型;activity、service、fragment、IProvider等,編譯時(shí)會(huì)根據(jù)被@Route注解的類的類型來設(shè)置
    private Element rawType;        // 路由原始類型,在編譯時(shí)用來判斷
    private Class<?> destination;   // 目的地:具體的 XxxActivity.class等
    private String path;            // Path
    private String group;           // Group
    private int priority = -1;      // 優(yōu)先級(jí),越小優(yōu)先級(jí)越高
    private int extra;              // Extra
    private Map<String, Integer> paramsType;  // 參數(shù)類型,例如activity中使用@Autowired的參數(shù)類型
    private String name; //路由名字,用于生成javadoc
    private Map<String, Autowired> injectConfig;  // 參數(shù)配置(對(duì)應(yīng)paramsType).
}
  • Postcard:路由的信息。 理解為是生活中的明信片。繼承自RouteMeta。例如Postcard中的mBundle等則是 明信片上寄件人寫的 “你好,xxx 節(jié)日快樂!” 這種文字內(nèi)容。
  • RouteMeta:路由元信息,即基礎(chǔ)信息。 理解就是 明信片上的 收件人地址 這種必備的基礎(chǔ)信息。 明信片上可以沒有文字內(nèi)容,但想要被郵寄就必須有收件人地址

2.2 路由過程

2.2.1 整體步驟

通過path構(gòu)建了PostCard后調(diào)用了其navigation()方法,也就是開始了路由過程:

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

看到連續(xù)走了兩個(gè)重載方法,最后走到ARouter的navigation方法,并且把自己傳了進(jìn)去。ARouter的navigation方法同樣會(huì)走到_ARouter的同名方法:

 // @param context     Activity or null.
 // @param postcard    Route metas
 // @param requestCode RequestCode,startActivity的requestCode
 // @param callback    cb,路由回調(diào):找到目的地、未找到、中斷、到達(dá)
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    //若有PretreatmentService的實(shí)現(xiàn),就進(jìn)行預(yù)處理。可以在真正路由前進(jìn)行一些判斷然后中斷路由。
    PretreatmentService pretreatmentService = ARouter.getInstance().navigation(PretreatmentService.class);
    if (null != pretreatmentService && !pretreatmentService.onPretreatment(context, postcard)) {
        // 預(yù)處理失返回false,路由取消.
        return null;
    }

    // 給路由設(shè)置context,例如啟動(dòng)activity需要。如果沒有傳就使用mContext,即初始化ARouter時(shí)傳入的Application
    postcard.setContext(null == context ? mContext : context);

    try {
        // 1. 完善postcard信息(目前只有path、group,還需要知道具體目的地,例如要跳轉(zhuǎn)到的activity信息)
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        //這里就是debug包中,沒找到路由目的地時(shí) 經(jīng)常出現(xiàn)的提示
        if (debuggable()) {
            runInMainThread(new Runnable() {
                public void run() {
                    Toast.makeText(mContext, "There's no route matched!\n" +" Path = [" + postcard.getPath() + "]\n" +
                            " Group = [" + postcard.getGroup() + "]", Toast.LENGTH_LONG).show();
                }
            });
        }
        //沒找到的回調(diào)
        if (null != callback) {
            callback.onLost(postcard);
        } else {
            // 沒有callback的話, (如果有)就回調(diào)到降低服務(wù) 
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }
    //找到的回調(diào)
    if (null != callback) {
        callback.onFound(postcard);
    }
    // 2. 不是綠色通道的話,要先走攔截器
    if (!postcard.isGreenChannel()) {
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
            
            //攔截器處理結(jié)果:繼續(xù)路由
            @Override
            public void onContinue(Postcard postcard) {
                // 3. 獲取路由結(jié)果
                _navigation(postcard, requestCode, callback);
            }

             //攔截器處理結(jié)果:中斷路由,回調(diào)中斷
            @Override
            public void onInterrupt(Throwable exception) {
                if (null != callback) {
                    callback.onInterrupt(postcard);
                }
            }
        });
    } else {
        //綠色通道,不走攔截器,就獲取路由結(jié)果
        return _navigation(postcard, requestCode, callback);
    }

    return null;
}

使用前面構(gòu)建好的PastCard經(jīng)過整體3個(gè)步驟,就完成了路由:

  1. 完善postcard信息:只有path、group還不行,還需要知道具體目的地,例如要跳轉(zhuǎn)到的Activity信息。
  2. 攔截器處理:不是綠色通道的話,要先經(jīng)過路由器的處理,結(jié)果是中斷或者繼續(xù)。
  3. 獲取路由結(jié)果:例如進(jìn)行目標(biāo)Activity的跳轉(zhuǎn)、獲取IProvider實(shí)現(xiàn)對(duì)象、獲取Fragment等。

2.2.2 獲取路由結(jié)果

先來看比較簡(jiǎn)單的最后一個(gè)步驟——路由結(jié)果獲取過程,也就是_navigation()方法:

private Object _navigation(final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    final Context currentContext = postcard.getContext();
    //根據(jù)路由類型來走對(duì)應(yīng)邏輯
    switch (postcard.getType()) {
        case ACTIVITY:
            // Activity, 使用postcard.getDestination()來構(gòu)建intent、傳入Extras、設(shè)置 flags、action
            final Intent intent = new Intent(currentContext, postcard.getDestination());
            intent.putExtras(postcard.getExtras());
            int flags = postcard.getFlags();
            if (0 != flags) {
                intent.setFlags(flags);
            }
            // 當(dāng)前的context不是activity,需要添加flag:FLAG_ACTIVITY_NEW_TASK
            //(啟動(dòng)startActivity需有任務(wù)棧的,application/service啟動(dòng)activity沒有任務(wù)棧,所以必須要new_task_flag新建一個(gè)任務(wù)棧)
            if (!(currentContext instanceof Activity)) {
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            }

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

            // 最后在主線程執(zhí)行 熟悉的startActivity,
            runInMainThread(new Runnable() {
                @Override
                public void run() {
                    startActivity(requestCode, currentContext, intent, postcard, callback);
                }
            });
            break;
        case PROVIDER:
            //provider,指的是想要獲取的服務(wù),即IProvider的實(shí)現(xiàn)類。直接從postCard中獲取。
            return postcard.getProvider();
        case BOARDCAST:
        case CONTENT_PROVIDER:
        case FRAGMENT:
        //Broadcast、ContentProvider、Fragment,都是使用postcard.getDestination()反射創(chuàng)建實(shí)例
            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;
            } ...
    }
    return null;
}

從上面可見,postcard 經(jīng)過完善后,路由類型type、目的地destination等都已經(jīng)被賦了值。destination就是目標(biāo)類的class對(duì)象。

方法內(nèi)容就是根據(jù)路由類型來走對(duì)應(yīng)邏輯:

  • Activity, 使用postcard.getDestination()來構(gòu)建intent、傳入Extras、設(shè)置 flags、action,最后在主線程執(zhí)行 熟悉的startActivity
  • provider,指的是想要獲取的服務(wù),即IProvider的實(shí)現(xiàn)類。是直接從postCard中獲取的,因?yàn)榉?wù)類是單例,只會(huì)在首次獲取時(shí)創(chuàng)建()。
  • Broadcast、ContentProvider、Fragment,都是使用postcard.getDestination()反射創(chuàng)建實(shí)例

整體邏輯還是比較簡(jiǎn)單的。這里你可能好奇 destination的值是如何獲取的,因?yàn)闊o論哪種類型的路由,都是要使用目標(biāo)class,這個(gè)就是ARouter最為核心的內(nèi)容——如何獲取 無直接依賴的模塊的 class對(duì)象,也就是完善postcard信息的過程。 不過我們先來把攔截器邏輯分析完,最后再來看這個(gè)核心點(diǎn)。

2.2.3 攔截器

攔截器模式是開發(fā)中常用設(shè)計(jì)模式之一,路由中也可以設(shè)置攔截器,對(duì)路徑進(jìn)行判斷決定是否需要中斷。

未設(shè)置綠色通道的路由需要經(jīng)過攔截器處理,也就是interceptorService的doInterceptions()方法。interceptorService是啥呢?

final class ARouter {
    ...
    private static InterceptorService interceptorService;
    ...
    //ARouter的初始化方法
    public static void init(Application application) {
        if (!hasInit) {
            logger = _ARouter.logger;
            hasInit = _ARouter.init(application);
            if (hasInit) {
                _ARouter.afterInit();
            }
        }
    }
   ...
}
    //_ARouter.java
    static void afterInit() {
        interceptorService = (InterceptorService) ARouter.getInstance().build("/arouter/service/interceptor").navigation();
    }

InterceptorService繼承IProvider,可見interceptorService也是一個(gè)服務(wù),在ARouter初始化后 獲取,用于處理攔截器的邏輯。具體的實(shí)現(xiàn)類是InterceptorServiceImpl:

@Route(path = "/arouter/service/interceptor")
public class InterceptorServiceImpl implements InterceptorService {
...
    @Override
    public void doInterceptions(final Postcard postcard, final InterceptorCallback callback) {
        //有攔截器
        if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
            ...
            LogisticsCenter.executor.execute(new Runnable() { //放入線程池異步執(zhí)行
                @Override
                public void run() { //interceptorCounter 用于保證所有攔截器都走完,并且設(shè)置了超時(shí)
                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                    try {//執(zhí)行第一個(gè)攔截器,如果沒有中斷 則遞歸調(diào)用繼續(xù)后面的攔截器
                        _execute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                        if (interceptorCounter.getCount() > 0) {    // count>0說明超時(shí)了,攔截器還沒處理完.
                            callback.onInterrupt(new HandlerException("The interceptor processing timed out."));
                        } else if (null != postcard.getTag()) {    //Tag!=null說明被某個(gè)攔截器回調(diào)中斷了
                            callback.onInterrupt((Throwable) postcard.getTag());
                        } else {
                            callback.onContinue(postcard); // 所有攔截器處理完、沒超時(shí)、也沒異常,則繼續(xù)路由
                        }
                    }...
                }
            });
        } else {
            //沒有攔截器則繼續(xù)路由
            callback.onContinue(postcard);
        }
    }

    private static void _execute(final int index, final CancelableCountDownLatch counter, final Postcard postcard) {
        if (index < Warehouse.interceptors.size()) {
            //從Warehouse.interceptors中獲取第index個(gè)攔截器,走process方法,如果回調(diào)到onContinue就繼續(xù)下一個(gè);
            IInterceptor iInterceptor = Warehouse.interceptors.get(index);
            iInterceptor.process(postcard, new InterceptorCallback() {
                @Override
                public void onContinue(Postcard postcard) {
                    counter.countDown();
                    _execute(index + 1, counter, postcard);  // 繼續(xù)下一個(gè)
                }
                @Override
                public void onInterrupt(Throwable exception) {
                    postcard.setTag(null == exception ? new HandlerException("No message.") : exception);    // save the exception message for backup.
                    counter.cancel();
                    ...
                }
            });
        }
    }
    
    @Override //此init方法會(huì)在服務(wù)被創(chuàng)建后調(diào)用。這里就是反射創(chuàng)建所有的攔截器實(shí)例
    public void init(final Context context) {
        LogisticsCenter.executor.execute(new Runnable() {
            @Override
            public void run() {
                if (MapUtils.isNotEmpty(Warehouse.interceptorsIndex)) {
                    //遍歷Warehouse.interceptorsIndex ,使用存儲(chǔ)與其中的攔截器class對(duì)象反射創(chuàng)建攔截器實(shí)例
                    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
                            Warehouse.interceptors.add(iInterceptor);
                        }...
                    }
                    interceptorHasInit = true;
                    ...
                }
            }
        });
    }...
}

doInterceptions()方法中判斷如果有攔截器,就放入線程池異步執(zhí)行第一個(gè)攔截器,且使用interceptorCounter 保證所有攔截器都走完,同時(shí)也設(shè)置了超時(shí)。 如果第一個(gè)攔截器沒有回調(diào)中斷 則遞歸調(diào)用繼續(xù)后面的攔截器。

攔截器的執(zhí)行,是從Warehouse.interceptors中獲取第index個(gè)攔截器,走process方法,如果回調(diào)到onContinue就繼續(xù)下一個(gè);若回調(diào)onInterrupt就中斷路由。

攔截器的執(zhí)行邏輯還是比較清晰的。那么攔截器是怎么獲取的呢?我們來看下InterceptorServiceImpl的init方法:init()方法會(huì)在服務(wù)被創(chuàng)建后立即調(diào)用,如上所示就是遍歷Warehouse.interceptorsIndex ,使用存儲(chǔ)在其中的攔截器class對(duì)象 反射創(chuàng)建攔截器實(shí)例,然后存在存入 Warehouse.interceptors。 也即是說,ARouter初始化完成后就獲取到了所有攔截器實(shí)例

那么Warehouse又是啥呢?interceptorsIndex是如何存儲(chǔ)的所有攔截器的class的?

2.2.4 路由元信息的收集

Warehouse意為倉(cāng)庫(kù),用于存放被 @Route、@Interceptor注釋的 路由相關(guān)的信息,也就是我們關(guān)注的destination等信息。既然是倉(cāng)庫(kù),那么就是有存有取

前面舉的例子:moduleB發(fā)起路由跳轉(zhuǎn)到moduleA的activity,moduleB沒有依賴moduleA,只是在moduleA的activity上增加了@Route注解。 由于進(jìn)行activity跳轉(zhuǎn)需要目標(biāo)Activity的class對(duì)象來構(gòu)建intent,所以必須有一個(gè)中間人,把路徑"/test/activity"翻譯成Activity的class對(duì)象,然后moduleB才能實(shí)現(xiàn)跳轉(zhuǎn)。(因此在ARouter的使用中 moduleA、moduleB 都是需要依賴 arouter-api的)

這個(gè)中間人那就是ARouter了,而這個(gè)翻譯工所作用到的詞典就是 Warehouse,它存著所有路由信息。

class Warehouse {
    //所有IRouteGroup實(shí)現(xiàn)類的class對(duì)象,是在ARouter初始化中賦值,key是path第一級(jí)
    //(IRouteGroup實(shí)現(xiàn)類是編譯時(shí)生成,代表一個(gè)組,即path第一級(jí)相同的所有路由,包括Activity和Provider服務(wù))
    static Map<String, Class<? extends IRouteGroup>> groupsIndex = new HashMap<>(); 
    //所有路由元信息,是在completion中賦值,key是path
    //首次進(jìn)行某個(gè)路由時(shí)就會(huì)加載整個(gè)group的路由,即IRouteGroup實(shí)現(xiàn)類中所有路由信息。包括Activity和Provider服務(wù)
    static Map<String, RouteMeta> routes = new HashMap<>();
    
    //所有服務(wù)provider實(shí)例,在completion中賦值,key是IProvider實(shí)現(xiàn)類的class
    static Map<Class, IProvider> providers = new HashMap<>();
    //所有provider服務(wù)的元信息(實(shí)現(xiàn)類的class對(duì)象),是在ARouter初始化中賦值,key是IProvider實(shí)現(xiàn)類的全類名。
    //主要用于使用IProvider實(shí)現(xiàn)類的class發(fā)起的獲取服務(wù)的路由,例如ARouter.getInstance().navigation(HelloService.class)
    static Map<String, RouteMeta> providersIndex = new HashMap<>();
    
    //所有攔截器實(shí)現(xiàn)類的class對(duì)象,是在ARouter初始化時(shí)收集到,key是優(yōu)先級(jí)
    static Map<Integer, Class<? extends IInterceptor>> interceptorsIndex = new UniqueKeyTreeMap<>("...");
    //所有攔截器實(shí)例,是在ARouter初始化完成后立即創(chuàng)建
    static List<IInterceptor> interceptors = new ArrayList<>();
...
}

Warehouse存了哪些信息呢?

  • groupsIndex所有路由組元信息。是所有IRouteGroup實(shí)現(xiàn)類的class對(duì)象,是在ARouter初始化中賦值,key是path第一級(jí)。IRouteGroup實(shí)現(xiàn)類是編譯時(shí)生成,代表一個(gè)組,即path第一級(jí)相同的所有路由,包括Activity和Provider服務(wù))。
  • routes所有路由元信息。是在LogisticsCenter.completion中賦值,key是path。首次進(jìn)行某個(gè)路由時(shí)就會(huì)加載整個(gè)group的路由,即IRouteGroup實(shí)現(xiàn)類中所有路由信息。包括Activity和Provider服務(wù)
  • providers所有服務(wù)provider實(shí)例。在LogisticsCenter.completion中賦值,key是IProvider實(shí)現(xiàn)類的class
  • providersIndex所有provider服務(wù)元信息(實(shí)現(xiàn)類的class對(duì)象)。是在ARouter初始化中賦值,key是IProvider實(shí)現(xiàn)類的全類名。用于使用IProvider實(shí)現(xiàn)類class發(fā)起的獲取服務(wù)的路由,例如ARouter.getInstance().navigation(HelloService.class)
  • interceptorsIndex所有攔截器實(shí)現(xiàn)類class對(duì)象。是在ARouter初始化時(shí)收集到,key是優(yōu)先級(jí)
  • interceptors所有攔截器實(shí)例。是在ARouter初始化完成后立即創(chuàng)建

其中g(shù)roupsIndex、providersIndex、interceptorsIndex是ARouter初始化時(shí)就準(zhǔn)備好的基礎(chǔ)信息,為業(yè)務(wù)中隨時(shí)發(fā)起路由操作(Activity跳轉(zhuǎn)、服務(wù)獲取、攔截器處理)做好準(zhǔn)備。

那么Warehouse的信息是如何收集到的呢? 下面就先來看下ARouter初始化具體做了哪些事情:

final class _ARouter {
    ...
    protected static synchronized boolean init(Application application) {
        mContext = application;
        LogisticsCenter.init(mContext, executor);
        ...
        return true;
    }

_ARouter的init方法中 調(diào)用了LogisticsCenter的init方法。LogisticsCenter意為物流中心,上面提到的完善postcard的completion操作也是此類提供。

//LogisticsCenter.java
//LogisticsCenter初始化,加載所有的路由元信息
public synchronized static void init(Context context, ThreadPoolExecutor tpe) throws HandlerException {...
    try {
        long startInit = System.currentTimeMillis();
        //先嘗試使用AGP transform 收集 根幫助類 后 寫好的注入代碼(要先引入插件才行 apply plugin: 'com.alibaba.arouter')
        loadRouterMap();
        if (registerByPlugin) {
            //registerByPlugin為true說明使用AGP加載ok了(通常都會(huì)用AGP,即registerByPlugin為true)
            logger.info(TAG, "Load router map by arouter-auto-register plugin.");
        } else {
            //若沒有使用 AGP transform,就用ClassUtils.getFileNameByPackageName來搜集dex中ROUTE_ROOT_PAKCAGE包下的所有類,即編譯時(shí)生成的所有幫助類
            //這樣的話,就是運(yùn)行時(shí) 遍歷搜集 會(huì)比較耗時(shí),也就是init會(huì)較為耗時(shí);而AGP transform 是在編譯時(shí)完成收集的。
            //當(dāng)前app是新安裝時(shí)才會(huì)走(收集到的幫助類會(huì)緩存到SP文件)
            Set<String> routerMap;
            if (ARouter.debuggable() || PackageUtils.isNewVersion(context)) {
                logger.info(TAG, "Run with debug mode or new install, rebuild router map.");
                // 這寫幫助類是在編譯時(shí)由arouter-compiler生成
                routerMap = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);
                if (!routerMap.isEmpty()) {
                    context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).edit().putStringSet(AROUTER_SP_KEY_MAP, routerMap).apply();
                }
                PackageUtils.updateVersion(context);
            } else {
                //不是新安裝的版本,就從SP文件中讀取
                routerMap = new HashSet<>(context.getSharedPreferences(AROUTER_SP_CACHE_KEY, Context.MODE_PRIVATE).getStringSet(AROUTER_SP_KEY_MAP, new HashSet<String>()));
            }...
            //遍歷幫助類,區(qū)分是哪種幫助類,然后反射創(chuàng)建幫助類實(shí)例后,調(diào)用其loadInto方法來填充Warehouse相應(yīng)的Map
            for (String className : routerMap) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    //類名開頭:com.alibaba.android.arouter.routes.ARouter$$Root
                    //填充Warehouse.groupsIndex,即所有IRouteGroup實(shí)現(xiàn)類的class對(duì)象
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    //類名開頭:com.alibaba.android.arouter.routes.ARouter$$Interceptors
                    //填充Warehouse.interceptorsIndex,即所有IInterceptor實(shí)現(xiàn)類的class對(duì)象
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    //類名開頭:com.alibaba.android.arouter.routes.ARouter$$Providers
                    //填充Warehouse.providersIndex,即所有provider的RouteMeta
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
        }...
    }
}

LogisticsCenter初始化 就是加載所有的路由元信息的過程,有兩種方式:

  1. 走loadRouterMap()方法:直接使用在編譯時(shí)收集好的幫助類信息,然后反射創(chuàng)建幫助類實(shí)例后,調(diào)用其loadInto方法來填充Warehouse相應(yīng)的Map。這需要開發(fā)者要先引入插件才行 apply plugin: 'com.alibaba.arouter'。如果加載成功,registerByPlugin這個(gè)值就為true,否則false。
  2. 若第一步?jīng)]有加載成功,即registerByPlugin為false:就會(huì)使用ClassUtils.getFileNameByPackageName在運(yùn)行時(shí)搜集dex中"com.alibaba.android.arouter.routes"包下的所有類(即幫助類),然后遍歷,區(qū)分是哪種幫助類。接著反射創(chuàng)建幫助類實(shí)例后,調(diào)用其loadInto方法來填充Warehouse相應(yīng)的Map。

兩種方式對(duì)比:

  • 相同點(diǎn):都是使用幫助類信息反射創(chuàng)建幫助類實(shí)例后,調(diào)用其loadInto方法來填充Warehouse相應(yīng)的Map。
  • 不同點(diǎn):在于幫助類信息的收集方式。前者是在編譯時(shí)搜集就完畢了,后者是運(yùn)行時(shí)。

一般都是使用第一種,因?yàn)?strong>運(yùn)行時(shí)遍歷dex搜集會(huì)比較耗時(shí),而第一種在編譯時(shí)已經(jīng)收集好了。 第一種方式的loadRouterMap()方法的實(shí)現(xiàn)邏輯 我們稍后再看。

先看兩個(gè)問題:

  • 上面提到的幫助類是個(gè)啥? 如何使用幫助類填充Warehouse的?
  • 為啥幫助類還要收集?還分 編譯時(shí)收集、運(yùn)行時(shí)收集?

2.2.4.1 攔截器元信息

我們先來看攔截器元信息(攔截器class信息)是如何通過幫助類填充的:

image.png

上圖是ARouter工程編譯后module-java的build目錄,ARouter$$ 開頭的這些類都是在ARouter在編譯過程中生成,它們就是所謂的幫助類:

ARouter$$Interceptors$$modulejava 這個(gè)類就是一個(gè)幫助類,幫助WareHouse填充WareHouse.interceptorsIndex。它實(shí)現(xiàn)接口IInterceptorGroup,loadInfo方法接受一個(gè)Map<Integer, Class<? extends IInterceptor>>,也就是WareHouse.interceptorsIndex的類型。loadInfo方法體內(nèi),是用接收的map來put當(dāng)前module所有攔截器的class,即使用 @Interceptor 注解并實(shí)現(xiàn) IInterceptor 接口的類。

在上面LogisticsCenter的init方法中第二種加載方式中看到,確實(shí)是遍歷收集到的幫助類,然后使用類名判斷是 ARouter$$Interceptors$$modulejava,接著就調(diào)用loadInfo方法,這就實(shí)現(xiàn)了對(duì)WareHouse.interceptorsIndex的賦值。 也就是說,有了ARouter$$Interceptors$$modulejava ,我們就能在ARouter初始化時(shí)對(duì)WareHouse.interceptorsIndex進(jìn)行賦值,就為創(chuàng)建所有攔截器實(shí)例做好了準(zhǔn)備。

那么到這里,關(guān)于攔截器還有一個(gè)問題,ARouter$$Interceptors$$modulejava的loadInfo方法中 攔截器實(shí)現(xiàn)類class是如何獲取的呢?—— 當(dāng)然是編譯時(shí)對(duì)注解 @Interceptor 的解析,解析過程將在下篇中介紹。

攔截器幫助類我們看完了,再來看看其他三種幫助類。

2.2.4.2 路由組元信息

路由組元信息的收集是通過 —— ARouter$$Root$$xxx —— 根幫助類:即用來幫助對(duì) WareHouse.groupsIndex 賦值。這樣就會(huì)把path第一級(jí)相同的所以路由分到同一個(gè)組中。 一個(gè)module對(duì)應(yīng)一個(gè)根幫助類。xxx是module名,就是在build.gradle中配置的 AROUTER_MODULE_NAME 。

image.png

如上圖,ARouter$$Root$$modulejava 就是根幫助類,幫助WareHouse填充Warehouse.groupsIndex。實(shí)現(xiàn)自IRouteRoot接口,loadInfo方法接受一個(gè)Map<String, Class<? extends IRouteGroup>>,也就是WareHouse.groupsIndex 的類型。loadInfo方法體內(nèi),是用接收的map來put當(dāng)前module所有路由組幫助類的class

在上面LogisticsCenter的init方法中同樣 對(duì)遍歷收集到的幫助類判斷類名,接著就調(diào)用loadInfo方法,這就實(shí)現(xiàn)了對(duì)WareHouse.groupsIndex 的賦值。

根幫助類,目的就是對(duì)路由進(jìn)行分組,分組的好處是避免一次性加載所有路由,減少反射耗時(shí)和內(nèi)存占用的性能問題。

根幫助類也是在編譯時(shí)生成,具體生成過程將在下篇中介紹。

2.2.4.3 路由元信息

路由元信息的收集是通過 —— ARouter$$Group$$xxx —— 組幫助類:即用來幫助對(duì) WareHouse.routes 賦值。也就是把同組的路由put到WareHouse.routes。 一個(gè)module可能有多個(gè)組,即對(duì)應(yīng)有多個(gè)根幫助類,xxx是組名,即path第一級(jí)。

image.png

如上圖,ARouter$$Group$$test 就是組幫助類,幫助WareHouse填充Warehouse.routes。實(shí)現(xiàn)自IRouteGroup接口,loadInfo方法接受一個(gè)Map<String, RouteMeta>,也就是WareHouse.routes 的類型。loadInfo方法體內(nèi),是用接收的map來put 當(dāng)前組 的所有路由元信息其中最重要的就是 每個(gè)路由的目標(biāo)類class

在上面LogisticsCenter的init方法中 沒有看到對(duì)組幫助類的處理。ARouter的設(shè)計(jì)是:在使用時(shí)才進(jìn)行加載,即首次使用某個(gè)組的路由時(shí),才會(huì)使用組幫助類對(duì) WareHouse.routes 進(jìn)行填充。

組幫助類,目的就是 首次使用時(shí) 一次性加載本組所有路由元信息。這比較符合實(shí)際使用場(chǎng)景:一般同組的路由都是同業(yè)務(wù)的內(nèi)容,當(dāng)前用戶進(jìn)入此業(yè)務(wù)時(shí),就把本組路由元信息準(zhǔn)備好,是比較合理的。

組幫助類也是在編譯時(shí)生成。

2.2.4.4 provider元信息

provider元信息 其實(shí)在上面 路由元信息 中已經(jīng)包含了,為啥還要單獨(dú)拎出來呢?我們回頭看下 _ARouter的navigation方法:

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

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    ...
}

前面介紹了4個(gè)參數(shù)的方法,而上面這個(gè)傳服務(wù)class的重載方法 就是單獨(dú)給獲取provider服務(wù)使用的。看到通過LogisticsCenter使用服務(wù)類name獲取到了PostCard,然后經(jīng)過完善PostCard,直接獲取provider服務(wù)。

//LogisticsCenter.java
public static Postcard buildProvider(String serviceName) {
    RouteMeta meta = Warehouse.providersIndex.get(serviceName);
    if (null == meta) {
        return null;
    } else {
        return new Postcard(meta.getPath(), meta.getGroup());
    }
}

其中Postcard其實(shí)就是從 Warehouse.providersIndex 中獲取到的RouteMeta后構(gòu)建的。而Warehouse.providersIndex的賦值就是通過 ——ARouter$$Providers$$xxx —— Provider幫助類

image.png

幫助類可能在其他文章中叫路由表,之前想叫做代理類,但覺得幫助類更合適,它們就是用來幫助填充WareHouse中的元數(shù)據(jù)的。

2.2.5 AGP方式加載路由

我們來看下路由信息收集的第一種方式,這是一般都會(huì)使用到的方式,也就是loadRouterMap()方法:

public class LogisticsCenter {
    private static boolean registerByPlugin;
    
    private static void loadRouterMap() {
        registerByPlugin = false;
        //主動(dòng)注冊(cè)插件 會(huì)在此處插入代碼。調(diào)用此方法就注冊(cè)了全部的 Routers、Interceptors、Provider
        
    }
   ...

你會(huì)驚奇地發(fā)現(xiàn),loadRouterMap()竟然只有一行代碼?!

反編譯 ARouter demo APK后,查看LogisticsCenter:

image.png

看到編譯后的loadRouterMap()方法,多了幾行register()方法的調(diào)用,而參數(shù)就是 所有的根幫助類、攔截器幫助類、provider幫助類。

//LogisticsCenter.java
...
 private static void register(String className) {
        if (!TextUtils.isEmpty(className)) {
            try {
                Class<?> clazz = Class.forName(className);
                Object obj = clazz.getConstructor().newInstance();
                if (obj instanceof IRouteRoot) {
                    registerRouteRoot((IRouteRoot) obj);
                } else if (obj instanceof IProviderGroup) {
                    registerProvider((IProviderGroup) obj);
                } else if (obj instanceof IInterceptorGroup) {
                    registerInterceptor((IInterceptorGroup) obj);
                } ...
            } ...
        }
    }
    private static void registerRouteRoot(IRouteRoot routeRoot) {
        markRegisteredByPlugin();
        if (routeRoot != null) {
            routeRoot.loadInto(Warehouse.groupsIndex);
        }
    }
    private static void registerInterceptor(IInterceptorGroup interceptorGroup) {
        markRegisteredByPlugin();
        if (interceptorGroup != null) {
            interceptorGroup.loadInto(Warehouse.interceptorsIndex);
        }
    }
    private static void registerProvider(IProviderGroup providerGroup) {
        markRegisteredByPlugin();
        if (providerGroup != null) {
            providerGroup.loadInto(Warehouse.providersIndex);
        }
    }
    private static void markRegisteredByPlugin() {
        if (!registerByPlugin) {
            registerByPlugin = true; //標(biāo)記通過AGP加載成功了
        }
    }

register()方法很簡(jiǎn)單,就是反射創(chuàng)建幫助類實(shí)例,調(diào)用loadInto方法對(duì)Warehouse進(jìn)行填充,和第二種路由信息收集方式的是一致的。 而第二種是在運(yùn)行時(shí)遍歷dex才找到的幫助類,在第一種方式就神奇的直接出現(xiàn)了?

這個(gè)神奇的操作,我們將在第三篇文章做詳細(xì)介紹,目前只需知道是在編譯時(shí)進(jìn)行掃描并動(dòng)態(tài)在loadRouterMap()中插入代碼就可以了。

2.2.3 路由信息的完善

上面兜了一大圈,從路由整體過程、獲取路由結(jié)果、攔截器、路由信息記載,到各個(gè)幫助類的介紹,也即是說 我們了解了 路由的發(fā)起、路由整體過程、路由結(jié)果獲取,以及路由元信息的加載,那么現(xiàn)在就來看看路由元信息是如何使用的。

//LogisticsCenter.java
public synchronized static void completion(Postcard postcard) {
    //完善postcard信息(目前只有path、group,還需要知道具體目的地,例如要跳轉(zhuǎn)到的activity信息)
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {
        //沒有從Warehouse.routes獲取到:要么不存在、要么還沒有加載本組路由
        //先看路由倉(cāng)庫(kù)中是否有這個(gè)組幫助類,沒就異常。(倉(cāng)庫(kù)里的已有 組幫助類 是誰放進(jìn)倉(cāng)庫(kù)的呢?就是 在 ARouter.init中調(diào)用 LogisticsCenter.init的時(shí)候。它里面的 loadRouterMap() 中執(zhí)行代碼是 transform時(shí)收集到的 APT 生成 根幫助類 的load方法。)
        if (!Warehouse.groupsIndex.containsKey(postcard.getGroup())) {
            throw new NoRouteFoundException(TAG + "There is no route match the path [" + postcard.getPath() + "], in group [" + postcard.getGroup() + "]");
        } else {
            try {
                //這里,倉(cāng)庫(kù)中有這個(gè)組的幫助類,那么就可以 加載 這個(gè)組的所有路由 到內(nèi)存
                addRouteGroupDynamic(postcard.getGroup(), null);
            } catch (Exception e) {
                throw new HandlerException(TAG + "Fatal exception when loading group meta. [" + e.getMessage() + "]");
            }
            //倉(cāng)庫(kù)有了這個(gè)組的路由信息,再重新完善
            completion(postcard);
        }
    } else {
        //有路由信息,就完善postcard
        postcard.setDestination(routeMeta.getDestination());
        postcard.setType(routeMeta.getType());
        postcard.setPriority(routeMeta.getPriority());
        postcard.setExtra(routeMeta.getExtra());
        ...
        switch (routeMeta.getType()) {
            case PROVIDER:  //provider, 獲取實(shí)例
                // 要實(shí)現(xiàn)自IProvider
                Class<? extends IProvider> providerMeta = (Class<? extends IProvider>) routeMeta.getDestination();
                IProvider instance = Warehouse.providers.get(providerMeta);
                if (null == instance) { // 沒有,就反射創(chuàng)建
                    IProvider provider;
                    try {
                        provider = providerMeta.getConstructor().newInstance();
                        provider.init(mContext);
                        Warehouse.providers.put(providerMeta, provider); //實(shí)例存入倉(cāng)庫(kù)
                        instance = provider;
                    } catch (Exception e) {...
                    }
                }
                postcard.setProvider(instance); //實(shí)例通過PostCard帶出去
                postcard.greenChannel();    // Provider 不用經(jīng)過攔截器
                break;
            case FRAGMENT:
                postcard.greenChannel();    // Fragment 不用經(jīng)過攔截器
        }
    }
}


public synchronized static void addRouteGroupDynamic(String groupName, IRouteGroup group) {
    if (Warehouse.groupsIndex.containsKey(groupName)){//這里,倉(cāng)庫(kù)中 有這個(gè)組的幫助類
        //拿到這個(gè) 組幫助類,實(shí)例化,調(diào)loadInfo 把 這個(gè)組所有的路由信息加載 到 倉(cāng)庫(kù)中的routes。
        Warehouse.groupsIndex.get(groupName).getConstructor().newInstance().loadInto(Warehouse.routes);
        Warehouse.groupsIndex.remove(groupName);
    }...
}
  1. 嘗試通過path從倉(cāng)庫(kù)中獲取對(duì)應(yīng)的路由元信息,如果沒有獲取到:要么不存在、要么還沒有加載本組路由。
  2. 先看路由倉(cāng)庫(kù)中是否有這個(gè)組幫助類,沒就拋出異常;有就通過addRouteGroupDynamic()加載這個(gè)組的所有路由,然后再調(diào)completion
  3. 有了path對(duì)應(yīng)的路由元信息,就同步到postCard中,其中最重要的就是 目標(biāo)類class——routeMeta.getDestination()。并且判斷如是provider就創(chuàng)建服務(wù)實(shí)例并存入倉(cāng)庫(kù)。

好了,到這里,我們終于可以解答最開始提出的問題了:ARouter最為核心的內(nèi)容——如何獲取 無直接依賴的模塊的 class對(duì)象

  • 編譯時(shí)ARouter根據(jù)注解 @Route 生成了各個(gè)幫助類,幫助類的loadInfo方法中包含了路由目標(biāo)信息,最重要的是注解的類class,然后在ARouter初始化時(shí)根據(jù) 根幫助類、provider幫助類、攔截器幫助類 對(duì)倉(cāng)庫(kù)WareHouse的groupsIndex進(jìn)行賦值(以及providersIndex、interceptorsIndex),然后在路由發(fā)起后,根據(jù)path通過WareHouse的groupsIndex 加載 同組的所有路由元信息,也就是拿到了目標(biāo)class。
  • 抽象一下就是:moduleA先把目標(biāo)class存入第三方倉(cāng)庫(kù)——ARouter的WareHouse,然后muduleB發(fā)起路由時(shí)從倉(cāng)庫(kù)中根據(jù)path獲取目標(biāo)class,ARouter就是這個(gè)倉(cāng)庫(kù)的管理者。 就好比 郵政是信件的管理者,它是兩方通信者的中間人。

2.3 流程圖

以上分析內(nèi)容梳理成流程圖:

image.png

三、總結(jié)

我們從路由發(fā)起開始使,介紹了整個(gè)路由詳細(xì)過程:moduelA通過中間人ARouter把路由信息的存到倉(cāng)庫(kù)WareHouse;moduleB發(fā)起路由時(shí),再通過中間人ARouter從倉(cāng)庫(kù)WareHouse取出路由信息,這要就實(shí)現(xiàn)了沒有依賴的兩者之間的跳轉(zhuǎn)與通信。其中涉及Activity的跳轉(zhuǎn)、服務(wù)provider的獲取、攔截器的處理等。

需要重點(diǎn)理解的是:路由框架的整體思路,通過中間人ARouter使用WareHouse加載和獲取路由信息;路由信息加載實(shí)現(xiàn)原理,各幫助類作用和路由完善過程。

其中ARouter在編譯時(shí)生成的幫助類,是用于對(duì)所有使用@Route、@Interceptor注解的類信息的分組和收集,編譯運(yùn)行時(shí)對(duì)路由信息倉(cāng)庫(kù)Warehouse的填充和使用。這里涉及到的是Annotation Process ToolAPT)技術(shù),即注解處理工具。

如何使用編譯時(shí)生成的幫助類呢?除了運(yùn)行時(shí)查找dex,還可以在編譯時(shí)掃描幫助類信息,并且直接在物流中心LogisticsCenter loadRouterMap()方法中直接插入使用幫助類的代碼,這里涉及 Android Gradle PluginAGP)技術(shù),即Android的gradle插件相關(guān)技術(shù)。

ARouter如何在編譯時(shí)解析注解、如何生成幫助類以及對(duì)APT技術(shù)的介紹將在本系列第二篇中詳細(xì)介紹。

ARouter如何在編譯時(shí)掃描目標(biāo)幫助類、如何注入代碼以及對(duì)AGP技術(shù)的介紹將在本系列第三篇中詳細(xì)介紹。

好了本篇就到這里,歡迎繼續(xù)關(guān)注~

你的 點(diǎn)贊、評(píng)論,是對(duì)我的巨大鼓勵(lì)!

歡迎關(guān)注我的 公眾號(hào) 胡飛洋 ,文章更新可第一時(shí)間收到;

如果有問題或者想進(jìn)群,號(hào)內(nèi)有加我微信的入口,我拉你進(jìn)技術(shù)討論群。在技術(shù)學(xué)習(xí)、個(gè)人成長(zhǎng)的道路上,我們一起前進(jìn)!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,412評(píng)論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,514評(píng)論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,373評(píng)論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,975評(píng)論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,743評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,199評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,262評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,414評(píng)論 0 288
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,951評(píng)論 1 336
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,780評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,983評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,527評(píng)論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,218評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,649評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,889評(píng)論 1 286
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,673評(píng)論 3 391
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,967評(píng)論 2 374

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

  • Arouter是什么及其注解處理器的原理在文章:Arouter之注解器處理器原理解析[https://www.ji...
    jxiang112閱讀 393評(píng)論 0 0
  • 相關(guān)角色: ARouter:負(fù)責(zé)提供客戶端使用的Api接口,采用了門面模式,實(shí)際上內(nèi)部委托給了_ARouter去處...
    魔焰之閱讀 2,876評(píng)論 0 1
  • 前言 隨著項(xiàng)目業(yè)務(wù)邏輯和功能點(diǎn)日益遞增, 邏輯的耦合程度也逐漸升高, 組件化技術(shù)可以很好的解決這個(gè)問題, 公司大佬...
    SharryChoo閱讀 1,104評(píng)論 0 9
  • 一、ARouter ARouter使用的是APT(Annotation Processing Tool)注解處理器...
    zzq_nene閱讀 2,222評(píng)論 0 0
  • 一、什么是路由? 路由是指路由器從一個(gè)接口上收到數(shù)據(jù)包,根據(jù)數(shù)據(jù)路由包的目的地址進(jìn)行定向并轉(zhuǎn)發(fā)到另一個(gè)接口的過程。...
    泡花茶閱讀 462評(píng)論 0 1