Android 多模組多元件開發
一.基本介紹
1. 藉此機會分享一下自己從剛入門到現在,在開發架構方面的一些心路歷程。最終我會把這部分程式碼進行開源,以後也會對其進行維護。但目前尚在測試中所以並未釋出正式版本。我們可以先把程式碼下載下來進行了解,如果有什麼問題可以隨時 Issues,這也將是我的第一個開源庫,希望能幫到大家。
2. 該庫所涉及到的類大概在 30 個左右,原始碼並不多相信我們都能讀懂裡面的內容,這裡羅列一下原始碼中所涉及到的一些知識點:
(1) 編譯時註解自動生成 Module、Action 和 Intercepter
(2) 執行緒、執行緒池、執行緒同步非同步和 Handler
(3) 責任鏈模式、享元模式、策略模式、模板模式 …
3. 作為一個多模組的路由通訊庫,相信它已支援了所有跨模組通訊的使用場景,功能介紹如下:
(1) 支援依賴注入,可單獨作為依賴注入框架使用
(2) 支援執行緒切換和排程(原始執行緒,主執行緒,同步,非同步)
(3) 支援多模組工程下的所有跨模組通訊使用場景
(4) 支援新增多個攔截器,可根據優先順序自定義攔截順序
(5) 支援許可權和網路檢測、登入攔截跳轉和資料埋點等功能
二.架構的演變之路
首先分享一下自己從剛入門到現在,在開發架構方面的一些心路歷程,從剛開始踏上程式這條不歸路(人生就是一條不歸路),想想已經是好幾個年頭,中間經歷過提升、迷茫和進階,也經歷過從不自信到自信、自滿到自負。剛開始一個人在小公司小打小鬧,是這個樣子的:
那個時候所有的類都是寫在一個包下面的,所有的 Activity 都是繼承自系統下的 android.app.Activity,網路框架都是直接用的 android-async-http 所以加班挺嚴重, 一方面是自己能力經驗還不夠,另一方面是沒有架構的情況下很多程式碼都是反覆冗餘的。畢業後去了一家上市公司,多人團隊協作開發所以就改變了一些:
這個時候已經有所提升了,所有的 Activity 也不是直接繼承自 android.app.Activity 了,也不再是直接使用 android-async-http 所以開發的日子自然舒坦了一些。一方面是經驗能力上去了遇到問題能馬上解決,另一方面基於架構開發很多程式碼不用反覆寫後期也便於維護,最主要的是大公司人多一些,之前是一個人幹四個人的活,現在四個人幹一個人的活。隨著開發人員的越來越多,還有就是基本每逢過節都要做一些活動,等活動過後這些程式碼和資源就要刪除,而且每兩個月就得迭代一個新的版本,所以變得越來越複雜起來。後來就衍生出了很多像熱更新、外掛化、多模組多元件開發等等。當然剛開始的多模組多元件並未基於路由,是蜘蛛網的狀態。
會有這麼亂嗎?隨著業務邏輯更加複雜的情況下應該會更亂,因為功能模組都是各自存在於自己的 Module 中,但是可能在開發的過程中 Module1 得呼叫 Module2 的程式碼,這個時候如果是直接新增依賴那麼肯定就會有這麼亂了。接下來看下基於路由情況下的多模組多元件開發:
三. DRouter 基本使用
- 在需要跨模組通訊的Module中新增依賴和配置
defaultConfig {
......
javaCompileOptions {
annotationProcessorOptions {
arguments = [moduleName: project.getName()]
}
}
}
dependencies {
.......
annotationProcessor project(':drouter-compiler')
}
- 在 Module 中建立需要執行的 Action
// path 必須是以在 gradle 中配置的 moduleName + "/" 開頭,否則編譯通不過。
// threadMode 支援 POSTING 、MAIN、BACKGROUND、ASYNC 預設情況下是 POSTING(原始執行緒)
@Action(path = "login/action", threadMode = ThreadMode.MAIN)
public class LoginAction implements IRouterAction {
@Override
public RouterResult invokeAction(Context context, Map<String, Object> requestData) {
// 通訊執行方法支援所有場景,啟動 Activity,Service,Provider,彈框,快取資料,獲取 Fragment 等等等等
Intent intent = new Intent(context, LoginActivity.class);
intent.putExtra("key", (String) requestData.get("key"));
context.startActivity(intent);
return new RouterResult.Builder().success().object(100).build();
}
}
- 初始化 SDK
public class BaseApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
// 開啟 debug
DRouter.openDebug();
// 初始化且只能初始化一次,引數必須是 Application
DRouter.getInstance().init(this);
}
}
- 可在任意 Module 中執行跳轉
// 根據 action 查詢只執行對應方法,不處理返回回撥,引數攜帶隨意
DRouter.getInstance()
.action("login/action")
.context(this)
.param("key", "value")
.invokeAction();
// 根據 action 查詢執行對應方法,並處理返回回撥
DRouter.getInstance()
.action("circlemodule/test")
.context(this)
.invokeAction(new ActionCallback() {
@Override
public void onInterrupt() {
Log.e("TAG", "被攔截了");
}
@Override
public void onResult(RouterResult result) {
// 注意該方法的執行執行緒是由 Action 的 threadMode 決定的,也就是說和 Action 在同一個執行緒
Log.e("TAG", "result = " + result.toString());
}
});
- 在任意模組下都可新增攔截
// priority 優先順序越高,攔截器執行越優先
@Interceptor(priority = 18)
public class CircleInterceptor implements ActionInterceptor {
@Override
public void intercept(ActionChain chain) {
ActionPost actionPost = chain.action();
// 圈子詳情頁必須是要登入,如果沒有登入即可攔截跳轉到登入頁面,否則繼續往下執行。
if (chain.actionPath().equals("circlemodule/test")) {
Toast.makeText(actionPost.context, "攔截圈子,跳轉到登入", Toast.LENGTH_LONG).show();
// 跳轉到登入頁面
DRouter.getInstance()
.action("login/action")
.context(actionPost.context)
.invokeAction();
// 這個方法呼叫後便會攔截整條鏈
chain.onInterrupt();
}
// 繼續向下轉發
chain.proceed(actionPost);
}
}
6.混淆配置
-keep public class com.drouter.assist.**{*;}