[Alibaba-ARouter] 簡單好用的Android頁面路由框架

開發(fā)一款A(yù)pp州藕,總會遇到各種各樣的需求和業(yè)務(wù)森篷,這時候選擇一個簡單好用的輪子,就可以事半功倍

前言

        Intent intent = new Intent(mContext, XxxActivity.class);
        intent.putExtra("key","value");
        startActivity(intent);
        
        Intent intent = new Intent(mContext, XxxActivity.class);
        intent.putExtra("key","value");
        startActivityForResult(intent, 666);

上面一段代碼营罢,在Android開發(fā)中赏陵,最常見也是最常用的功能就是頁面的跳轉(zhuǎn),我們經(jīng)常需要面對從瀏覽器或者其他App跳轉(zhuǎn)到自己App中頁面的需求愤钾,不過就算是簡簡單單的頁面跳轉(zhuǎn)瘟滨,隨著時間的推移候醒,也會遇到一些問題:

  1. 集中式的URL管理:談到集中式的管理能颁,總是比較蛋疼,多人協(xié)同開發(fā)的時候倒淫,大家都去AndroidManifest.xml中定義各種IntentFilter伙菊,使用隱式Intent,最終發(fā)現(xiàn)AndroidManifest.xml中充斥著各種Schame,各種Path敌土,需要經(jīng)常解決Path重疊覆蓋镜硕、過多的Activity被導(dǎo)出,引發(fā)安全風(fēng)險等問題
  2. 可配置性較差:Manifest限制于xml格式返干,書寫麻煩兴枯,配置復(fù)雜,可以自定義的東西也較少
  3. 跳轉(zhuǎn)過程中無法插手:直接通過Intent的方式跳轉(zhuǎn)矩欠,跳轉(zhuǎn)過程開發(fā)者無法干預(yù)财剖,一些面向切面的事情難以實施悠夯,比方說登錄、埋點這種非常通用的邏輯躺坟,在每個子頁面中判斷又很不合理沦补,畢竟activity已經(jīng)實例化了
  4. 跨模塊無法顯式依賴:在App小有規(guī)模的時候,我們會對App做水平拆分咪橙,按照業(yè)務(wù)拆分成多個子模塊夕膀,之間完全解耦,通過打包流程控制App功能美侦,這樣方便應(yīng)對大團隊多人協(xié)作产舞,互相邏輯不干擾,這時候只能依賴隱式Intent跳轉(zhuǎn)菠剩,書寫麻煩庞瘸,成功與否難以控制。

另一個輪子

為了解決以上問題赠叼,我們需要一款能夠解耦擦囊、簡單、功能多嘴办、定制性較強瞬场、支持?jǐn)r截邏輯的路由組件:我們選擇了Alibaba的ARouter,偷個懶涧郊,直接貼ARouter的中文介紹文檔:

Demo gif
Demo gif

一贯被、功能介紹

  1. 支持直接解析URL進行跳轉(zhuǎn)、參數(shù)按類型解析到Bundle妆艘,支持Java基本類型(*)
  2. 支持應(yīng)用內(nèi)的標(biāo)準(zhǔn)頁面跳轉(zhuǎn)彤灶,API接近Android原生接口
  3. 支持多模塊工程中使用,允許分別打包批旺,包結(jié)構(gòu)符合Android包規(guī)范即可(*)
  4. 支持跳轉(zhuǎn)過程中插入自定義攔截邏輯幌陕,自定義攔截順序(*)
  5. 支持服務(wù)托管,通過ByName,ByType兩種方式獲取服務(wù)實例汽煮,方便面向接口開發(fā)與跨模塊調(diào)用解耦(*)
  6. 映射關(guān)系按組分類搏熄、多級管理,按需初始化暇赤,減少內(nèi)存占用提高查詢效率(*)
  7. 支持用戶指定全局降級策略
  8. 支持獲取單次跳轉(zhuǎn)結(jié)果
  9. 豐富的API和可定制性
  10. 被ARouter管理的頁面心例、攔截器、服務(wù)均無需主動注冊到ARouter鞋囊,被動發(fā)現(xiàn)
  11. 支持Android N推出的Jack編譯鏈

二止后、不支持的功能

  1. 自定義URL解析規(guī)則(考慮支持)
  2. 不能動態(tài)加載代碼模塊和添加路由規(guī)則(考慮支持)
  3. 多路徑支持(不想支持,貌似是導(dǎo)致各種混亂的起因)
  4. 生成映射關(guān)系文檔(考慮支持)

三溜腐、典型應(yīng)用場景

  1. 從外部URL映射到內(nèi)部頁面译株,以及參數(shù)傳遞與解析
  2. 跨模塊頁面跳轉(zhuǎn)微饥,模塊間解耦
  3. 攔截跳轉(zhuǎn)過程,處理登陸古戴、埋點等邏輯
  4. 跨模塊API調(diào)用欠橘,模塊間解耦(注冊ARouter服務(wù)的形式,通過接口互相調(diào)用)

四现恼、基礎(chǔ)功能

  1. 添加依賴和配置

    apply plugin: 'com.neenbedankt.android-apt'

     buildscript {
         repositories {
             jcenter()
         }
         dependencies {
             classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
         }
     }
    
     apt {
         arguments {
             moduleName project.getName();
         }
     }
    
     dependencies {
         apt 'com.alibaba:arouter-compiler:x.x.x'
         compile 'com.alibaba:arouter-api:x.x.x'
         ...
     }
    
  2. 添加注解

     // 在支持路由的頁面肃续、服務(wù)上添加注解(必選)
     // 這是最小化配置,后面有詳細(xì)配置
     @Route(path = "/test/1")
     public class YourActivity extend Activity {
         ...
     }
    
  3. 初始化SDK

     ARouter.init(mApplication); // 盡可能早叉袍,推薦在Application中初始化
    
  4. 發(fā)起路由操作
    // 1. 應(yīng)用內(nèi)簡單的跳轉(zhuǎn)(通過URL跳轉(zhuǎn)在'中階使用'中)
    ARouter.getInstance().build("/test/1").navigation();

     // 2. 跳轉(zhuǎn)并攜帶參數(shù)
     ARouter.getInstance().build("/test/1")
                 .withLong("key1", 666L)
                 .withString("key3", "888")
                 .navigation();
    
  5. 添加混淆規(guī)則(如果使用了Proguard)

     -keep public class com.alibaba.android.arouter.routes.**{*;}
    

五始锚、進階用法

  1. 通過URL跳轉(zhuǎn)

     // 新建一個Activity用于監(jiān)聽Schame事件
     // 監(jiān)聽到Schame事件之后直接傳遞給ARouter即可
     // 也可以做一些自定義玩法,比方說改改URL之類的
     // http://www.example.com/test/1
     public class SchameFilterActivity extends Activity {
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
    
             // 外面用戶點擊的URL
             Uri uri = getIntent().getData();
             // 直接傳遞給ARouter即可
             ARouter.getInstance().build(uri).navigation();
             finish();
         }
     }
    
     // AndroidManifest.xml 中 的參考配置
     <activity android:name=".activity.SchameFilterActivity">
             <!-- Schame -->
             <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>
     </activity>
    
  2. 使用ARouter協(xié)助解析參數(shù)類型

     // URL中的參數(shù)會默認(rèn)以String的形式保存在Bundle中
     // 如果希望ARouter協(xié)助解析參數(shù)(按照不同類型保存進Bundle中)
     // 只需要在需要解析的參數(shù)上添加 @Param 注解
     @Route(path = "/test/1")
     public class Test1Activity extends Activity {
         @Param                   // 聲明之后喳逛,ARouter會從URL中解析對應(yīng)名字的參數(shù)瞧捌,并按照類型存入Bundle
         public String name;
         @Param
         private int age;
         @Param(name = "girl")   // 可以通過name來映射URL中的不同參數(shù)
         private boolean boy;
    
         @Override
         protected void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
    
             name = getIntent().getStringExtra("name");
             age = getIntent().getIntExtra("age", -1);
             boy = getIntent().getBooleanExtra("girl", false);   // 注意:使用映射之后,要從Girl中獲取润文,而不是boy
         }
     }
    
  3. 開啟ARouter參數(shù)自動注入(實驗性功能姐呐,不建議使用,正在開發(fā)保護策略)

     // 首先在Application中重寫 attachBaseContext方法典蝌,并加入ARouter.attachBaseContext();
     @Override
     protected void attachBaseContext(Context base) {
        super.attachBaseContext(base);
    
        ARouter.attachBaseContext();
     }
    
     // 設(shè)置ARouter的時候曙砂,開啟自動注入
     ARouter.enableAutoInject();
    
     // 至此,Activity中的屬性骏掀,將會由ARouter自動注入鸠澈,無需 getIntent().getStringExtra("xxx")等等
    
  4. 聲明攔截器(攔截跳轉(zhuǎn)過程,面向切面搞事情)

     // 比較經(jīng)典的應(yīng)用就是在跳轉(zhuǎn)過程中處理登陸事件截驮,這樣就不需要在目標(biāo)頁重復(fù)做登陸檢查
    
     // 攔截器會在跳轉(zhuǎn)之間執(zhí)行笑陈,多個攔截器會按優(yōu)先級順序依次執(zhí)行
     @Interceptor(priority = 666, name = "測試用攔截器")
     public class TestInterceptor implements IInterceptor {
         /**
          * The operation of this interceptor.
          *
          * @param postcard meta
          * @param callback cb
          */
         @Override
         public void process(Postcard postcard, InterceptorCallback callback) {
             ...
    
             callback.onContinue(postcard);  // 處理完成,交還控制權(quán)
             // callback.onInterrupt(new RuntimeException("我覺得有點異常"));      // 覺得有問題葵袭,中斷路由流程
    
             // 以上兩種至少需要調(diào)用其中一種涵妥,否則會超時跳過
         }
    
         /**
          * Do your init work in this method, it well be call when processor has been load.
          *
          * @param context ctx
          */
         @Override
         public void init(Context context) {
    
         }
     }
    
  5. 處理跳轉(zhuǎn)結(jié)果

     // 通過兩個參數(shù)的navigation方法,可以獲取單次跳轉(zhuǎn)的結(jié)果
     ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
         @Override
         public void onFound(Postcard postcard) {
               ...
         }
    
         @Override
         public void onLost(Postcard postcard) {
             ...
         }
     });
    
  6. 自定義全局降級策略

         // 實現(xiàn)DegradeService接口眶熬,并加上一個Path內(nèi)容任意的注解即可
        @Route(path = "/xxx/xxx") // 必須標(biāo)明注解
         public class DegradeServiceImpl implements DegradeService {
           /**
            * Router has lost.
            *
            * @param postcard meta
            */
           @Override
           public void onLost(Context context, Postcard postcard) {
                 // do something.
           }
    
           /**
            * Do your init work in this method, it well be call when processor has been load.
            *
            * @param context ctx
            */
           @Override
           public void init(Context context) {
    
           }
         }
    
  7. 為目標(biāo)頁面聲明更多信息

     // 我們經(jīng)常需要在目標(biāo)頁面中配置一些屬性妹笆,比方說"是否需要登陸"之類的
     // 可以通過 Route 注解中的 extras 屬性進行擴展块请,這個屬性是一個 int值娜氏,換句話說,單個int有4字節(jié)墩新,也就是32位贸弥,可以配置32個開關(guān)
     // 剩下的可以自行發(fā)揮,通過字節(jié)操作可以標(biāo)識32個開關(guān)
     @Route(path = "/test/1", extras = Consts.XXXX)
    
  8. 使用ARouter管理服務(wù)(一) 暴露服務(wù)

     /**
      * 聲明接口
      */
     public interface IService extends IProvider {
         String hello(String name);
     }
    
     /**
      * 實現(xiàn)接口
      */
     @Route(path = "/service/1", name = "測試服務(wù)")
     public class ServiceImpl implements IService {
    
         @Override
         public String hello(String name) {
             return "hello, " + name;
         }
    
         /**
          * Do your init work in this method, it well be call when processor has been load.
          *
          * @param context ctx
          */
         @Override
         public void init(Context context) {
    
         }
     }
    
  9. 使用ARouter管理服務(wù)(二) 發(fā)現(xiàn)服務(wù)

     1. 可以通過兩種API來獲取Service海渊,分別是ByName绵疲、ByType
     IService service = ARouter.getInstance().navigation(IService.class);    //  ByType
     IService service = (IService) ARouter.getInstance().build("/service/1").navigation(); //  ByName
    
     service.hello("zz");
    
     2. 注意:推薦使用ByName方式獲取Service哲鸳,ByType這種方式寫起來比較方便,但如果存在多實現(xiàn)的情況時盔憨,SDK不保證能獲取到你想要的實現(xiàn)
    
  10. 使用ARouter管理服務(wù)(三) 管理依賴

        可以通過ARouter service包裝您的業(yè)務(wù)邏輯或者sdk徙菠,在service的init方法中初始化您的sdk,不同的sdk使用ARouter的service進行調(diào)用郁岩,
    每一個service在第一次使用的時候會被初始化婿奔,即調(diào)用init方法。
        這樣就可以告別各種亂七八糟的依賴關(guān)系的梳理问慎,只要能調(diào)用到這個service萍摊,那么這個service中所包含的sdk等就已經(jīng)被初始化過了,完全不需要
    關(guān)心各個sdk的初始化順序如叼。
    

六冰木、更多功能

  1. 初始化中的其他設(shè)置

     ARouter.openLog();  // 開啟日志
     ARouter.printStackTrace(); // 打印日志的時候打印線程堆棧
    
  2. 詳細(xì)的API說明

     // 構(gòu)建標(biāo)準(zhǔn)的路由請求
     ARouter.getInstance().build("/home/main").navigation();
    
     // 構(gòu)建標(biāo)準(zhǔn)的路由請求,并指定分組
     ARouter.getInstance().build("/home/main", "ap").navigation();
    
     // 構(gòu)建標(biāo)準(zhǔn)的路由請求笼恰,通過Uri直接解析
     Uri uri;
     ARouter.getInstance().build(uri).navigation();
    
     // 構(gòu)建標(biāo)準(zhǔn)的路由請求踊沸,startActivityForResult
     // navigation的第一個參數(shù)必須是Activity,第二個參數(shù)則是RequestCode
     ARouter.getInstance().build("/home/main", "ap").navigation(this, 5);
    
     // 直接傳遞Bundle
     Bundle params = new Bundle();
     ARouter.getInstance()
                 .build("/home/main")
                 .with(params)
                 .navigation();
    
     // 指定Flag
     ARouter.getInstance()
                 .build("/home/main")
                 .withFlags();
                 .navigation();
    
     // 覺得接口不夠多社证,可以直接拿出Bundle賦值
     ARouter.getInstance()
                 .build("/home/main")
                 .getExtra();
    
     // 使用綠色通道(跳過所有的攔截器)
     ARouter.getInstance().build("/home/main").greenChannal().navigation();
    

附錄

ARouter Github鏈接
Demo apk

  • 最新版本
    arouter-annotation : 1.0.0
    arouter-compiler : 1.0.1
    arouter-api : 1.0.2

  • Gradle依賴

dependencies {
    apt 'com.alibaba:arouter-compiler:1.0.1'
    compile 'com.alibaba:arouter-api:1.0.2'
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末雕沿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子猴仑,更是在濱河造成了極大的恐慌审轮,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件辽俗,死亡現(xiàn)場離奇詭異疾渣,居然都是意外死亡,警方通過查閱死者的電腦和手機崖飘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門榴捡,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人朱浴,你說我怎么就攤上這事吊圾。” “怎么了翰蠢?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵项乒,是天一觀的道長。 經(jīng)常有香客問我梁沧,道長檀何,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮频鉴,結(jié)果婚禮上栓辜,老公的妹妹穿的比我還像新娘。我一直安慰自己垛孔,他們只是感情好藕甩,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著周荐,像睡著了一般辛萍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上羡藐,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天贩毕,我揣著相機與錄音,去河邊找鬼仆嗦。 笑死辉阶,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的瘩扼。 我是一名探鬼主播谆甜,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼集绰!你這毒婦竟也來了规辱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤栽燕,失蹤者是張志新(化名)和其女友劉穎罕袋,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體碍岔,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡浴讯,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蔼啦。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片榆纽。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖捏肢,靈堂內(nèi)的尸體忽然破棺而出奈籽,到底是詐尸還是另有隱情,我是刑警寧澤鸵赫,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布衣屏,位于F島的核電站,受9級特大地震影響奉瘤,放射性物質(zhì)發(fā)生泄漏勾拉。R本人自食惡果不足惜煮甥,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一盗温、第九天 我趴在偏房一處隱蔽的房頂上張望藕赞。 院中可真熱鬧,春花似錦卖局、人聲如沸斧蜕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽批销。三九已至,卻和暖如春染坯,著一層夾襖步出監(jiān)牢的瞬間均芽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工单鹿, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掀宋,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓仲锄,卻偏偏與公主長得像劲妙,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子儒喊,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344

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