人之所以能,是相信能。
一、前言
開始模組化開發專案之後,一個很重要的問題就是頁面見的跳轉問題。
關於模組化發開,可詳見我的另一片文章Android模組化開發探索。
正是由於將專案模組化拆分,各模組之間沒有任何依賴關係,也互相不可見,那麼從A模組的a介面跳轉到B模組的b介面該怎麼辦呢?
二、跨模組跳轉的方法
這裡我們會先介紹這幾種常見的跳轉方法:
- 顯示跳轉
- 隱示跳轉
- Scheme協議跳轉
- Router路由表方案
2.1 顯示跳轉
顯示跳轉即我們最最常用的跳轉方法:使用Intent,傳入當前Activity上下文,和目標Activity的class物件即可,如下:
Intent intent = new Intent();
intent.setClass(mContext, GuideActivity.class);
startActivity(intent);
複製程式碼
顯然,這種方法只能是目標Activity可見(Activity在同一個Module下)的時候才可以這樣呼叫。不適合跨模組間的跳轉。
2.2 隱示跳轉
我們這裡說的隱示跳轉,intent不設定class,而是設定Action或者Category。
例如:
在清單檔案中
<!--網頁展示介面-->
<activity
android:name="com.whaty.base.BaseWebViewActivity"
android:hardwareAccelerated="true">
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="com.whaty.base.BaseWebViewActivity" />
</intent-filter>
</activity>
複製程式碼
跳轉時:
//建立一個隱式的 Intent 物件:Action 動作
Intent intent = new Intent();
//設定 Intent 的動作為清單中指定的action
intent.setAction("com.whaty.base.BaseWebViewActivity");
startActivity(intent);
複製程式碼
2.3 scheme跳轉
如果我們為 B 頁面定義一個 URI - wsc://home/bbb,然後把共享的 messageModel 拍平序列化成 Json 串,那麼 A 只需要拼裝一個符合 B 頁面 scheme 的跳轉協議就可以了。 wsc://home/bbb?message={ “name”:”John”, “age”:31, “city”:”New York” }
在清單檔案中,配置data屬性,設定其host、path、scheme等
<activity android:name=".ui.BbbActivity"
<intent-filter>
<category android:name="android.intent.category.DEFAULT" />
<action android:name="android.intent.action.VIEW" />
<data
android:host="bbb"
android:path="/home"
android:scheme="wsc" />
</intent-filter>
</activity>
複製程式碼
跳轉時:
final Uri uri = new Uri.Builder().authority("wsc").path("home/bbb").appendQueryParameter("message", new Gson().toJson(messageModel)).build();
final Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
startActivity(intent);
複製程式碼
以上的方法,都不是我們想要的,接下來開始介紹我們的Router方案。
三、為什麼要用Router
Google提供了顯式和隱式兩種原生路由方案。但在模組化開發中,顯式Intent存在類直接依賴的問題,造成模組間嚴重耦合。隱式Intent則需要在Manifest中配置大量路徑,導致難以擴充(如進行跳轉攔截)。為了解決以上問題,我們需要採用一套更為靈活的Router方案。
四、實現思路
思路是這樣的:
使用註解,為每個目標Activity標註別名。在應用啟動時,對所有類進行掃名,將註解過的Activity存於路由表中。
跳轉時,在路由表中通過別名獲取目標Activity的class物件,使用Intent實現跳轉。
五、程式碼實現
5.1 自定義註解
/**
* Description: 路由跳轉介面 註解
* Created by jia on 2018/1/10.
* 人之所以能,是相信能
*/
@Target(ElementType.TYPE) //註解作用於型別(類,介面,註解,列舉)
@Retention(RetentionPolicy.RUNTIME) //執行時保留,執行中可以處理
@Documented // 生成javadoc檔案
public @interface Action {
String DEFAULT = "js";
String value() default DEFAULT;
}
複製程式碼
關於自定義註解的詳細介紹,請閱讀我的文章java進階之自定義註解。這裡不再多說。
5.2 註解Activity
@Action("MainActivity")
public class MainActivity extends BaseActivity implements TabLayout.OnTabSelectedListener {
...
}
複製程式碼
在建立Activity時,用剛剛自定義的註解進行註解,為其註釋別名。
5.3 啟動時掃描
private void getAllActivities(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") && entryName.contains("Activity")) {
//通過反射獲得Activity類
Class entryClass = Class.forName(entryName);
if(entryClass.isAnnotationPresent(Action.class)) {
Action action = (Action)entryClass.getAnnotation(Action.class);
this.map.put(action.value(), entryClass);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
複製程式碼
在應用啟動時,Application中對包下的所有類進行掃描,先找到名字中到activity的(定義到activity包下),並將帶有註解標註的Activity,存入map中。
5.4 跳轉
/**
* 頁面跳轉
* @param activity
* @param alias
*/
public void jumpActivity(Activity activity, String alias) throws ClassNotFoundException{
if(map.containsKey(alias)) {
Intent intent = new Intent(activity, map.get(alias));
activity.startActivity(intent);
} else {
throw new ClassNotFoundException();
}
}
複製程式碼
跳轉的時候傳入目標Activity的別名即可(這裡的別名就是註解的別名)。
總結
通過這種方式,解決了跳轉Activity所產生的的模組依賴問題,相較於原生方案,擴充性更強。但這種方案只是階段性的,還存在一些問題。首先,載入過程中,頻繁使用到反射,會產生效能問題。其次,對於每個Activity的別名,需要進行統一維護,增加了協作成本。還有待優化。
當然,市面上有很多流行的Router方案(如阿里的ARouter),這裡只是介紹了一個思路,有好的建議歡迎交流,一起進步。
更多精彩內容,歡迎關注我的微信公眾號——Android機動車