ARouter源碼淺析

簡介

Android平臺中對頁面、服務提供路由功能的中間件,個人目標是 —— 簡單且夠用。具體的使用能夠參考:https://github.com/alibaba/ARouter。 若是對內部的詳細原理感興趣能夠參考:http://www.jianshu.com/p/3c4f4e3e621f。 下文分析的代碼來源於ARouter官方Demo,閱讀下文以前建議先下載一份源碼運行並對照源碼進行比對,由於下文中我會用官方demo的源碼截圖。java

APT

APT技術可讓咱們經過自定義註解動態生成編譯後的class代碼,具體的使用我在這裏就不詳細說了,感興趣的能夠參考我之前寫的:編寫最基本的APT Demo。 我這裏直接來看下ARouter說涉及到的幾個註解,以及編譯以後動態生成的代碼。android

annotation

image.png
其中Param已被Autowired代替

Route:用於標記咱們須要跳轉的四大組件(能夠經過Intent跳轉的,由於其實ARouter 內部最後也是經過Intent來進行跳轉)、service(此處的sevice是相似於後臺的服務,須要繼承IProvider)。 Interceptor:主要的做用是經過AOP技術(面向切面編程)在咱們進行頁面跳轉以前能夠進行一系列的攔截操做 Autowired:主要的做用是經過IOC技術(依賴注入)獲取頁面跳轉的參數。git

註解解析

image.png
能夠看到對應了上面的三個註解。這裏具體的代碼我就不分析了,感興趣的能夠直接去看源碼(雖然不算很難可是比較繁瑣,必定要耐心),當咱們全局編譯之後會動態生成如下代碼
image.png

**ARouter$$Root$$app:**由於Arouter採起的是懶加載技術,因此咱們須要對router進行分組,這裏的Root內部就是經過Map以組名爲key存儲了每組router的信息信息。 **ARouter$$Group$$xxx:**咱們按不一樣的router類型進行分組,內部經過Map以router path存儲了具體router的信息 **ARouter$$Interceptors$$app:**其中app是咱們的module名(經過查看源碼可知),內部 以priority優先級爲key存儲了具體的Interceptors的class信息。 **ARouter$$Providers$$app:**其中app是咱們的module名(經過查看源碼可知),內部以類的完整限定名爲key保存了service的信息,結構同ARouter$$Group$$xxx一致,只是用於不一樣的功能。github

關於ARouter的APT分支就到這了,下面來看下ARouter的初始化。編程

init

這正式分析初始化以前咱們先了解幾個類json

ARouter:_ARouter的代理類,這裏採用了代理模式,其實ARouter對外只開放了這一個api,全部的操做基本上都是經過ARouter來完成了。 **_ARouter:**ARouter所代理獲得類,功能的具體執行者。 **LogisticsCenter:**物流調度中心,對路由信息進行解析和加工。 **Warehouse:**倉庫,存儲了全部具體的router、interceptors、service等信息,內部是一系列的Map。api

ARouter的初始化流程:ARouter#init ──》_ARouter#init ──》LogisticsCenter#init ──》Warehouse#Map#put bash

image.png

// 得到指定包名下的全部類名
            List<String> classFileNames = ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);

            for (String className : classFileNames) {
                if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_ROOT)) {
                    // This one of root elements, load root. 經過反射找到Arouter$$Root$$xx 加載根路由集合
                    ((IRouteRoot) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.groupsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_INTERCEPTORS)) {
                    // Load interceptorMeta 經過反射找到Arouter$$Interceptors$$xx 加載攔截器集合
                    ((IInterceptorGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.interceptorsIndex);
                } else if (className.startsWith(ROUTE_ROOT_PAKCAGE + DOT + SDK_NAME + SEPARATOR + SUFFIX_PROVIDERS)) {
                    // Load providerIndex 經過反射找到Arouter$$Providers$$xx 加載服務集合 此處的service對應後臺的概念 不是四大組件的service
                    ((IProviderGroup) (Class.forName(className).getConstructor().newInstance())).loadInto(Warehouse.providersIndex);
                }
            }
複製代碼

其中第三步和第四步的代碼會根據指定報名查找下面全部的類信息,而後根據類獲得全限定名進行功能分組,並把信息保存在Warehouse的Map中。到此Arouter的初始化過程就完成。app

Router的跳轉和參數注入

ARouter.getInstance().build("/test/activity1")
                        .withString("name", "老王")
                        .withInt("age", 18)
                        .withBoolean("boy", true)
                        .withLong("high", 180)
                        .withString("url", "https://a.b.c")
                        .withParcelable("pac", testParcelable)
                        .withObject("obj", testObj)
                        .navigation();

public class Test1Activity extends AppCompatActivity {

    @Autowired
    String name;

    @Autowired
    int age;

    @Autowired(name = "boy")
    boolean girl;

    @Autowired
    TestParcelable pac;

    @Autowired
    TestObj obj;

    private long high;

    @Autowired
    String url;

    @Autowired
    HelloService helloService;

}
複製代碼

其中Test1Activity 經過APT動態生成的代碼以下:框架

atlas.put("/test/activity1", RouteMeta.build(RouteType.ACTIVITY, Test1Activity.class, "/test/activity1", "test", new java.util.HashMap<String, Integer>(){{put("pac", 9); put("obj", 10); put("name", 8); put("boy", 0); put("age", 3); put("url", 8); }}, -1, -2147483648));
複製代碼

全部的跳轉參數都保存在map中,其中key是一一對應,而value是參數類型,對題對照以下:

public enum TypeKind {
    // Base type
    BOOLEAN,
    BYTE,
    SHORT,
    INT,
    LONG,
    CHAR,
    FLOAT,
    DOUBLE,

    // Other type
    STRING,
    PARCELABLE,
    OBJECT;
}
複製代碼

下面咱們來看具體的跳轉流程。

#build

ARouter.getInstance().build("/test/activity1")升溫咱們說過ARouter是_ARouter的代理的類,全部的api最終都會進入到真正的執行類_ARouter。 _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));
        }
    }
複製代碼

這裏咱們先來看下PathReplaceService這個類

public interface PathReplaceService extends IProvider {

    /**
     * For normal path.
     *
     * @param path raw path
     */
    String forString(String path);

    /**
     * For uri type.
     *
     * @param uri raw uri
     */
    Uri forUri(Uri uri);
}
複製代碼

PathReplaceService我稱它爲重定向服務(具體怎麼使用請參考官網文檔),它繼承IProvider,那IProvider是什麼,其實他是用來實現service的,因此PathReplaceService就至關於咱們本身的自定義服務,惟一的區別是,自定義服務須要咱們顯示去調用,跟調用router同樣,可是PathReplaceService不須要顯示調用,他是做用於全部服務和路由的,並且無論你實現了幾個PathReplaceService,最終全局都只會保存在APT時掃描到的最後一個服務。爲何這麼說,請看下面的代碼:

public class ARouter$$Providers$$app implements IProviderGroup {
  @Override
  public void loadInto(Map<String, RouteMeta> providers) {
    //第一個重定向服務
    providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl.class, "/redirect/r1", "redirect", null, -1, -2147483648));
    //第二個重定向服務
    providers.put("com.alibaba.android.arouter.facade.service.PathReplaceService", RouteMeta.build(RouteType.PROVIDER, PathReplaceServiceImpl2.class, "/redirect/r2", "redirect", null, -1, -2147483648));
    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));
  }
}
複製代碼

由於第一個和第二個重定向服務的key是同樣都是PathReplaceService的類全限定名,因此第一個服務會被覆蓋掉。好了關於PathReplaceService咱們就說到這,他是咱們一個重定向服務,做用域全部的跳轉,並且全局只有一個。 咱們繼續往下分析,若是單例沒有實現自定義重定向服務的時候,PathReplaceService pService == null,因此會直接調用兩個參數的重載的build方法。

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);
        }
    }
複製代碼

這裏有調用了一遍ARouter.getInstance().navigation(PathReplaceService.class),感受不必啊,可是無論確定仍是同樣的返回空。因此最終ARouter.getInstance().build()會返回一個Postcard(個包含跳轉信息的容器,包含跳轉參數和目標類的信息)。下面進入真正的跳轉。其實真正的跳轉位於_ARouter#navigation。 ###_ARouter#navigation

protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        try {
            //完善跳轉信息
            LogisticsCenter.completion(postcard);
        } catch (NoRouteFoundException ex) {
           ……………………
            return null;
        }

        if (null != callback) {
            callback.onFound(postcard);
        }
        //不是綠色通道因此會進入默認interceptorService.doInterceptions
        if (!postcard.isGreenChannel()) {   // It must be run in async thread, maybe interceptor cost too mush time made ANR.
            interceptorService.doInterceptions(postcard, new InterceptorCallback() {
                /**
                 * Continue process
                 *
                 * @param postcard route meta
                 */
                @Override
                public void onContinue(Postcard postcard) {
                    _navigation(context, postcard, requestCode, callback);
                }

                /**
                 * Interrupt process, pipeline will be destory when this method called.
                 *
                 * @param exception Reson of interrupt.
                 */
                @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(context, postcard, requestCode, callback);
        }

        return null;
    }
複製代碼

InterceptorService是咱們在初始化完成之後ARouter爲咱們自動註冊的攔截器服務,由於咱們並無爲咱們獲得路由匹配相應的攔截器,因此應該會進入onContinue方法,通過斷點調試確實和咱們想的同樣,但是onContinue是個回調函數,它又具體是在哪被調用的呢?咱們通過查找發現是在InterceptorServiceImpl中

InterceptorServiceImpl#doInterceptions

LogisticsCenter.executor.execute(new Runnable() {
                @Override
                public void run() {
                    CancelableCountDownLatch interceptorCounter = new CancelableCountDownLatch(Warehouse.interceptors.size());
                    try {
                        _excute(0, interceptorCounter, postcard);
                        interceptorCounter.await(postcard.getTimeout(), TimeUnit.SECONDS);
                        if (interceptorCounter.getCount() > 0) {    // Cancel the navigation this time, if it hasn't return anythings. callback.onInterrupt(new HandlerException("The interceptor processing timed out.")); } else if (null != postcard.getTag()) { // Maybe some exception in the tag. callback.onInterrupt(new HandlerException(postcard.getTag().toString())); } else { callback.onContinue(postcard); } } catch (Exception e) { callback.onInterrupt(e); } } }); 複製代碼

這裏開了一個線程用來執行攔截器或者普通的跳轉因此調用了callback.onContinue,接下來就進入到咱們真正的跳轉執行的地方了。

_ARouter#_navigation

private Object _navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
        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);
                }

                // 由於上文咱們是在子線程中檢查是否有匹配的攔截器,因此咱們要在這裏切換到UI線程執行具體的跳轉
                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 ((0 != postcard.getEnterAnim() || 0 != 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:
                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;
    }
複製代碼

這裏的代碼比較簡單,就是調用了Android原生的Intent進行跳轉,而後根據不一樣的狀態,調用一些回調函數。到此關於ARouter的跳轉到這裏就結束了,下面咱們來看下目標對象的參數是如何獲取的。

Autowired

這裏在分析參數獲取以前咱們先廢話2句,在看到Autowired註解的時候,是否是感受似曾相識,沒錯這裏的原理跟ButterKnife是一毛同樣的,我強烈懷疑Arouter做者是參考ButterKnife代碼寫的,因此當咱們分析完Autowired的時候,其實就至關於把ButterKnife也給分析了,哈哈,正式一箭雙鵰啊。還有,這種開發思想其實在後臺開發中很是廣泛,好比大名鼎鼎的Spring就是這種IOC(控制反轉)思想的最佳表明。好了,下面進入正題。

Autowired註解處理器

當咱們在編譯過程當中,系統會是掃描有Autowired註解的成員變量類,而後生成自動生成如下代碼:

public class Test1Activity$$ARouter$$Autowired implements ISyringe {
  private SerializationService serializationService;

  @Override
  public void inject(Object target) {
    serializationService = ARouter.getInstance().navigation(SerializationService.class);;
    Test1Activity substitute = (Test1Activity)target;
    substitute.name = substitute.getIntent().getStringExtra("name");
    substitute.age = substitute.getIntent().getIntExtra("age", 0);
    substitute.girl = substitute.getIntent().getBooleanExtra("boy", false);
    substitute.pac = substitute.getIntent().getParcelableExtra("pac");
    if (null != serializationService) {
      substitute.obj = serializationService.json2Object(substitute.getIntent().getStringExtra("obj"), TestObj.class);
    } else {
      Log.e("ARouter::", "You want automatic inject the field 'obj' in class 'Test1Activity' , then you should implement 'SerializationService' to support object auto inject!");
    }
    substitute.url = substitute.getIntent().getStringExtra("url");
    substitute.helloService = ARouter.getInstance().navigation(HelloService.class);
  }
}
複製代碼

這裏的代碼很簡單,應該能直接看懂,咱們先來看他的父類ISyringe,他其實至關於一個模板類,爲了便於編程ARouter內核提供了許多的模板類,存儲在以下路徑中:

image.png
那麼爲何要提供模板類呢?簡單了來講,當咱們在變成過程當中,因爲框架做者並不知道哪些具體類被標註了註解,因此要動態獲取對象,只能經過反射動態來獲取實例,而後調用接口的方法來執行具體的操做,這就是多態的概念,以下代碼所示:
image.png
這裏插一句,反射是會影響性能的,因此通常咱們在編程中除非萬不得已,不然儘可能不要採用反射,可是這裏是activity初始化的時候反射,原本就會進行大量耗時的操做,哪怕有一點點的性能損耗也是能夠接受的。還記得Arouter的初始化嗎?官網上有一句原話是這麼說的:
image.png
你們有沒有想過,爲何要儘量早的初始化,我想除了要掃描大量的對象並保存到全局的map集合中之外,跟初始化的時候用到反射也有關係吧,畢竟仍是有性能損耗的。以下所示
image.png
image.png

總結

好了,到這咱們已經把頁面跳轉和參數綁定都分析完了,剩下的重定向,攔截器,降級等不少其餘功能,其實都是在跳轉的過程當中插入的一些攔截操做而已,我相信只要你們只要耐下心來看代碼都是能夠看明白的。

請參考ARouter 源碼淺析第二篇

相關文章
相關標籤/搜索