ARouter源碼分析

組件化被越來越多的Android項目采用,而作為組件化的基礎——路由也是重中之重。本篇文章將詳細的分析阿里巴巴開源的路由框架ARouter。從源碼的角度解釋為什么這樣使用,以及避免做什么,讓你使用地更加順滑。

項目地址

ARouter

項目結構

我們把項目clone到本地,打開ARouter項目,可以看到分為如下幾個Module:

image

其中app、module-java、module-kotlin是演示demo相關的,也就是使用的示例。

arouter-register是1.3.0版本新添加的gradle插件的代碼,用于路由表的自動注冊,可以縮短初始化時間,解決應用加固導致無法直接訪問dex文件初始化失敗的問題。(本篇文章不涉及這個模塊)

arouter-annotation包含了注解類以及攜帶數據的bean;

arouter-compiler包含了注解處理類,通過java的Annotation Processor Tool按照定義的Processor生成所需要的類。
以demo為例子,當運行項目后會在build文件下生成arouter相關的代碼:

image

我們先不用去關心生成代碼的細節,只需要知道按照用法的提示添加諸如Route等注解之后,arouter-compiler中的注解處理類會自動幫我們生成需要的代碼(如上圖所示)。對源碼的分析也只需要知道生成的類的做了什么就夠了。

本篇文章也不討論生成代碼細節,如果想了解apt的生成過程,可以參考:Java注解處理器 以及更方便生成的Java代碼的庫javapoet

arouter-api提供了給我們使用的api,以實現路由功能。

那我們就從api開始分析。

源碼分析

init

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

if (isDebug()) {           // 這兩行必須寫在init之前,否則這些配置在init過程中將無效
    ARouter.openLog();     // 打印日志
    ARouter.openDebug();   // 開啟調試模式(如果在InstantRun模式下運行,必須開啟調試模式!線上版本需要關閉,否則有安全風險)
}
ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化

我們直接進入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用于保證初始化代碼只執行一次;
logger是一個日志工具類;

注意_ARouter類的下劃線,Arouter是對外暴露api的類,而_ARouter是真正的實現類。為什么這樣設計那?當然是為了解耦啦。具體點說有哪些好處呢?看看這個init方法,除了對實現類的調用,還有日志的打印,有沒有裝飾模式的感覺?可以增加一些額外的功能。其次,對比_ARouter類與Arouter類的方法,可以看到明顯Arouter類的方法少。要知道Arouter是對外暴露的,我們可以有選擇暴露用戶需要的方法,而把一些方法隱藏在內部。相比于用private修飾,這種方式靈活性更強。

接著我們進入實現類看下:

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

明顯有價值的是LogisticsCenter.init(mContext, executor);,executor是一個線程池。我們接著來看下去除日志等無關代碼后的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>()));
    }

    //判斷類型,使用反射實例化對象,并調用方法
    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);
        }
    }
    ...
}

這里體現了debuggable模式的作用,如果沒有開啟debuggable,并且調試的時候肯定不會更改版本號,因此只會從緩存中讀取類信息,所以新添加的路由不會加載到內存中。

ROUTE_ROOT_PAKCAGE是一個常量:

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

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

拿到所有生成類名的集合后,通過反射實例化對象并調用方法,將注解的一些元素添加到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,其實做的很簡單,僅僅是在key(優先級)相同時,拋出指定的異常。因此,記住不要出現優先級相同的interceptor。

生成的類有統一的命名規則,方便區分,分別實現對應的接口:

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生成的實現類都做了什么:

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

//將module中使用@Route注解的activity或Fragment添加到集合中,這里的方法會在之后調用
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的實現將有@Route注解的module名添加到參數集合中,也就是groupsIndex。

這里會存在一個小陷阱,如果不同的module中存在相同的分組(即路徑的第一段,如上面的“test”),則會在對應的module中生成不同的IRouteGroup的實現,然后在此處會執行分別執行routes.put("test", ARouter$Group$moduleA.class);,以及routes.put("test", ARouter$Group$moduleB.class);,但是因為key相同,因此前一個會被覆蓋,導致前一個定義的路由無法找到。具體可以看我提的issue,官方的建議是路徑分組與模塊名相同,并且不同模塊不要使用相同的分組。

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

同樣的IInterceptorGroup的實現將@Interceptor注解的類添加到參數集合中,也就是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的實現將繼承自IProvider的類添加到參數集合中,也就是providersIndex中。

RouteMeta是一個數據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傳遞的數據類型定義了對應的int類型: BOOLEAN,BYTE,SHORT,INT,LONG,CHAR,FLOAT,DOUBLE,STRING,PARCELABLE,OBJECT分別對應0,1,2,3...

RouteType是一個枚舉,表示被注解類的路由類型:

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,但是也預留了Service和Boardcast的類型,可能以后也會實現。

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

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

它是一個管理攔截器的服務,實現了IProvider接口,并且用自己實現的的路由方式獲取實例。嗯,沒毛病!

根據官方的說明,IProvider接口是用來暴露服務,并且在初始化的時候會被調用init(Context context)方法。具體的服務有其實現提供,那我們就來看下它的實現做了些什么:

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

還記的在上一步初始化的時候將所有注解了Interceptor的類的信息存入了Warehouse.interceptorsIndex,這里就將這些類實例化,并調用iInterceptor.init(context);完成自定義的初始化內容,最后放入Warehouse.interceptors集合中。

總結來說,init過程就是把所有注解的信息加載內存中,并且完成所有攔截器的初始化。

navigation

完成初始化之后,就可以實現路由跳轉了:

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

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

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

build方法調動了實現類_ARouter的build方法,繼續看:

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

這里出現一個PathReplaceService,它是繼承IProvider的接口,它是預留給用戶實現路徑動態變化功能,官方有詳細說明:

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

需要注意的是,build(String path)方法及build(String path, String group)方法中都嘗試使用PathReplaceService完成路徑動態變化,因此,在變化前需要做好判斷,否則出現變換兩次的情況(例如拼接字符)。

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

build方法最終返回一個Postcard對象。它也是一個數據bean,繼承自RouteMeta,附加一些跳轉信息,如參數之類:

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;

    ...
}

最后調用該Postcard對象的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) {
        if (null != callback) {
            callback.onLost(postcard);
        } else {    // 交給全局降級策略
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }

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

    ...    //方法未結束,分到之后
}

大致可看出LogisticsCenter.completion(postcard);肯定是試圖找到跳轉的目標,如果找不到則讓callback回調onLost,或者交給全局降級策略處理。找到則回調callback的onFound方法。

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

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

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

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

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

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

可以看到主要功能是找到匹配的目標類,并將目標類的一些信息拷貝到postcard對象中。

我們繼續看navigation方法的后半部分:

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

Code, final NavigationCallback callback) {
    ...
    if (!postcard.isGreenChannel()) {  //不是綠色通道,即經過攔截器處理
        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方法是最終處理,并且有返回值,如果路由目標是Fragment或者IProvider的話。攔截器的處理可能會耗時,因此會放到子線程處理,通過回調完成繼續操作,但此時就無法返回目標類(Fragment或IProvider),也就解釋了為什么上面的completion方法中設置了綠色通道。

我們先不考慮攔截過程,直接看_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或者不傳,不傳的話默認就用Application的context,每開啟一個Activity就會新開一個task。建議普通界面跳轉都傳入Activity,避免棧的混亂。

至此整個路由跳轉的過程大致完成,整個過程可以分為:封裝Postcard -> 查找信息集合,實例化目標類 -> 返回實例或者跳轉。

Interceptor

前面我們跳過了攔截過程,現在我們來分析下這個過程,攔截功能是通過ARouter提供的interceptorService實現的,并且之前我們在init章節分析它的初始化,接下來看看具體是如何攔截的:

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

可以看到調用了doInterceptions方法,在看這個方法之前,我們先要了解一個類:CountDownLatch。相關資料:CountDownLatch理解一:與join的區別

簡單來說CountDownLatch可以阻塞一個線程,知道內部計數器為0時,才繼續執行阻塞的線程,計數器的初始值通過構造傳入,通過調用countDown()方法減少一個計數。

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

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

首先會創建一個與攔截器數量相同的CancelableCountDownLatch初始計數值,每放行一個攔截器就countDown,并交給后一個攔截器,如果攔截則清0計數,并將攔截的Throwable存入postcard的tag字段,interceptorCounter.await();阻塞直到計數歸0或者阻塞超時(默認是300秒),最后通過interceptorCounter.getCount()判斷是否是超時,還是攔截或者放行。

可以看到攔截的過程都是在子線程中處理,包括Interceptor的process也是在子線程調用的,因此,如果想要在攔截過程中展示dialog等都需要切換到主線程。

inject

通過@Autowired注解的屬性,通過調用ARouter.getInstance().inject(this);可以實現自動注入。我們知道原生的Activity傳遞數據是通過Bundle攜帶的。因此,ARouter的數據傳遞肯定也是基于Bundle,并實現了自動賦值的功能。

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

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

AutowiredService與InterceptorService類似,都是Arouter自己實現的繼承IProvider接口的服務。我們找到AutowiredService的實現類:

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

去掉緩存集合之后,其實很簡單,核心內容就兩句:

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

ISyringe是一個接口:

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

它的實現是apt生成的,每一個帶有@Autowired注解的類都會生成一個對應的ISyringe的實現,如demo中的:

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

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

注意這里賦值的操作是直接調用“目標類對象.屬性”的方式賦值,因此,private修飾的屬性無法通過這種方式賦值,并且在賦值時會拋出異常,被AutowiredServiceImpl的autowire方法中的try-catch捕獲,存入不需要注入的集合中,最終導致同一個類中的其他非private屬性也無法注入。

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

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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。