[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),隨著時間的推移,也會遇到一些問題:
- 集中式的URL管理:談到集中式的管理,總是比較蛋疼,多人協(xié)同開發(fā)的時候,大家都去AndroidManifest.xml中定義各種IntentFilter,使用隱式Intent,最終發(fā)現(xiàn)AndroidManifest.xml中充斥著各種Schame,各種Path,需要經(jīng)常解決Path重疊覆蓋、過多的Activity被導(dǎo)出,引發(fā)安全風(fēng)險等問題
- 可配置性較差:Manifest限制于xml格式,書寫麻煩,配置復(fù)雜,可以自定義的東西也較少
- 跳轉(zhuǎn)過程中無法插手:直接通過Intent的方式跳轉(zhuǎn),跳轉(zhuǎn)過程開發(fā)者無法干預(yù),一些面向切面的事情難以實施,比方說登錄、埋點這種非常通用的邏輯,在每個子頁面中判斷又很不合理,畢竟activity已經(jīng)實例化了
- 跨模塊無法顯式依賴:在App小有規(guī)模的時候,我們會對App做水平拆分,按照業(yè)務(wù)拆分成多個子模塊,之間完全解耦,通過打包流程控制App功能,這樣方便應(yīng)對大團隊多人協(xié)作,互相邏輯不干擾,這時候只能依賴隱式Intent跳轉(zhuǎn),書寫麻煩,成功與否難以控制。
另一個輪子
為了解決以上問題,我們需要一款能夠解耦、簡單、功能多、定制性較強、支持?jǐn)r截邏輯的路由組件:我們選擇了Alibaba的ARouter。
一、功能介紹
- 支持直接解析URL進行跳轉(zhuǎn)、參數(shù)按類型解析到Bundle,支持Java基本類型(*)
- 支持應(yīng)用內(nèi)的標(biāo)準(zhǔn)頁面跳轉(zhuǎn),API接近Android原生接口
- 支持多模塊工程中使用,允許分別打包,包結(jié)構(gòu)符合Android包規(guī)范即可(*)
- 支持跳轉(zhuǎn)過程中插入自定義攔截邏輯,自定義攔截順序(*)
- 支持服務(wù)托管,通過ByName,ByType兩種方式獲取服務(wù)實例,方便面向接口開發(fā)與跨模塊調(diào)用解耦(*)
- 映射關(guān)系按組分類、多級管理,按需初始化,減少內(nèi)存占用提高查詢效率(*)
- 支持用戶指定全局降級策略
- 支持獲取單次跳轉(zhuǎn)結(jié)果
- 豐富的API和可定制性
- 被ARouter管理的頁面、攔截器、服務(wù)均無需主動注冊到ARouter,被動發(fā)現(xiàn)
- 支持Android N推出的Jack編譯鏈
二、不支持的功能
- 自定義URL解析規(guī)則(考慮支持)
- 不能動態(tài)加載代碼模塊和添加路由規(guī)則(考慮支持)
- 多路徑支持(不想支持,貌似是導(dǎo)致各種混亂的起因)
- 生成映射關(guān)系文檔(考慮支持)
三、典型應(yīng)用場景
- 從外部URL映射到內(nèi)部頁面,以及參數(shù)傳遞與解析
- 跨模塊頁面跳轉(zhuǎn),模塊間解耦
- 攔截跳轉(zhuǎn)過程,處理登陸、埋點等邏輯
- 跨模塊API調(diào)用,模塊間解耦(注冊ARouter服務(wù)的形式,通過接口互相調(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'
...
}
添加注解
// 在支持路由的頁面、服務(wù)上添加注解(必選)
// 這是最小化配置,后面有詳細(xì)配置
@Route(path = "/test/1")
public class YourActivity extend Activity {
...
}
初始化SDK
ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化
發(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();
添加混淆規(guī)則(如果使用了Proguard)
-keep public class com.alibaba.android.arouter.routes.**{*;}
五、進階用法
通過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>
使用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
}
}
開啟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")等等
聲明攔截器(攔截跳轉(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) {
}
}
處理跳轉(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) {
...
}
});
自定義全局降級策略
// 實現(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) {
}
}
為目標(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)
使用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) {
}
}
使用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)
使用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的初始化順序。
六、更多功能
初始化中的其他設(shè)置
ARouter.openLog(); // 開啟日志 ARouter.printStackTrace(); // 打印日志的時候打印線程堆棧
詳細(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-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'
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android開發(fā)Kotlin語言協(xié)程中的并發(fā)問題和互斥鎖
Android開發(fā)Kotlin語言提供了多種機制來處理并發(fā)和同步,其中包括高層次和低層次的工具,對于常規(guī)的并發(fā)任務(wù),可以利用 Kotlin 協(xié)程提供的結(jié)構(gòu)化并發(fā)方式,而對于需要更低層次的鎖定機制,可以使用Mutex(互斥鎖)來實現(xiàn)對共享資源的線程安全訪問2024-06-06
【Android 基礎(chǔ)】詳解Animation 動畫介紹和實現(xiàn)
這篇文章主要介紹了【Android 基礎(chǔ)】詳解Animation 動畫介紹和實現(xiàn) ,對于想要學(xué)習(xí)android開發(fā)的同學(xué)具有一定的參考價值,有需要的可以了解一下。2016-12-12
Android 實現(xiàn)調(diào)用系統(tǒng)照相機拍照和錄像的功能
這篇文章主要介紹了Android 實現(xiàn)調(diào)用系統(tǒng)照相機拍照和錄像的功能的相關(guān)資料,需要的朋友可以參考下2016-11-11
Android RecyclerView自定義上拉和下拉刷新效果
這篇文章主要為大家詳細(xì)介紹了Android RecyclerView自定義上拉和下拉刷新效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-02-02
Android之ArcSlidingHelper制作圓弧滑動效果
這篇文章主要介紹了Android之ArcSlidingHelper制作圓弧滑動效果,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-08-08
Flutter?Widget?之FocusableActionDetector使用詳解
這篇文章主要為大家介紹了Flutter?Widget?之FocusableActionDetector使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-11-11
Android實現(xiàn)單頁面浮層可拖動view的一種方法
本篇文章主要介紹了Android實現(xiàn)單頁面浮層可拖動view的一種方法,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-10-10

