Android ARouter路由框架解析
一、ARouter介紹及主要應(yīng)用場景:
1、介紹:
是ARouter是阿里巴巴開源的Android平臺中對頁面、服務(wù)提供路由功能的中間件,提倡的是簡單且夠用。
2、原生的路由方案存在的問題
首先談一談原生的路由方案存在的問題以及為什么需要路由框架。我們所使用的原生路由方案一般是通過顯式intent和隱式intent兩種方式實現(xiàn)的,而在顯式intent的情況下,因為會存在直接的類依賴的問題,導(dǎo)致耦合非常嚴重;而在隱式intent情況下,則會出現(xiàn)規(guī)則集中式管理,導(dǎo)致協(xié)作變得非常困難。而且一般而言配置規(guī)則都是在Manifest中的,這就導(dǎo)致了擴展性較差。除此之外,使用原生的路由方案會出現(xiàn)跳轉(zhuǎn)過程無法控制的問題,因為一旦使用了StartActivity()就無法插手其中任何環(huán)節(jié)了,只能交給系統(tǒng)管理,這就導(dǎo)致了在跳轉(zhuǎn)失敗的情況下無法降級,而是會直接拋出運營級的異常。
這時候如果考慮使用自定義的路由組件就可以解決以上的一些問題,比如通過URL索引就可以解決類依賴的問題;通過分布式管理頁面配置可以解決隱式intent中集中式管理Path的問題;自己實現(xiàn)整個路由過程也可以擁有良好的擴展性,還可以通過AOP的方式解決跳轉(zhuǎn)過程無法控制的問題,與此同時也能夠提供非常靈活的降級方式。
3、為什么要用路由組件
為什么要用路由組件
前面提到的主要是開發(fā)與協(xié)作中的問題,而使用一款路由框架時還會涉及到其他的兩個大方面:一方面是組件化,而另一方面就是Native和H5的問題。剛才所提到的主要是開發(fā)和協(xié)作中作為開發(fā)者所需要面對的問題,而一旦一款A(yù)PP達到一定體量的時候,業(yè)務(wù)就會膨脹得比較嚴重,而開發(fā)團隊的規(guī)模也會越來越大,這時候一般都會提出組件化的概念。組件化就是將APP按照一定的功能和業(yè)務(wù)拆分成多個小組件,不同的組件由不同的開發(fā)小組來負責,這樣就可以解決大型APP開發(fā)過程中的開發(fā)與協(xié)作的問題,將這些問題分散到小的APP中。目前而言組件化已經(jīng)有非常多比較成熟的方案了,而自定義路由框架也可以非常好地解決整個APP完成組件化之后模塊之間沒有耦合的問題,因為沒有耦合時使用原生的路由方案肯定是不可以的。
另外一個問題就是Native與H5的問題,因為現(xiàn)在的APP很少是純Native的,也很少會有純H5的,一般情況下都是將兩者進行結(jié)合。這時候就需要非常便捷并且統(tǒng)一的跳轉(zhuǎn)方案,因為在H5中是無法使用StartActivity()跳轉(zhuǎn)到Native頁面的,而從Native跳轉(zhuǎn)到H5頁面也只能通過配置瀏覽器的方式實現(xiàn)。
4、路由框架的特點
為了解決以上的問題就需要實現(xiàn)一個自定義的路由框架,而路由框架一般都具有以下的三種特點:
- 分發(fā):把一個URL或者請求按照一定的規(guī)則分配給一個服務(wù)或者頁面來處理,這個流程就是分發(fā),分發(fā)是路由框架最基本的功能,當然也可以理解成為簡單的跳轉(zhuǎn)。
- 管理:將組件和頁面按照一定的規(guī)則管理起來,在分發(fā)的時候提供搜索、加載、修改等操作,這部分就是管理,也是路由框架的基礎(chǔ),上層功能都是建立在管理之上。
- 控制:就像路由器一樣,路由的過程中,會有限速、屏蔽等一些控制操作,路由框架也需要在路由的過程中,對路由操作做一些定制性的擴展,比方剛才提到的AOP,后期的功能更新,也是圍繞這個部分來做的。
5、ARouter的7個優(yōu)勢
優(yōu)勢一:直接解析URL路由,解析參數(shù)并賦值到對應(yīng)目標字段的頁面中。
優(yōu)勢二:支持多模塊項目,因為現(xiàn)在很少有APP是單模塊的項目,一般都是多模塊單工程的,由不同的團隊負責不同的模塊開發(fā),這時候支持多模塊項目開發(fā)就顯得尤為重要。
優(yōu)勢三:支持InstantRun,目前很多路由框架并不支持InstantRun,而InstantRun是Google在AndroidStudio2.0阿爾法版本中提供的新功能,其類似于代碼的日更新,其只不過面向的是開發(fā)過程,這樣做可以在開發(fā)的過程中減少開發(fā)和編譯的次數(shù),可以簡單地將代碼修改即時地同步到APK中,從而可以大規(guī)模降低開發(fā)復(fù)雜度。
優(yōu)勢四:允許自定義攔截器,ARouter是支持攔截器的,而攔截器其實就是AOP的實現(xiàn),可以自定義多個攔截器解決一些面向行為編程上出現(xiàn)的問題。
優(yōu)勢五:ARouter可以提供IoC容器,IoC其實就是控制反轉(zhuǎn),這一部分做過服務(wù)端開發(fā)的朋友可能比較了解,因為服務(wù)端開發(fā)經(jīng)常用到的Spring框架能夠提供的一個非常重要的能力就是控制反轉(zhuǎn)。
優(yōu)勢六:映射關(guān)系自動注冊,在頁面不是很多的小型APP上面,自動注冊并不會體現(xiàn)出太大優(yōu)勢,但是對于大型APP而言,可能頁面數(shù)量已經(jīng)達到的幾十個或者數(shù)百個,在這樣的情況下,自動注冊就顯得非常重要了,因為不可能將每一個頁面都通過代碼的方式進行注冊。
優(yōu)勢七:靈活的降級策略,ARouter可以提供很多種降級策略供用戶自行選擇,而原生的路由方案存在無法靈活降級的問題,StartActivity()一旦失敗將會拋出運營級異常。
6、應(yīng)用場景:
- 從外部URL映射到內(nèi)部頁面,以及參數(shù)傳遞與解析
- 跨模塊頁面跳轉(zhuǎn),模塊間解耦
- 攔截跳轉(zhuǎn)過程,處理登陸、埋點等邏輯
- 跨模塊API調(diào)用,通過控制反轉(zhuǎn)來做組件解耦
GitHub:https://github.com/alibaba/ARouter
二、ARouter的基本使用步驟:
1、添加依賴和配置
android { defaultConfig { ... javaCompileOptions { annotationProcessorOptions { arguments = [ moduleName : project.getName() ] } } } } dependencies { // 替換成最新版本, 需要注意的是api // 要與compiler匹配使用,均使用最新版可以保證兼容 compile 'com.alibaba:arouter-api:x.x.x' annotationProcessor 'com.alibaba:arouter-compiler:x.x.x' ... } // 替換成最新版本, 需要注意的是api // 要與compiler匹配使用,均使用最新版可以保證兼容 compile 'com.alibaba:arouter-api:x.x.x' annotationProcessor 'com.alibaba:arouter-compiler:x.x.x'
這里替換相應(yīng)的Api和compiler
2、添加注解及初始化
ARouter初始化
if (isDebug()) { // 這兩行必須寫在init之前,否則這些配置在init過程中將無效 ARouter.openLog(); // 打印日志 ARouter.openDebug(); // 開啟調(diào)試模式(如果在InstantRun模式下運行,必須開啟調(diào)試模式!線上版本需要關(guān)閉,否則有安全風險) } ARouter.init(mApplication); // 盡可能早,推薦在Application中初始化
在需要跳轉(zhuǎn)的Activity上進行注解配置
// 在支持路由的頁面上添加注解(必選) // 這里的路徑需要注意的是至少需要有兩級,/xx/xx @Route(path = "/com/ARouter2Activity") public class ARouter2Activity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_arouter2); //.... }
應(yīng)用中的簡單跳轉(zhuǎn)
ARouter.getInstance().build("/com/ARouter2Activity").navigation();
應(yīng)用中攜帶參數(shù)的跳轉(zhuǎn)
ARouter.getInstance().build("/com/ARouter2Activity") .withString("name","onex") .withInt("age",22) .navigation();
在目標Activity中接受相關(guān)的參數(shù)
為每一個參數(shù)聲明一個字段,并使用 @Autowired 標注
URL中不能傳遞Parcelable類型數(shù)據(jù),通過ARouter api可以傳遞
Parcelable對象
@Route(path = "/com/ARouter2Activity") public class ARouter2Activityextends Activity { @Autowired public String name; @Autowired int age; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ARouter.getInstance().inject(this); // ARouter會自動對字段進行賦值,無需主動獲取 Log.d("param", name + age + boy); } }
這里需要注意的是
ARouter.getInstance().inject(this);
三、進階使用
1、Activity之間傳遞自定義對象
如果需要傳遞自定義對象,需要實現(xiàn) SerializationService,并使用@Route注解標注(方便用戶自行選擇序列化方式)
@Route(path = "/service/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); } @Override public String object2Json(Object instance) { return JSON.toJSONString(instance); } }
以下是ARouter支持的傳遞的數(shù)據(jù)類型
TestParcelable testParcelable = new TestParcelable("jack", 666); TestObj testObj = new TestObj("Rose", 777); List<TestObj> objList = new ArrayList<>(); objList.add(testObj); Map<String, List<TestObj>> map = new HashMap<>(); map.put("testMap", objList); ARouter.getInstance().build("/test/activity1") .withString("name", "老王") .withInt("age", 18) .withBoolean("boy", true) .withLong("high", 180) .withString("url", "https://a.b.c") .withParcelable("pac", testParcelable) .withObject("obj", testObj) .withObject("objList", objList) .withObject("map", map) .navigation();
需要注意的是:我們在傳遞的時候使用的是什么樣的KEY,在目標Activity的地方使用相同的KEY,進行聲明變量例如:
Man man = new Man("OneX","男"); ARouter.getInstance().build("/com/ARouter2Activity") .withObject("obj",man) .navigation();
目標頁面Code
@Autowired Man obj;
2、通過Url進行跳轉(zhuǎn)
新建一個Activity用于監(jiān)聽Schame事件,之后直接把url傳遞給ARouter即可(類似于轉(zhuǎn)發(fā)路由的Activity)
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>
3、設(shè)置攔截器
比較經(jīng)典的應(yīng)用就是在跳轉(zhuǎn)過程中處理登陸事件,這樣就不需要在目標頁重復(fù)做登陸檢攔截器會在跳轉(zhuǎn)之間執(zhí)行
多個攔截器會按優(yōu)先級順序依次執(zhí)行
@Interceptor(priority = 8, name = "測試用攔截器") public class TestInterceptor implements IInterceptor { @Override public void process(Postcard postcard, InterceptorCallback callback) { ... callback.onContinue(postcard); // 處理完成,交還控制權(quán) // callback.onInterrupt(new RuntimeException("我覺得有點異常")); // 覺得有問題,中斷路由流程 // 以上兩種至少需要調(diào)用其中一種,否則不會繼續(xù)路由 } @Override public void init(Context context) { // 攔截器的初始化,會在sdk初始化的時候調(diào)用該方法,僅會調(diào)用一次 } }
4、監(jiān)聽路由跳轉(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) { ... } });
5、獲取Fragment的對象
// 獲取Fragment Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();
6、通過依賴注入解耦:服務(wù)管理,暴露服務(wù)
// 聲明接口,其他組件通過接口來調(diào)用服務(wù) public interface HelloService extends IProvider { String sayHello(String name); } // 實現(xiàn)接口 @Route(path = "/service/hello", name = "測試服務(wù)") public class HelloServiceImpl implements HelloService { @Override public String sayHello(String name) { return "hello, " + name; } @Override public void init(Context context) { } }
在Class中的相關(guān)使用
public class Test { @Autowired HelloService helloService; @Autowired(name = "/service/hello") HelloService helloService2; HelloService helloService3; HelloService helloService4; public Test() { ARouter.getInstance().inject(this); } public void testService() { // 1. (推薦)使用依賴注入的方式發(fā)現(xiàn)服務(wù),通過注解標注字段,即可使用,無需主動獲取 // Autowired注解中標注name之后,將會使用byName的方式注入對應(yīng)的字段,不設(shè)置name屬性,會默認使用byType的方式發(fā)現(xiàn)服務(wù)(當同一接口有多個實現(xiàn)的時候,必須使用byName的方式發(fā)現(xiàn)服務(wù)) helloService.sayHello("Vergil"); helloService2.sayHello("Vergil"); // 2. 使用依賴查找的方式發(fā)現(xiàn)服務(wù),主動去發(fā)現(xiàn)服務(wù)并使用,下面兩種方式分別是byName和byType helloService3 = ARouter.getInstance().navigation(HelloService.class); helloService4 = (HelloService) ARouter.getInstance().build("/service/hello").navigation(); helloService3.sayHello("Vergil"); helloService4.sayHello("Vergil"); } }
四、注意事項及混淆配置
1、初始化中的其他配置
ARouter.openLog(); // 開啟日志 ARouter.openDebug(); // 使用InstantRun的時候,需要打開該開關(guān),上線之后關(guān)閉,否則有安全風險 ARouter.printStackTrace(); // 打印日志的時候打印線程堆棧
2、詳細API:
// 構(gòu)建標準的路由請求 ARouter.getInstance().build("/home/main").navigation(); // 構(gòu)建標準的路由請求,并指定分組 ARouter.getInstance().build("/home/main", "ap").navigation(); // 構(gòu)建標準的路由請求,通過Uri直接解析 Uri uri; ARouter.getInstance().build(uri).navigation(); // 構(gòu)建標準的路由請求,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(); // 獲取Fragment Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation(); // 對象傳遞 ARouter.getInstance() .withObject("key", new TestObj("Jack", "Rose")) .navigation(); // 覺得接口不夠多,可以直接拿出Bundle賦值 ARouter.getInstance() .build("/home/main") .getExtra(); // 轉(zhuǎn)場動畫(常規(guī)方式) ARouter.getInstance() .build("/test/activity2") .withTransition(R.anim.slide_in_bottom, R.anim.slide_out_bottom) .navigation(this); // 轉(zhuǎn)場動畫(API16+) ActivityOptionsCompat compat = ActivityOptionsCompat. makeScaleUpAnimation(v, v.getWidth() / 2, v.getHeight() / 2, 0, 0); // ps. makeSceneTransitionAnimation 使用共享元素的時候,需要在navigation方法中傳入當前Activity ARouter.getInstance() .build("/test/activity2") .withOptionsCompat(compat) .navigation(); // 使用綠色通道(跳過所有的攔截器) ARouter.getInstance().build("/home/main").greenChannel().navigation(); // 使用自己的日志工具打印日志 ARouter.setLogger();
3、路由中的分組概念
SDK中針對所有的路徑(/test/1 /test/2)進行分組,分組只有在分組中的某一個路徑第一次被訪問的時候,該分組才會被初始化
通過 @Route 注解主動指定分組,否則使用路徑中第一段字符串(/*/)作為分組
注意:一旦主動指定分組之后,應(yīng)用內(nèi)路由需要使用 ARouter.getInstance().build(path, group) 進行跳轉(zhuǎn),手動指定分組,否則無法找到
@Route(path = "/test/1", group = "app")
4、添加混淆規(guī)則(如果使用了Proguard)
-keep public class com.alibaba.android.arouter.routes.**{*;} -keep class * implements com.alibaba.android.arouter.facade.template.ISyringe{*;} # 如果使用了 byType 的方式獲取 Service,需添加下面規(guī)則,保護接口 -keep interface * implements com.alibaba.android.arouter.facade.template.IProvider # 如果使用了 單類注入,即不定義接口實現(xiàn) IProvider,需添加下面規(guī)則,保護實現(xiàn) -keep class * implements com.alibaba.android.arouter.facade.template.IProvider
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android Activity之間的數(shù)據(jù)傳遞方法總結(jié)
這篇文章主要給大家總結(jié)介紹了關(guān)于Android Activity之間的數(shù)據(jù)傳遞方法,文中通過示例代碼介紹的非常詳細,對各位Android開發(fā)者們具有一定的參考學習價值,需要的朋友們下面來一起學習學習吧2019-06-06分享Android開發(fā)自學筆記之AndroidStudio常用功能
這篇文章主要給大家分享Android開發(fā)自學筆記之AndroidStudio常用功能的相關(guān)資料,需要的朋友可以參考下2015-12-12Java4Android開發(fā)教程(三)java基本概念
本文介紹了Java4Android的基本概念,都是開發(fā)必備的基礎(chǔ)知識,希望能對大家有所幫助2014-10-10Android實現(xiàn)MVVM架構(gòu)數(shù)據(jù)刷新詳解流程
MVVM架構(gòu)模式,即Model-View-ViewModel三個層級,MVVM模式出來的時間已經(jīng)很長了,網(wǎng)上關(guān)于MVVM模式的解析也有很多,我這里只說一下我自己的理解,基本上是和MVP模式相比較的一個差異2021-10-10Android把svg圖片轉(zhuǎn)為jpg保存到相冊圖庫
這篇文章主要為大家詳細介紹了Android把svg圖片轉(zhuǎn)為jpg保存到相冊圖庫,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05