在引入多模塊開發后,我們首先要解決的就是模塊間頁面跳轉的問題。本文意在提供一種思路而非框架。
為什么要使用Router
我們知道,Google SDK提供了顯式和隱式兩種原生路由方案。但在模塊化開發中,顯式Intent存在類直接依賴的問題,造成模塊間嚴重耦合。隱式Intent則需要在Manifest中配置大量路徑,導致難以拓展(如進行跳轉攔截)。為了解決以上問題,我們需要采用一套更為靈活的Router方案。
方案思路
我們的思路很簡單,使用注解,為每個activity類標注別名。在啟動時對類進行掃描,將帶有注解的activity存放路由表中。整個過程被我們封裝在ActionManager
類中,并對外暴露startAction(String alias ,Bundle data)
接口,跳轉時通過別名在路由表中進行匹配,完成跳轉。大致流程如下:
代碼實現
以下是我截取的代碼片段,方便大家理解,
- 在創建Activity時,通過注解,為其注釋別名:
@Action("loginActivity ")
public class LoginActivity extends BaseBase{
//代碼省略...
}
- 在啟動時(Application類中),對包下的所有類進行掃描,將帶有注解標注的Activity,存入map,代碼如下:
private void activityScan(Context ctx) {
try {
//通過資源路徑獲得DexFile
DexFile e = new DexFile(ctx.getPackageResourcePath());
Enumeration entries = e.entries();
//遍歷所有元素
while(entries.hasMoreElements()) {
String entryName = (String)entries.nextElement();
//匹配Activity包名
if(entryName.contains("activity")) {
//通過反射獲得Activity類
Class entryClass = Class.forName(entryName);
if(entryClass.isAnnotationPresent(Action.class)) {
Action action = (Action)entryClass.getAnnotation(Action.class);
this.mapping.put(action.value(), entryClass.getName());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
- 對外暴露接口,提供根據別名跳轉Activity的方法:
ActionManager.getInstance().startAction(currentActivity.this ,"loginActivity ");
我們可以請求方法中做一些攔截處理,同樣可以通過Bundle傳輸數據。以下是startAction方法的實現:
public void startAction(Activity original, String alias) throws ClassNotFoundException {
if(this.mapping.containsKey(alias)) {
Intent intent = new Intent(original, Class.forName((String)this.mapping.get(alias)));
original.startActivity(intent);
} else {
throw new ClassNotFoundException();
}
}
我們通過這種方式,解決了跳轉Activity所產生的的模塊依賴問題,相較于原生方案,拓展性更強。但這種方案只是階段性的,還存在一些問題。首先,加載過程中,頻繁使用到反射,會產生性能問題。其次,對于每個Activity的別名,需要進行統一維護,增加了協作成本。對此,我們正在嘗試使用APT工具改進,目的是讓掃描過程在編譯期完成,避免運行時加載。
期望
目前市場上有不少Router框架,秉承不重復造輪子的原則,我們可以在項目中直接使用。但如開篇所述,我們意在提供一種思路,記錄演進過程,框架是別人的,思路是自己的,只有這樣才能形成對自己有益的技術棧。