ARouter使用解析

ARouter

ARouter 是阿里云出品的Android中間件木羹,負(fù)責(zé)處理盅跳轉(zhuǎn)頁面時(shí)的邏輯,簡(jiǎn)化和優(yōu)化之前跳轉(zhuǎn)頁面的方法昨稼。同時(shí)他也是組件化的基礎(chǔ)之一步咪,實(shí)現(xiàn)了模塊間的解耦论皆。

ARouter使用

項(xiàng)目的主頁有提供ARouter的使用方法,主要就是

  • 注解可以跳轉(zhuǎn)的類(Activity,Service点晴,ContentProvider感凤,F(xiàn)ragment等)
  • 要跳轉(zhuǎn)頁面的時(shí)候使用Arouter,類似于ARouter.getInstance().build("/kotlin/test").withString("name", "老王").withInt("age", 23).navigation();

用法是不能再簡(jiǎn)單了粒督,猜想是把一個(gè)String與一個(gè)Activity對(duì)應(yīng)起來就可以了陪竿,然而實(shí)際代碼應(yīng)該比猜想復(fù)雜N多倍。下面一起分析一下這個(gè)中間屠橄,挖掘他的所有信息族跛。

ARouter源碼解析

下面分析源碼分為幾部分

  • 跳轉(zhuǎn)頁面流程分析
  • 在類上和成員上的注解都做了什么
  • 解釋在ARouter文檔上的所有功能特點(diǎn)和典型應(yīng)用是如何實(shí)現(xiàn)的,把這些特點(diǎn)抄到下面來锐墙,方便查看礁哄,我們會(huì)挨個(gè)把所有的特點(diǎn)分析一遍

一、功能介紹

  1. 支持直接解析標(biāo)準(zhǔn)URL進(jìn)行跳轉(zhuǎn)溪北,并自動(dòng)注入?yún)?shù)到目標(biāo)頁面中
  2. 支持多模塊工程使用
  3. 支持添加多個(gè)攔截器桐绒,自定義攔截順序
  4. 支持依賴注入,可單獨(dú)作為依賴注入框架使用
  5. 支持InstantRun
  6. 支持MultiDex(Google方案)
  7. 映射關(guān)系按組分類之拨、多級(jí)管理茉继,按需初始化
  8. 支持用戶指定全局降級(jí)與局部降級(jí)策略
  9. 頁面、攔截器蚀乔、服務(wù)等組件均自動(dòng)注冊(cè)到框架
  10. 支持多種方式配置轉(zhuǎn)場(chǎng)動(dòng)畫
  11. 支持獲取Fragment
  12. 完全支持Kotlin以及混編(配置見文末 其他#5)
  13. 支持第三方 App 加固(使用 arouter-register 實(shí)現(xiàn)自動(dòng)注冊(cè))

二烁竭、典型應(yīng)用

  1. 從外部URL映射到內(nèi)部頁面,以及參數(shù)傳遞與解析
  2. 跨模塊頁面跳轉(zhuǎn)吉挣,模塊間解耦
  3. 攔截跳轉(zhuǎn)過程颖变,處理登陸、埋點(diǎn)等邏輯
  4. 跨模塊API調(diào)用听想,通過控制反轉(zhuǎn)來做組件解耦

跳轉(zhuǎn)頁面流程分析

使用的方法是

ARouter.getInstance()
        .build("/kotlin/test")
        .withString("name", "老王")
        .withInt("age", 23)
        .navigation();

ARouter使用了單例,內(nèi)部存儲(chǔ)了頁面的映射表马胧,初始化狀態(tài)汉买,debug信息等,等一下都會(huì)用到的信息(其實(shí)在保存在_ARouter單例中)佩脊,同時(shí)也方便在使用的時(shí)候直接使用ARouter的靜態(tài)方法蛙粘。
實(shí)際上除了ARouter之外還有一個(gè)_ARouter類,ARouter中幾乎是把所有的方法都直接給了_ARouter處理威彰,這一層的轉(zhuǎn)換把_ARouter中復(fù)雜的方法轉(zhuǎn)化為ARouter中簡(jiǎn)單的方法向外暴露出牧,算是一種門面吧,值得學(xué)習(xí)一下歇盼。
build方法不例外地轉(zhuǎn)給了_ARouter

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

在_ARouter中構(gòu)造了PostCard

/**
* Build postcard by path and default group
*/
protected Postcard build(String path) {
    if (TextUtils.isEmpty(path)) {
        throw new HandlerException(Consts.TAG + "Parameter is invalid!");
    } else {
        //這個(gè)Service里可以將傳入的path處理舔痕,換在另外一個(gè),也相當(dāng)于一個(gè)攔截器
        PathReplaceService pService = ARouter.getInstance().navigation(PathReplaceService.class);
        if (null != pService) {
            path = pService.forString(path);
        }
        return build(path, extractGroup(path));
    }
}

其中PathReplaceService相關(guān)處理邏輯是根據(jù)當(dāng)前的path換成另外的path繼續(xù)走下面的流程,所以主流程還是build方法伯复,其中有有個(gè)extractGroup(path)方法調(diào)用慨代,將path中第一部分抽取出來作為group,繼續(xù)看主線

/**
 * Build postcard by path and group
 */
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啸如,出于什么目的侍匙,很簡(jiǎn)單,因?yàn)檫@個(gè)方法有可能不是上面那個(gè)路徑調(diào)過來的叮雳,_ARouter還有兩個(gè)參數(shù)的builde方法想暗,直接調(diào)用了這個(gè)方法,但是不做任何區(qū)分直接再找一次也有點(diǎn)可以優(yōu)化的空間??
返回的結(jié)果就是最后構(gòu)造出來的一個(gè)PostCard帘不,這個(gè)類上的解釋是A container that contains the roadmap.说莫,包含這一次路由過程中所要的所有信息。PostCard的構(gòu)造方法沒有什么邏輯厌均,就是另外構(gòu)造了一個(gè)bundle放在了PostCard里面?zhèn)湎旅媸褂谩?/p>

到這里就返回到了最初調(diào)用build的地方唬滑,再往下是兩個(gè)withXXX方法,就是向PostCard中放入幾個(gè)跳轉(zhuǎn)頁面要帶過去的信息棺弊,都是直接放到了bundle里面晶密,當(dāng)然這不是主線。
繼續(xù)看下面的navigation方法模她。

/**
 * Navigation to the route with path in postcard.
 * No param, will be use application context.
 */
public Object navigation() {
    //后面會(huì)用application的Context
    return navigation(null);
}

給出的例子都是使用沒有參數(shù)所navigation方法稻艰,后面拿applicationContext,要設(shè)置intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);感覺有點(diǎn)誤導(dǎo)的感覺侈净,這里正正經(jīng)經(jīng)地傳個(gè)activity過來才應(yīng)該是最廣泛的使用方法尊勿。

/**
 * Navigation to the route with path in postcard.
 *
 * @param context Activity and so on.
 */
public Object navigation(Context context) {
    //這里沒有caback,這個(gè)callback有onFound畜侦,onLost元扔,onArrival,和onInterrupt方法旋膳,都是跳轉(zhuǎn)時(shí)的各種回調(diào)澎语,單獨(dú)的跳轉(zhuǎn)降級(jí)就是通過這個(gè)callback完成的
    return navigation(context, null);
}

/**
 * Navigation to the route with path in postcard.
 *
 * @param context Activity and so on.
 */
public Object navigation(Context context, NavigationCallback callback) {
    //這里又增加了一個(gè)-1的參數(shù),不求不需求forResult
    return ARouter.getInstance().navigation(context, this, -1, callback);
}

到這里方法調(diào)用就出了PostCard验懊,到了ARouter中擅羞,當(dāng)然又會(huì)委托給_ARouter進(jìn)行真正的業(yè)務(wù)。
看_ARouter的方法义图。

/**
 * Use router navigation.
 */
protected Object navigation(final Context context, final Postcard postcard, final int requestCode, final NavigationCallback callback) {
    try {
        //主流程减俏,補(bǔ)全路由所要的信息
        LogisticsCenter.completion(postcard);
    } catch (NoRouteFoundException ex) {
        logger.warning(Consts.TAG, ex.getMessage());
        if (debuggable()) { // Show friendly tips for user.
            Toast...
        }
        if (null != callback) {
            callback.onLost(postcard);
        } else {    // No callback for this invoke, then we use the global degrade service.
            //統(tǒng)一降級(jí)邏輯
            DegradeService degradeService = ARouter.getInstance().navigation(DegradeService.class);
            if (null != degradeService) {
                degradeService.onLost(context, postcard);
            }
        }
        return null;
    }
    if (null != callback) {
        callback.onFound(postcard);
    }

    //主流程,根據(jù)是否綠色通道走攔截的邏輯
    //這里還有個(gè)友情提示: It must be run in async thread, maybe interceptor cost too mush time made ANR.
    if (!postcard.isGreenChannel()) {  
        interceptorService.doInterceptions(postcard, new InterceptorCallback() {
            @Override
            public void onContinue(Postcard postcard) {
                //走完攔截器并通過的碱工,繼續(xù)走跳轉(zhuǎn)的邏輯
                _navigation(context, 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 {
        //主流程娃承,綠色通道的主要邏輯奏夫,此時(shí)所有的信息已經(jīng)準(zhǔn)備完成,下面就是與系統(tǒng)交互進(jìn)行跳轉(zhuǎn)了
        return _navigation(context, postcard, requestCode, callback);
    }
    return null;
}

主流程沒有幾名話草慧,先補(bǔ)全路由的信息桶蛔,走攔截邏輯,然后真正的跳轉(zhuǎn)漫谷,其中補(bǔ)全PostCard是重要過程仔雷,我們看一下方法詳情

/**
 * Completion the postcard by route metas
 * @param postcard Incomplete postcard, should complete by this method.
 */
public synchronized static void completion(Postcard postcard) {
    RouteMeta routeMeta = Warehouse.routes.get(postcard.getPath());
    if (null == routeMeta) {    // Maybe its does't exist, or didn't load.
        //沒有找到RouteMeta,檢查他所有的組是否還沒有加載舔示,如果已經(jīng)加載碟婆,則異常,沒有加載去加載
        Class<? extends IRouteGroup> groupMeta = Warehouse.groupsIndex.get(postcard.getGroup());  // Load route meta.
        if (null == groupMeta) {
            throw new NoRouteFoundException(...);
        } else {
            // Load route and cache it into memory, then delete from metas.
            try {
                IRouteGroup iGroupInstance = groupMeta.getConstructor().newInstance();
                //加載所在的組惕稻,如果加載完成就把這個(gè)group從未加載列表中刪除
                iGroupInstance.loadInto(Warehouse.routes);
                //將些組從未加載中移除竖共,防止重復(fù)加載,
                Warehouse.groupsIndex.remove(postcard.getGroup());
            } catch (Exception e) {
                throw new HandlerException(...);
            }
            // 加載了組信息俺祠,重新去重新去走補(bǔ)全的邏輯
            completion(postcard);   // Reload
    } else {
        將RouteMeta的信息放到PostCard中公给,
        如果是通過uri跳轉(zhuǎn)的話再將路徑中的信息解析出來放到postCard的bound中

        下面又對(duì)兩種類型的跳轉(zhuǎn)做的特殊處理
        1. PROVIDER
           從倉庫中找到provider的實(shí)例,疳賦值給postCard
           設(shè)置綠色通道蜘渣,防止攔截
        2. FRAGMENT
           設(shè)置綠色通道淌铐,防止攔截
    }
}

此時(shí)post的信息已經(jīng)完全了,我們路過攔截的邏輯蔫缸,直接看下面真正的跳轉(zhuǎn)方法腿准,感覺是沒有必要把代碼再拿出來,就是根據(jù)類型區(qū)分了一下拾碌,activity就直接new Intent進(jìn)行跳轉(zhuǎn)吐葱,如果是Privider,F(xiàn)ragment就返回實(shí)例校翔。
到這里基本完成了一次跳轉(zhuǎn)頁面所走的全部路徑弟跑。并沒有高深難懂的邏輯,一個(gè)比較好玩的就是PathReplaceService防症,DegradeService窖认,SerializationService等都是通過注冊(cè)一個(gè)Service完成的,這就大大增加了這個(gè)框架的靈活性告希,而且框架向外提供的這個(gè)功能,自己內(nèi)部已經(jīng)先用起來了烧给,這個(gè)也是挺有意思的燕偶。

ARouter中的注解有什么用,是怎么起作用的

@Route

作用:注解一個(gè)類础嫡,這個(gè)類就可以通過ARouter找到使用
Route主要有兩個(gè)屬性指么,path和group酝惧,在RouteProcessor中處理這個(gè)注解,在注解處理的方法中會(huì)根據(jù)注解的類型創(chuàng)建上面使用過的RouteMeta

for (Element element : routeElements) {
    TypeMirror tm = element.asType();
    Route route = element.getAnnotation(Route.class);
    RouteMeta routeMeta = null;

    if (types.isSubtype(tm, type_Activity)) {                 // Activity
        logger.info(">>> Found activity route: " + tm.toString() + " <<<");

        // Get all fields annotation by @Autowired
        Map<String, Integer> paramsType = new HashMap<>();
        for (Element field : element.getEnclosedElements()) {
            if (field.getKind().isField() && field.getAnnotation(Autowired.class) != null && !types.isSubtype(field.asType(), iProvider)) {
                // It must be field, then it has annotation, but it not be provider.
                Autowired paramConfig = field.getAnnotation(Autowired.class);
                paramsType.put(StringUtils.isEmpty(paramConfig.name()) ? field.getSimpleName().toString() : paramConfig.name(), typeUtils.typeExchange(field));
            }
        }
        routeMeta = new RouteMeta(route, element, RouteType.ACTIVITY, paramsType);
    } else if (types.isSubtype(tm, iProvider)) {         // IProvider
        logger.info(">>> Found provider route: " + tm.toString() + " <<<");
        routeMeta = new RouteMeta(route, element, RouteType.PROVIDER, null);
    } else if (types.isSubtype(tm, type_Service)) {           // Service
        logger.info(">>> Found service route: " + tm.toString() + " <<<");
        routeMeta = new RouteMeta(route, element, RouteType.parse(SERVICE), null);
    } else if (types.isSubtype(tm, fragmentTm) || types.isSubtype(tm, fragmentTmV4)) {
        logger.info(">>> Found fragment route: " + tm.toString() + " <<<");
        routeMeta = new RouteMeta(route, element, RouteType.parse(FRAGMENT), null);
    } else {
        throw new RuntimeException("ARouter::Compiler >>> Found unsupported class type, type = [" + types.toString() + "].");
    }

    categories(routeMeta);
    // if (StringUtils.isEmpty(moduleName)) {   // Hasn't generate the module name.
    //     moduleName = ModuleUtils.generateModuleName(element, logger);
    // }
}

分別構(gòu)建出來RouteMeta伯诬,還構(gòu)建出來一個(gè)分組的信息晚唇,下面將這些信息構(gòu)建兩個(gè)java文件。類似于這樣

public class ARouter$$Root$$app implements IRouteRoot {
  @Override
  public void loadInto(Map<String, Class<? extends IRouteGroup>> routes) {
    routes.put("service", ARouter$$Group$$service.class);
    routes.put("test", ARouter$$Group$$test.class);
  }
}

rootInfo盗似,存放所有的組的信息哩陕,就是上面找不到RouteMeta的時(shí)候會(huì)從這里找到對(duì)應(yīng)的組,再找到組信息對(duì)應(yīng)的類赫舒,然后加載
還有一個(gè)組詳細(xì)信息的類悍及,類似開這樣

public class ARouter$$Group$$test implements IRouteGroup {
    @Override
    public void loadInto(Map<String, RouteMeta> atlas) {
      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/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));
    }
}  

加載的時(shí)候把這些信息加載到map中,以后跳轉(zhuǎn)使用

@Interceptor

作用:設(shè)置全局跳轉(zhuǎn)的攔截器接癌,可以設(shè)置優(yōu)先級(jí)
處理注解基本和和@Route一樣心赶,得到類,得到屬性缺猛,javapoet寫出一個(gè)類似于這樣的類:

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

這個(gè)map是個(gè)特別的map缨叫,根據(jù)key的值自動(dòng)排序,如果key重復(fù)會(huì)異常荔燎,也也是這個(gè)攔截器可以按優(yōu)先級(jí)排序的原因

@Autowired

作用:自動(dòng)裝配耻姥,注解成員后,可以自動(dòng)從Intent中解出數(shù)據(jù)并賦值給變量
實(shí)現(xiàn)也很相似湖雹,找到被注解的成員咏闪,生成一個(gè)helper,在需要將intent的數(shù)據(jù)解出來的時(shí)候使用helper的inject方法摔吏,ARouter又使用了一個(gè)AutowiredService專門做這個(gè)事鸽嫂,只要將要注入的類傳過來就可以了

@Override
public void autowire(Object instance) {
    String className = instance.getClass().getName();
    try {
        if (!blackList.contains(className)) {
            // 只有一個(gè)inject方法
            ISyringe autowiredHelper = classCache.get(className);
            if (null == autowiredHelper) {  // No cache.
                autowiredHelper = (ISyringe) Class.forName(instance.getClass().getName() + SUFFIX_AUTOWIRED).getConstructor().newInstance();
            }
            // autowiredHelper就是根據(jù)注解生成的特定helper
            autowiredHelper.inject(instance);
            classCache.put(className, autowiredHelper);
        }
    } catch (Exception ex) {
        blackList.add(className);    // This instance need not autowired.
    }
}

javaPoet實(shí)在是有點(diǎn)煩瑣,真的不愿把他的代碼拿來征讲。有意的同學(xué)可以直接去arouter查看

解釋所有官方列舉的特點(diǎn)

  1. 支持直接解析標(biāo)準(zhǔn)URL進(jìn)行跳轉(zhuǎn)据某,并自動(dòng)注入?yún)?shù)到目標(biāo)頁面中

在Manifast頁面中注冊(cè)了兩個(gè)filter

<intent-filter>
    <data
        android:host="m.aliyun.com"
        android:scheme="arouter"/>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>

<!-- App Links -->
<intent-filter android:autoVerify="true">
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data
        android:host="m.aliyun.com"
        android:scheme="http"/>
    <data
        android:host="m.aliyun.com"
        android:scheme="https"/>
</intent-filter>

第一個(gè)是處理特定的協(xié)議,如果協(xié)議中有arouter诗箍,則會(huì)用這個(gè)Acitity處理
還有一個(gè)是處理applink的癣籽,在網(wǎng)頁上點(diǎn)擊特定連接,也是在這個(gè)Activity中處理
在這個(gè)Activity主要代碼就兩行:

Uri uri = getIntent().getData();
ARouter.getInstance().build(uri).navigation(this, new NavCallback() {
            @Override
            public void onArrival(Postcard postcard) {
                finish();
            }
        });

這里使用的是一個(gè)URI滤祖,在構(gòu)造PostCard的時(shí)候會(huì)將URI后面掛的參數(shù)直接轉(zhuǎn)化到bound中去筷狼,也就解釋了第一個(gè)特征。

  1. 支持多模塊工程使用

各模塊只是依賴一個(gè)String匠童,編譯時(shí)會(huì)掃描整個(gè)所有的工程埂材,所以直接就支持多模塊的工程。但是有個(gè)問題就是別的頁面要跳轉(zhuǎn)的時(shí)候都要將字符串寫死進(jìn)去汤求,如果定義常量的話會(huì)出現(xiàn)多個(gè)模塊依賴一個(gè)常量類的情況俏险。

  1. 支持添加多個(gè)攔截器严拒,自定義攔截順序
    攔截器注解定義

如果設(shè)置了這個(gè)優(yōu)先級(jí)別,生成的java代碼中會(huì)將這個(gè)優(yōu)先級(jí)做為key竖独,放到傳過來的一個(gè)容器中裤唠,而這個(gè)窗口的定義在com.alibaba.android.arouter.core.Warehouse中,是一個(gè)UniqueKeyTreeMap莹痢,保證key是唯一的种蘸,并且按key進(jìn)行排序

UniqueKeyTreeMap

這里也就解釋了自定義攔截順序的特點(diǎn)

  1. 支持依賴注入,可單獨(dú)作為依賴注入框架使用

不知道講的是什么格二。劈彪。
navigation方法返回的是一個(gè)Object

  1. 支持InstantRun
  2. 支持MultiDex(Google方案)

這里看到源碼中找了一個(gè)所有的的dex文件,再從這些所有的dex中查找要找的router的類顶猜,應(yīng)該就是處理這個(gè)問題沧奴。等于沒有說。长窄。就簡(jiǎn)單看一下調(diào)用鏈吧

Arouter.init() -> LogisticsCenter.init(mContext, executor) 
-> ClassUtils.getFileNameByPackageName(mContext, ROUTE_ROOT_PAKCAGE);//這里就是是指定的ali的包名滔吠,就是生成的那些類包名
-> ClassUtils.getSourcePaths(context);//從多dex下找類 
-> ClassUtils.tryLoadInstantRunDexFile(applicationInfo)//instantRun
-> 找到所有生成的注冊(cè)router的類
  1. 映射關(guān)系按組分類、多級(jí)管理挠日,按需初始化

路由的注解中可以注冊(cè)一個(gè)group信息疮绷,如果不定義這個(gè)group信息,arouter會(huì)拿路徑中的第一段做為group嚣潜。處理注解的時(shí)候會(huì)生成兩組信息冬骚,第一是組信息,其中有所有g(shù)roup的信息懂算,每一組都會(huì)指向一個(gè)描述這個(gè)組中所有路徑的類只冻。
初始化時(shí)僅僅加載了組的信息,并沒有加載每一組內(nèi)的所有路由计技,使用路由時(shí)會(huì)先查找有沒有這個(gè)路由信息喜德,如果沒有的話就去加載這一組所有的路由。做到了按需初始化垮媒。
這個(gè)過程上面路由過程已經(jīng)用代碼分析過了舍悯。

  1. 支持用戶指定全局降級(jí)與局部降級(jí)策略

每一次使用路由時(shí)可以傳入一個(gè)callback,作為單次路由失敗的降級(jí)策略睡雇,其實(shí)也不僅僅是降級(jí)策略萌衬,callback提供了多個(gè)回調(diào)方法使用:

public interface NavigationCallback {
    //找到路由
    void onFound(Postcard postcard);
    //沒有找到,降級(jí)吧
    void onLost(Postcard postcard);
    //向android發(fā)出了startActivity的請(qǐng)求
    void onArrival(Postcard postcard);
    //使用攔截器時(shí)
    void onInterrupt(Postcard postcard);
}

也可以注冊(cè)一個(gè)IProvider它抱,用來處理所有的降級(jí)策略秕豫。

  1. 頁面、攔截器抗愁、服務(wù)等組件均自動(dòng)注冊(cè)到框架

使用注解馁蒂,編譯期處理,運(yùn)行時(shí)直接無反射運(yùn)行(多dex什么的還是要反射)

  1. 支持多種方式配置轉(zhuǎn)場(chǎng)動(dòng)畫

支持蜘腌,無特殊

  1. 支持獲取Fragment

navigate的時(shí)候支持返回一個(gè)fragment沫屡,只要注冊(cè)了路由的fragment,都可以通過路由來得到實(shí)例撮珠。

  1. 完全支持Kotlin以及混編(配置見文末 其他#5)
  2. 支持第三方 App 加固(使用 arouter-register 實(shí)現(xiàn)自動(dòng)注冊(cè))
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末沮脖,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子芯急,更是在濱河造成了極大的恐慌勺届,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娶耍,死亡現(xiàn)場(chǎng)離奇詭異免姿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)榕酒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門胚膊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人想鹰,你說我怎么就攤上這事紊婉。” “怎么了辑舷?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵喻犁,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我何缓,道長(zhǎng)肢础,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任歌殃,我火速辦了婚禮乔妈,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘氓皱。我一直安慰自己路召,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布波材。 她就那樣靜靜地躺著股淡,像睡著了一般。 火紅的嫁衣襯著肌膚如雪廷区。 梳的紋絲不亂的頭發(fā)上唯灵,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音隙轻,去河邊找鬼埠帕。 笑死垢揩,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的敛瓷。 我是一名探鬼主播叁巨,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼呐籽!你這毒婦竟也來了锋勺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤狡蝶,失蹤者是張志新(化名)和其女友劉穎庶橱,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體贪惹,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡苏章,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了馍乙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片布近。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖丝格,靈堂內(nèi)的尸體忽然破棺而出撑瞧,到底是詐尸還是另有隱情,我是刑警寧澤显蝌,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布预伺,位于F島的核電站,受9級(jí)特大地震影響曼尊,放射性物質(zhì)發(fā)生泄漏酬诀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一骆撇、第九天 我趴在偏房一處隱蔽的房頂上張望瞒御。 院中可真熱鬧,春花似錦神郊、人聲如沸肴裙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蜻懦。三九已至,卻和暖如春夕晓,著一層夾襖步出監(jiān)牢的瞬間宛乃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留征炼,地道東北人析既。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像谆奥,于是被迫代替她去往敵國和親渡贾。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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

  • 組件化 模塊化雄右、組件化與插件化 在項(xiàng)目發(fā)展到一定程度,隨著人員的增多纺讲,代碼越來越臃腫擂仍,這時(shí)候就必須進(jìn)行模塊化的拆分...
    silentleaf閱讀 4,943評(píng)論 2 12
  • ARouter源碼解讀 以前看優(yōu)秀的開源項(xiàng)目,看到了頁面路由框架ARouter熬甚,心想頁面路由是個(gè)啥東東逢渔,于是乎網(wǎng)上...
    陸元偉閱讀 509評(píng)論 0 1
  • 盛夏將盡,時(shí)光染暖 答應(yīng)會(huì)在 牽起的手小心翼翼 仿佛那是稀世珍寶乡括,稍一用力就會(huì)破碎 盛夏將盡肃廓,溫度剛好 約定了一世...
    戀物念一樣閱讀 282評(píng)論 1 2
  • 軒兒閱讀 89評(píng)論 0 0
  • 無形的網(wǎng) 我們都是些 負(fù)重的 帶殼兒的蟲 動(dòng)彈不了 被它粘著 全都低著頭 專注著 蠶食每本起皺的書 多刻苦 每當(dāng)翻...
    燦7閱讀 236評(píng)論 1 5