Markdown版本筆記 | 我的GitHub首頁 | 我的博客 | 我的微信 | 我的郵箱 |
---|---|---|---|---|
MyAndroidBlogs | baiqiantao | baiqiantao | bqt20094 | baiqiantao@sina.com |
ARouter 路由 組件 跳轉 MD
目錄
簡介
一個用於幫助 Android App 進行組件化改造
的框架 —— 支持模塊間的路由、通信、解耦
模塊 | arouter-api | arouter-compiler | arouter-register | arouter-idea-plugin |
---|---|---|---|---|
2018-12-15 最新版本 | ![]() |
![]() |
![]() |
![]() |
支持的功能
- 支持直接解析
標准URL
進行跳轉,並自動注入參數
到目標頁面中 - 支持
多模塊
工程使用 - 支持添加多個
攔截器
,自定義攔截順序 - 支持
依賴注入
,可單獨作為依賴注入框架使用 - 支持
InstantRun
- 支持
MultiDex
- 映射關系
按組分類、多級管理,按需初始化
- 支持用戶指定
全局降級與局部降級
策略 - 頁面、攔截器、服務等組件均
自動注冊
到框架 - 支持多種方式配置
轉場動畫
- 支持獲取
Fragment
- 完全支持
Kotlin
以及混編 - 支持
第三方 App 加固
(使用 arouter-register 實現自動注冊) - 支持生成
路由文檔
- 提供
IDE 插件
便捷的關聯路徑和目標類
典型應用
- 從
外部URL
映射到內部頁面,以及參數傳遞與解析 跨模塊
頁面跳轉,模塊間解耦攔截
跳轉過程,處理登陸、埋點等邏輯跨模塊API調用
,通過控制反轉來做組件解耦
簡單使用
1、添加依賴和配置
android {
defaultConfig {
...
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
}
dependencies {
api 'com.alibaba:arouter-api:x.x.x' // 需要注意的是api
annotationProcessor 'com.alibaba:arouter-compiler:x.x.x' // 要與compiler匹配使用
}
2、添加注解
// 在支持路由的頁面上添加注解(必選),這里的路徑需要注意的是至少需要有兩級,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity { ... }
3、初始化SDK
if (isDebug()) {
ARouter.openLog(); // 打印日志,這兩行必須寫在init之前,否則這些配置在init過程中將無效
ARouter.openDebug(); // 開啟調試模式(如果在InstantRun模式下運行,必須開啟調試模式!線上版本需要關閉,否則有安全風險)
}
ARouter.init(mApplication);
4、發起路由操作
ARouter.getInstance().build("/test/activity").navigation(); // 應用內簡單的跳轉
ARouter.getInstance().build("/test/1")
.withLong("key1", 666L) // 跳轉並攜帶參數
.withString("key3", "888")
.withObject("key4", new Test("Jack", "Rose"))
.navigation();
5、添加混淆規則
-keep public class com.alibaba.android.arouter.routes.**{*;}
-keep public class com.alibaba.android.arouter.facade.**{*;}
-keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;}
# 如果使用了 byType 的方式獲取 Service,需添加下面規則,保護接口
-keep interface * implements com.alibaba.android.arouter.facade.template.IProvider
# 如果使用了 單類注入,即不定義接口實現 IProvider,需添加下面規則,保護實現
# -keep class * implements com.alibaba.android.arouter.facade.template.IProvider
6、使用 Gradle 插件實現路由表的自動加載(可選)
apply plugin: 'com.alibaba.arouter'
buildscript {
repositories {
jcenter()
}
dependencies {
classpath "com.alibaba:arouter-register:?"
}
}
可選使用,通過 ARouter 提供的注冊插件進行路由表的自動加載(power by AutoRegister),默認通過掃描 dex 的方式進行加載,通過 gradle 插件進行自動注冊可以縮短初始化時間,解決應用加固導致無法直接訪問 dex 文件,初始化失敗的問題。需要注意的是,該插件必須搭配 api 1.3.0 以上版本使用!
7、使用 IDE 插件導航到目標類
在 Android Studio 插件市場中搜索 ARouter Helper
, 或者直接下載文檔上方 最新版本
中列出的 arouter-idea-plugin
zip 安裝包手動安裝,安裝后,插件無任何設置,可以在跳轉代碼的行首找到一個圖標,點擊該圖標,即可跳轉到標識了代碼中路徑的目標類。
進階使用
1、通過URL跳轉
新建一個Activity用於監聽Schame事件,之后直接把url傳遞給ARouter即可
public class SchameFilterActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Uri uri = getIntent().getData();
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>
</activity>
2、解析URL中的參數
URL中不能傳遞Parcelable類型數據,通過ARouter api可以傳遞Parcelable對象
@Route(path = "/test/activity")
public class Test1Activity extends Activity {
@Autowired public String name; // 為每一個參數聲明一個字段,並使用 @Autowired 標注
@Autowired int age;
@Autowired(name = "girl") boolean boy; // 通過name來映射URL中的不同參數
@Autowired TestObj obj; // 支持解析自定義對象,URL中使用json傳遞
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ARouter.getInstance().inject(this); //注入
Log.d("param", name + age + boy); // ARouter會自動對字段進行賦值,無需主動獲取
}
}
如果需要傳遞自定義對象
,新建一個類(並非自定義對象類),然后實現 SerializationService
,並使用@Route
注解標注(方便用戶自行選擇序列化方式),例如:
@Route(path = "/yourservicegroupname/json")
public class JsonServiceImpl implements SerializationService {
@Override
public void init(Context context) {
//可選,用於對序列化框架初始化
}
@Override
public <T> T json2Object(String text, Class<T> clazz) {
return JSON.parseObject(text, clazz); //自行選擇反序列化方式,比如Gson、fastjson
}
@Override
public String object2Json(Object instance) {
return JSON.toJSONString(instance); //自行選擇序列化方式,比如Gson、fastjson
}
}
3、聲明攔截器
攔截跳轉過程,面向切面編程
。
比較經典的應用就是在跳轉過程中處理登陸事件
,這樣就不需要在目標頁重復做登陸檢查
。
攔截器會在跳轉之間
執行,多個攔截器會按優先級順序依次執行。
@Interceptor(priority = 8, name = "測試用攔截器")
public class TestInterceptor implements IInterceptor {
@Override
public void process(Postcard postcard, InterceptorCallback callback) {
//...條件判斷
callback.onContinue(postcard); // 處理完成,交還控制權
// callback.onInterrupt(new RuntimeException("我覺得有點異常")); // 覺得有問題,中斷路由流程
// 以上兩種至少需要調用其中一種,否則不會繼續路由
}
@Override
public void init(Context context) {
// 攔截器的初始化,會在sdk初始化的時候調用該方法,僅會調用一次
}
}
4、處理跳轉結果
使用兩個參數的navigation方法,可以獲取單次跳轉的結果。
ARouter.getInstance().build("/test/1").navigation(this, new NavigationCallback() {
@Override
public void onFound(Postcard postcard) {
//...
}
@Override
public void onLost(Postcard postcard) {
//...
}
});
5、自定義全局降級策略
實現DegradeService接口,並加上一個Path內容任意的注解即可
@Route(path = "/xxx/xxx")
public class DegradeServiceImpl implements DegradeService {
@Override
public void onLost(Context context, Postcard postcard) {
// do something.
}
@Override
public void init(Context context) {
}
}
6、為目標頁面聲明更多信息
我們經常需要在目標頁面中配置一些屬性,比方說"是否需要登陸"之類的,可以通過 Route 注解中的 extras
屬性進行擴展。
這個屬性是一個 int 值,也就是32位,可以配置32個開關。
剩下的可以自行發揮,通過字節操作可以標識32個開關,通過開關標記目標頁面的一些屬性,在攔截器中可以拿到這個標記進行業務邏輯判斷
@Route(path = "/test/activity", extras = Consts.XXXX)
7、通過依賴注入
解耦
服務管理(一) 暴露服務
public interface HelloService extends IProvider {
String sayHello(String name); // 聲明接口,其他組件通過接口來調用服務
}
實現接口
@Route(path = "/yourservicegroupname/hello", name = "測試服務")
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello, " + name;
}
@Override
public void init(Context context) {
}
}
服務管理(二) 發現服務
public class Test {
@Autowired HelloService helloService;
@Autowired(name = "/yourservicegroupname/hello") HelloService helloService2;
HelloService helloService3;
HelloService helloService4;
public Test() {
ARouter.getInstance().inject(this);
}
public void testService() {
// 1、(推薦)使用依賴注入的方式發現服務,通過注解標注字段即可使用,無需主動獲取
// Autowired注解中標注name之后,將會使用byName的方式注入對應的字段,不設置name屬性,會默認使用byType的方式發現服務(當同一接口有多個實現的時候,必須使用byName的方式發現服務)
helloService.sayHello("Vergil");
helloService2.sayHello("Vergil");
// 2、使用依賴查找的方式發現服務,主動去發現服務並使用,下面兩種方式分別是byName和byType
helloService3 = ARouter.getInstance().navigation(HelloService.class);
helloService4 = (HelloService) ARouter.getInstance().build("/yourservicegroupname/hello").navigation();
helloService3.sayHello("Vergil");
helloService4.sayHello("Vergil");
}
}
更多功能
1、初始化中的其他設置
ARouter.openLog(); // 開啟日志
ARouter.openDebug(); // 使用InstantRun的時候,需要打開該開關,上線之后關閉,否則有安全風險
ARouter.printStackTrace(); // 打印日志的時候打印線程堆棧
2、詳細的API說明
ARouter.getInstance().build("/home/main").navigation(); // 構建標准的路由請求
ARouter.getInstance().build("/home/main", "ap").navigation(); // 構建標准的路由請求,並指定分組
ARouter.getInstance().build(uri).navigation(); // 構建標准的路由請求,通過Uri直接解析
ARouter.getInstance().build("/home/main", "ap").navigation(this, 5); // startActivityForResult 形式跳轉
ARouter.getInstance().build("/home/main").with(params).navigation(); // 直接傳遞Bundle
ARouter.getInstance().build("/home/main").withFlags().navigation(); // 指定Flag
ARouter.getInstance().withObject("key", new TestObj("Jack", "Rose")).navigation(); // 對象傳遞
ARouter.getInstance().build("/home/main").getExtra(); // 覺得接口不夠多,可以直接拿出Bundle賦值
ARouter.getInstance().build("/home/main").greenChannel().navigation();// 使用綠色通道(跳過所有的攔截器)
ARouter.setLogger();// 使用自己的日志工具打印日志
ARouter.setExecutor();// 使用自己提供的線程池
Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation(); //獲取Fragment
ARouter.getInstance().build("/test/activity2")
.withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom).navigation(this); //轉場動畫
ARouter.getInstance().build("/test/activity2")
.withOptionsCompat(compat).navigation(this); // 轉場動畫(API16+)
3、獲取原始的URI
String uriStr = getIntent().getStringExtra(ARouter.RAW_URI);
4、重寫跳轉URL
實現 PathReplaceService 接口,並加上一個 Path 內容任意的注解即可
@Route(path = "/xxx/xxx") // 必須標明注解
public class PathReplaceServiceImpl implements PathReplaceService {
/**
* For normal path.
*
* @param path raw path
*/
String forString(String path) {
return path; // 按照一定的規則處理之后返回處理后的結果
}
/**
* For uri type.
*
* @param uri raw uri
*/
Uri forUri(Uri uri) {
return url; // 按照一定的規則處理之后返回處理后的結果
}
}
5、生成路由文檔
生成的文檔路徑:build/generated/source/apt/(debug or release)/com/alibaba/android/arouter/docs/arouter-map-of-${moduleName}.json
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName(), AROUTER_GENERATE_DOC: "enable"]
}
}
}
}
其他
1、路由中的分組概念
- SDK中針對所有的路徑進行分組,只有在分組中的某一個路徑第一次被訪問的時候,
該分組才會被初始化
- 可以通過
@Route
注解主動指定分組,否則使用路徑中第一段字符串/*/
作為分組 - 注意:一旦主動指定分組之后,應用內路由需要使用
ARouter.getInstance().build(path, group)
進行跳轉,手動指定分組,否則無法找到
@Route(path = "/test/1", group = "app")
2、攔截器和服務的異同
- 攔截器和服務所需要實現的接口不同,但是結構類似,都存在
init(Context)
方法,但是兩者的調用時機不同 - 攔截器因為其特殊性,會被任何一次路由所觸發,攔截器會在ARouter初始化的時候異步初始化,如果第一次路由的時候攔截器還沒有初始化結束,路由會
等待
,直到初始化完成。 - 服務沒有該限制,某一服務可能在App整個生命周期中都不會用到,所以服務只有被調用的時候才會觸發初始化操作
3、Kotlin項目中的配置方式
可以參考 module-kotlin 模塊中的寫法
apply plugin: 'kotlin-kapt'
kapt {
arguments {
arg("AROUTER_MODULE_NAME", project.getName())
}
}
dependencies {
compile 'com.alibaba:arouter-api:x.x.x'
kapt 'com.alibaba:arouter-compiler:x.x.x'
}
Q&A
1、"W/ARouter::: ARouter::No postcard![ ]"
2、"W/ARouter::: ARouter::There is no route match the path [/xxx/xxx], in group [xxx][ ]"
3、開啟InstantRun之后無法跳轉(高版本Gradle插件下無法跳轉)?
因為開啟InstantRun之后,很多類文件不會放在原本的dex中,需要單獨去加載,ARouter默認不會去加載這些文件,因為安全原因,只有在開啟了openDebug之后,ARouter才回去加載InstantRun產生的文件,所以在以上的情況下,需要在init之前調用openDebug。
4、TransformException:java.util.zip.ZipException: duplicate entry ....
ARouter有按組加載的機制,ARouter允許一個module中存在多個分組,但是不允許多個module中存在相同的分組,會導致映射文件沖突。
5、Kotlin類中的字段無法注入如何解決?
首先,Kotlin中的字段是可以自動注入的,但是注入代碼為了減少反射,使用的字段賦值的方式來注入的,Kotlin默認會生成set/get方法,並把屬性設置為private,所以只要保證Kotlin中字段可見性不是private即可,簡單解決可以在字段上添加 @JvmField
。
6、通過URL跳轉之后,在intent中拿不到參數如何解決?
需要注意的是,如果不使用自動注入,那么可以不寫 ARouter.getInstance().inject(this)
,但是需要取值的字段仍然需要標上 @Autowired
注解,因為只有標上注解之后,ARouter才能知道以哪一種數據類型提取URL中的參數並放入Intent中,這樣您才能在intent中獲取到對應的參數。
7、新增頁面之后,無法跳轉?
ARouter加載Dex中的映射文件會有一定耗時,所以ARouter會緩存映射文件,直到新版本升級(版本號或者versionCode變化),而如果是開發版本,ARouter 每次啟動都會重新加載映射文件,開發階段一定要打開 Debug 功能。
2018-12-15