[Alibaba-ARouter]淺談簡(jiǎn)單好用的Android頁(yè)面路由框架
開(kāi)發(fā)一款A(yù)pp,總會(huì)遇到各種各樣的需求和業(yè)務(wù),這時(shí)候選擇一個(gè)簡(jiǎn)單好用的輪子,就可以事半功倍
前言
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開(kāi)發(fā)中,最常見(jiàn)也是最常用的功能就是頁(yè)面的跳轉(zhuǎn),我們經(jīng)常需要面對(duì)從瀏覽器或者其他App跳轉(zhuǎn)到自己App中頁(yè)面的需求,不過(guò)就算是簡(jiǎn)簡(jiǎn)單單的頁(yè)面跳轉(zhuǎn),隨著時(shí)間的推移,也會(huì)遇到一些問(wèn)題:
- 集中式的URL管理:談到集中式的管理,總是比較蛋疼,多人協(xié)同開(kāi)發(fā)的時(shí)候,大家都去AndroidManifest.xml中定義各種IntentFilter,使用隱式Intent,最終發(fā)現(xiàn)AndroidManifest.xml中充斥著各種Schame,各種Path,需要經(jīng)常解決Path重疊覆蓋、過(guò)多的Activity被導(dǎo)出,引發(fā)安全風(fēng)險(xiǎn)等問(wèn)題
- 可配置性較差:Manifest限制于xml格式,書寫麻煩,配置復(fù)雜,可以自定義的東西也較少
- 跳轉(zhuǎn)過(guò)程中無(wú)法插手:直接通過(guò)Intent的方式跳轉(zhuǎn),跳轉(zhuǎn)過(guò)程開(kāi)發(fā)者無(wú)法干預(yù),一些面向切面的事情難以實(shí)施,比方說(shuō)登錄、埋點(diǎn)這種非常通用的邏輯,在每個(gè)子頁(yè)面中判斷又很不合理,畢竟activity已經(jīng)實(shí)例化了
- 跨模塊無(wú)法顯式依賴:在App小有規(guī)模的時(shí)候,我們會(huì)對(duì)App做水平拆分,按照業(yè)務(wù)拆分成多個(gè)子模塊,之間完全解耦,通過(guò)打包流程控制App功能,這樣方便應(yīng)對(duì)大團(tuán)隊(duì)多人協(xié)作,互相邏輯不干擾,這時(shí)候只能依賴隱式Intent跳轉(zhuǎn),書寫麻煩,成功與否難以控制。
另一個(gè)輪子
為了解決以上問(wèn)題,我們需要一款能夠解耦、簡(jiǎn)單、功能多、定制性較強(qiáng)、支持?jǐn)r截邏輯的路由組件:我們選擇了Alibaba的ARouter。
一、功能介紹
- 支持直接解析URL進(jìn)行跳轉(zhuǎn)、參數(shù)按類型解析到Bundle,支持Java基本類型(*)
- 支持應(yīng)用內(nèi)的標(biāo)準(zhǔn)頁(yè)面跳轉(zhuǎn),API接近Android原生接口
- 支持多模塊工程中使用,允許分別打包,包結(jié)構(gòu)符合Android包規(guī)范即可(*)
- 支持跳轉(zhuǎn)過(guò)程中插入自定義攔截邏輯,自定義攔截順序(*)
- 支持服務(wù)托管,通過(guò)ByName,ByType兩種方式獲取服務(wù)實(shí)例,方便面向接口開(kāi)發(fā)與跨模塊調(diào)用解耦(*)
- 映射關(guān)系按組分類、多級(jí)管理,按需初始化,減少內(nèi)存占用提高查詢效率(*)
- 支持用戶指定全局降級(jí)策略
- 支持獲取單次跳轉(zhuǎn)結(jié)果
- 豐富的API和可定制性
- 被ARouter管理的頁(yè)面、攔截器、服務(wù)均無(wú)需主動(dòng)注冊(cè)到ARouter,被動(dòng)發(fā)現(xiàn)
- 支持Android N推出的Jack編譯鏈
二、不支持的功能
- 自定義URL解析規(guī)則(考慮支持)
- 不能動(dòng)態(tài)加載代碼模塊和添加路由規(guī)則(考慮支持)
- 多路徑支持(不想支持,貌似是導(dǎo)致各種混亂的起因)
- 生成映射關(guān)系文檔(考慮支持)
三、典型應(yīng)用場(chǎng)景
- 從外部URL映射到內(nèi)部頁(yè)面,以及參數(shù)傳遞與解析
- 跨模塊頁(yè)面跳轉(zhuǎn),模塊間解耦
- 攔截跳轉(zhuǎn)過(guò)程,處理登陸、埋點(diǎn)等邏輯
- 跨模塊API調(diào)用,模塊間解耦(注冊(cè)ARouter服務(wù)的形式,通過(guò)接口互相調(diào)用)
四、基礎(chǔ)功能
添加依賴和配置
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' ... }
添加注解
// 在支持路由的頁(yè)面、服務(wù)上添加注解(必選) // 這是最小化配置,后面有詳細(xì)配置 @Route(path = "/test/1") public class YourActivity extend Activity { ... }
初始化SDK
ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化
發(fā)起路由操作
// 1. 應(yīng)用內(nèi)簡(jiǎn)單的跳轉(zhuǎn)(通過(guò)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();
添加混淆規(guī)則(如果使用了Proguard)
-keep public class com.alibaba.android.arouter.routes.**{*;}
五、進(jìn)階用法
通過(guò)URL跳轉(zhuǎn)
// 新建一個(gè)Activity用于監(jiān)聽(tīng)Schame事件 // 監(jiān)聽(tīng)到Schame事件之后直接傳遞給ARouter即可 // 也可以做一些自定義玩法,比方說(shuō)改改URL之類的 // http://www.example.com/test/1 public class SchameFilterActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 外面用戶點(diǎn)擊的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>
使用ARouter協(xié)助解析參數(shù)類型
// URL中的參數(shù)會(huì)默認(rèn)以String的形式保存在Bundle中 // 如果希望ARouter協(xié)助解析參數(shù)(按照不同類型保存進(jìn)Bundle中) // 只需要在需要解析的參數(shù)上添加 @Param 注解 @Route(path = "/test/1") public class Test1Activity extends Activity { @Param // 聲明之后,ARouter會(huì)從URL中解析對(duì)應(yīng)名字的參數(shù),并按照類型存入Bundle public String name; @Param private int age; @Param(name = "girl") // 可以通過(guò)name來(lái)映射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 } }
開(kāi)啟ARouter參數(shù)自動(dòng)注入(實(shí)驗(yàn)性功能,不建議使用,正在開(kāi)發(fā)保護(hù)策略)
// 首先在Application中重寫 attachBaseContext方法,并加入ARouter.attachBaseContext(); @Override protected void attachBaseContext(Context base) { super.attachBaseContext(base); ARouter.attachBaseContext(); } // 設(shè)置ARouter的時(shí)候,開(kāi)啟自動(dòng)注入 ARouter.enableAutoInject(); // 至此,Activity中的屬性,將會(huì)由ARouter自動(dòng)注入,無(wú)需 getIntent().getStringExtra("xxx")等等
聲明攔截器(攔截跳轉(zhuǎn)過(guò)程,面向切面搞事情)
// 比較經(jīng)典的應(yīng)用就是在跳轉(zhuǎn)過(guò)程中處理登陸事件,這樣就不需要在目標(biāo)頁(yè)重復(fù)做登陸檢查 // 攔截器會(huì)在跳轉(zhuǎn)之間執(zhí)行,多個(gè)攔截器會(huì)按優(yōu)先級(jí)順序依次執(zhí)行 @Interceptor(priority = 666, name = "測(cè)試用攔截器") 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("我覺(jué)得有點(diǎn)異常")); // 覺(jué)得有問(wèn)題,中斷路由流程 // 以上兩種至少需要調(diào)用其中一種,否則會(huì)超時(shí)跳過(guò) } /** * 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) { } }
處理跳轉(zhuǎn)結(jié)果
// 通過(guò)兩個(gè)參數(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) { ... } });
自定義全局降級(jí)策略
// 實(shí)現(xiàn)DegradeService接口,并加上一個(gè)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) { } }
為目標(biāo)頁(yè)面聲明更多信息
// 我們經(jīng)常需要在目標(biāo)頁(yè)面中配置一些屬性,比方說(shuō)"是否需要登陸"之類的 // 可以通過(guò) Route 注解中的 extras 屬性進(jìn)行擴(kuò)展,這個(gè)屬性是一個(gè) int值,換句話說(shuō),單個(gè)int有4字節(jié),也就是32位,可以配置32個(gè)開(kāi)關(guān) // 剩下的可以自行發(fā)揮,通過(guò)字節(jié)操作可以標(biāo)識(shí)32個(gè)開(kāi)關(guān) @Route(path = "/test/1", extras = Consts.XXXX)
使用ARouter管理服務(wù)(一) 暴露服務(wù)
/** * 聲明接口 */ public interface IService extends IProvider { String hello(String name); } /** * 實(shí)現(xiàn)接口 */ @Route(path = "/service/1", name = "測(cè)試服務(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) { } }
使用ARouter管理服務(wù)(二) 發(fā)現(xiàn)服務(wù)
1. 可以通過(guò)兩種API來(lái)獲取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這種方式寫起來(lái)比較方便,但如果存在多實(shí)現(xiàn)的情況時(shí),SDK不保證能獲取到你想要的實(shí)現(xiàn)
使用ARouter管理服務(wù)(三) 管理依賴
可以通過(guò)ARouter service包裝您的業(yè)務(wù)邏輯或者sdk,在service的init方法中初始化您的sdk,不同的sdk使用ARouter的service進(jìn)行調(diào)用,每一個(gè)service在第一次使用的時(shí)候會(huì)被初始化,即調(diào)用init方法。
這樣就可以告別各種亂七八糟的依賴關(guān)系的梳理,只要能調(diào)用到這個(gè)service,那么這個(gè)service中所包含的sdk等就已經(jīng)被初始化過(guò)了,完全不需要關(guān)心各個(gè)sdk的初始化順序。
六、更多功能
初始化中的其他設(shè)置
ARouter.openLog(); // 開(kāi)啟日志 ARouter.printStackTrace(); // 打印日志的時(shí)候打印線程堆棧
詳細(xì)的API說(shuō)明
// 構(gòu)建標(biāo)準(zhǔn)的路由請(qǐng)求 ARouter.getInstance().build("/home/main").navigation(); // 構(gòu)建標(biāo)準(zhǔn)的路由請(qǐng)求,并指定分組 ARouter.getInstance().build("/home/main", "ap").navigation(); // 構(gòu)建標(biāo)準(zhǔn)的路由請(qǐng)求,通過(guò)Uri直接解析 Uri uri; ARouter.getInstance().build(uri).navigation(); // 構(gòu)建標(biāo)準(zhǔn)的路由請(qǐng)求,startActivityForResult // navigation的第一個(gè)參數(shù)必須是Activity,第二個(gè)參數(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(); // 覺(jué)得接口不夠多,可以直接拿出Bundle賦值 ARouter.getInstance() .build("/home/main") .getExtra(); // 使用綠色通道(跳過(guò)所有的攔截器) ARouter.getInstance().build("/home/main").greenChannal().navigation();
附錄
最新版本
- 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' }
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
android 實(shí)現(xiàn)控件左右或上下抖動(dòng)教程
這篇文章主要介紹了android 實(shí)現(xiàn)控件左右或上下抖動(dòng)教程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03Android音頻開(kāi)發(fā)之音頻采集的實(shí)現(xiàn)示例
本篇文章主要介紹了Android音頻開(kāi)發(fā)之音頻采集的實(shí)現(xiàn)示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04Android指紋識(shí)別功能深入淺出分析到實(shí)戰(zhàn)(6.0以下系統(tǒng)解決方案)
指紋識(shí)別在現(xiàn)實(shí)應(yīng)用中已經(jīng)很多了,本篇文章主要介紹了Android指紋識(shí)別功能,具有一定的參考價(jià)值,有需要的可以了解一下。2016-11-11Android Studio中導(dǎo)入JNI生成的.so庫(kù)的實(shí)現(xiàn)方法
這篇文章主要介紹了Android Studio中導(dǎo)入JNI生成的.so庫(kù)的實(shí)現(xiàn)方法的相關(guān)資料,這里不僅提供實(shí)現(xiàn)方案并提供了實(shí)現(xiàn)的方法,需要的朋友可以參考下2017-07-07Android開(kāi)發(fā)模仿qq視頻通話懸浮按鈕(實(shí)例代碼)
這篇文章主要介紹了Android開(kāi)發(fā)模仿qq視頻通話懸浮按鈕功能的實(shí)例代碼,需要的的朋友參考下2017-02-02Android布局技巧之include、merge與ViewStub標(biāo)簽的巧用
Android 官方提供了三個(gè)用來(lái)優(yōu)化布局的標(biāo)簽,分別是include、merge與ViewStub,下面這篇文章主要給大家介紹了關(guān)于Android布局技巧之include、merge與ViewStub標(biāo)簽巧用的相關(guān)資料,需要的朋友可以參考下2018-06-06Android startService的使用與Service生命周期案例詳解
這篇文章主要介紹了Android startService的使用與Service生命周期案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-09-09