ps:未完 ~
之前組件化我寫了一篇,基礎的問題可以去看那篇
先吐槽下
組件化是從去年開始接觸的,因為一是組件化是現在的發展思路,大家或多或少都在做這個,二呢是因為我司項目也是到了需要組件化的時候了
我司業務包結構:
能看到我們的業務分包已經分了很多了,出現的問題比較多,最典型的就是
- 編譯打包慢
- 業務溝通難
比如我想啟動不是我寫的業務模塊的頁面,我是不知道那個頁面的 class 名字的,我就要去問這個頁面誰寫的,名字叫什么,傳什么參數。
或者不同模塊間可能有數據傳遞,那么我還是要問問哪個同學寫的這個模塊,然后溝通模塊間 iterface 通訊接口,并且這個 iterface 通訊接口的獲取還是強依賴的,一般我著都是通過 static 來做的
要不就是我的頁面需要顯示別的模塊的列表,那么我需要去看其他同學的 adapter 怎么用,傳參,動畫,回調設置
上面種種問題很多,一個是強依賴的問題,別人改我就得跟著改,其次是來回文別人很非時間精力,還老是打擾別人,惹人不快。
能看出來我死這是沒有文檔管理的,所以有啥不知道都得問別人,也是沒有好的架構設計,大家都是破罐破摔習慣了,技術上也沒有成長,這是不正確的,所以我打算上組件化,只是后來公司變動,裁員我離開了,沒有付之行動罷了
組件化我們干什么
組件化就是把我們 app 中種種功能,業務單獨抽象拿出來,組織成一個個單獨的 module ,當然不是每一個功能都是一個 module ,而是根據相關性把他們或多或少的分成一個活多個 module ,具體的要看你司單位自己的了
借美團的圖看一下:
大概就是這個意思
今天我看到一句很 Nice 的話:
除了選擇你熟悉的框架都高效完成業務之外,我們認為剩下的是狀態管理。簡單應用使用組件內 State 方便快捷,但隨著應用復雜度上升,會發現數據散落在不同的組件,組件通信會變得異常復雜。
組件化改造過程
任何改造都是由簡到難的,組件化也一樣
我們先不動業務,先把基礎功能抽象出來,比如網絡,緩存,數據庫,放到一個單獨的基礎 module 中,有人叫 baseLib
基礎功能實現分離了,那么我們現在可以懂業務庫了,每一個打的業務模塊抽象成一個單獨的 module,比如 orderModule,shopModule
進一步抽象業務模塊中通用的 UI 或是功能,把他們再抽象出來,形成 commonModule,前文說的哪些通用的 adapter 就可以拿出來放到 commonModule 里
module 之間代碼是隔離的,那么 module 之間的通信,頁面的喚起就需要中間件 router 了,router 說簡單他就是一個喚起頁面的,說復雜,router 包括業務 module 的初始化,頁面的相互調用,外鏈頁面調用,module 間數據交互等,挺多復雜的東西,你司 app 要是還有跨進程通信的部分會更麻煩,這里可以做的很簡單,也可以做的很復雜,完全看個人是不是有興趣在技術上探索一下,更進一步了
剩下的就是最麻煩,最難搞的了,module 間依賴管理,小公司就一個 app ,代碼雖然分成了一個個 module ,但是 module 還是在 app 工程內的,module 一般提供 file 依賴管理就行了,沒啥說的。
但是在大公司就不同了,大公司不單單是只有一個 app 了,是有很多個 app 在同時開發,那么 module 怎么依賴,像 baseLib 這些底層的變化小的可以做 arr 依賴,但是像業務module 用 file 依賴就沒法搞了,你把 module file 放到哪個 app 的工程代碼里去,若是都放的話,你怎么同步不同 app 工程下對同一個 module 的修改。業務 module 因為修改頻繁,提供 arr 依賴分麻煩,不是太適合的
到這里我也不是很清楚了,之前看到一個方案,使用 Git Submodule 命令將子模塊全部遷移到獨立倉庫中!Git Submodule是Git提供的功能,它允許你將一個 Git 倉庫作為另一個 Git 倉庫的子目錄,它能讓你將另一個倉庫克隆到自己的項目中,同時還保持提交的獨立。
我不知道大公司是不是這么做的,但是應該八九不離十了。但是 Git Submodule 有坑,你主工程切分枝了,Submodule 加載進來的 module 可不是跟著改,同樣朱工程提交了 Submodule 加載進來的 module 一樣也不會跟著提交,關于這塊大家看下面文章,作者自己寫了一個插件:
大公司永遠走的比我們快,大家可以參考微信,美團的相關文章:
上面我們只是完成一個目前大眾水平的單工程組件化方案,算是可以接受,但是這不是終點哦,大家跟著大公司的幾部繼續往下走吧
看過上面2篇文章的同學,可以看到大公司的業務 module 是在太多了,而且還是垂直分組的,這樣的話怎么組織 module ,不同的 app 殼對某些業務有不同的要求,比如顯示,這些微信,美團采用了下面這個措施:
- 使用 .api 代替 .java 提供對外依賴接口
- 封裝優秀的 router 組件
- 使用 flavor 來配置不同的 app 殼對應的 sourceSets
- 使用 p 工程在 module 內部再次實現代碼隔離
上邊我有逼逼了一次組件化的東西,我最后想說的是:光說沒用,大家得親自去干才行,要不不會有深刻深入的體會的
更多的組件化思路大家看,簡書 _ 慕涵盛華 _ 組件化專欄:
router 路由都干什么事
router 路由簡單來說就是 module 組件間用來通信的,這個通信包括:
- 頁面調起
- 簡單的數據間的交互
- 復雜的 interface 交互
1. 頁面調起
大家是不是覺得頁面調起很簡單啊,其實錯誤了,月面調起很復雜的,從頁面調起的來源就能看出來
頁面跳轉來源:
- 來自 app 內的跳轉
- 來自 app 內 web 容器的跳轉
- 從 app 外部通過 Uri 喚起的 app 跳轉
- 從通知中心 push 喚起的 app 跳轉
現在你還覺得 module 間頁面跳轉還容易嗎,不容易的,由是是我們還要在實現代碼解耦的基礎上實現更難,所以一個 優秀的 router 組件對我們非常重要
2. 對于數據交換
2.1 如果是簡單的數據間的交互,我們使用 rxbus ,eventbus,livedatabus 這些就可以,上面幾個可以非常簡單的實現一對多的效果通知,消息本質不就是數據嘛,所以用來做簡單的數據同步通信也是沒問題的,推薦 livadatabus 哦
2.2 如果是復雜的 interface 交互,那么一般都是通過 router 注冊返回 interface 交互接口實現類的工廠類。或者大家狠狠心,可以把相關的業務代碼下沉到基礎業務層 commonModule,這樣使用靜態單例實現通信也行,但是不具有普遍性
另外 interface 交互接口實現類,大家維護一個也行,維護多個也行,在自己,不過遇到跨進程通信的,認為還是維護一個好些
router 選型
目前主流的 router 就是 ARouter 和 WMRouter 了
2者差不多,下面這是對比圖:
2者都需要寫學習成本,WMRouter 配置比 ARouter 復雜寫,但是可以通過配置做的更靈活,我使用的是 WMRouter
WMRouter 學習資料:
- 官方介紹:WMRouter_美團外賣Android開源路由框架
- 官方文檔:WMRouter_設計與使用文檔
- 美團開源路由框架(WMRouter)學習——使用篇
- WMRouter源碼分析(4)-頁面路由實例分析
WMRouter 的官方文檔寫的不是很好,不是從易到難的順序來寫的,大家一定要通篇看下來才能看個大概,然后跑下 demo 才行
Arouter學習資料:
router 自己實現
大家若是想自己實現 router 的話,那么現在不支持 apt 注解的話是沒有什么意義了
我可以給大家推薦一寫文章,都是自己實現 router 的:
Android架構思考(模塊化、多進程)
這是 17年初早期的一篇文章,作者的 router 實現了 跨進程通信和 interface 通信,思路還是很好的,但是沒有 apt 實現Android組件化之通信(多模塊,多進程)
這是對上一篇的改進,添加了 apt 進去Android組件化最佳路由—ARetrofit
這個作者寫的像 retrofit 那樣用 router完美解決Android進程間通信—ABridge
跨進程通信,思路也很不錯,大家沒事看看
能看到最后的都是有緣人,現在大廠都是 平臺化 開發了,已經不單單是用 gradle 插件了,已經上升到了平臺畫開發容器的程度了,對于此我也是很陌生
下面是我收集的一些文章,大家開拓下思路:
比如阿里的 Quinox 容器框架
關于插件化中的路由實現
-
MyRouter
大家可以通過這個項目去初步熟悉下插件化的代碼
添加一下,外部鏈接打開本地 app
1. 基本思路
不管是哪種開源庫,還是自己做,基本都是 通過 Scheme 協議打開 APP 界面,如下面 activity 的設置,這是 Scheme 設置最全的了
<activity
android:name=".ui.activity.ZMCertTestActivity"
android:label="@string/app_name"
android:launchMode="singleTask"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="scheme1"
android:host="host1"
android:path="/path1"
android:port="8080" />
</intent-filter>
</activity>
activity 獲取外鏈中的數據
Uri uri = getIntent().getData();
if (uri != null) {
// 完整的url信息
String url = uri.toString();
Log.i(TAG, "url:" + uri);
// scheme部分
String scheme = uri.getScheme();
Log.i(TAG, "scheme:" + scheme);
// host部分
String host = uri.getHost();
Log.i(TAG, "host:" + host);
// port部分
int port = uri.getPort();
Log.i(TAG, "port:" + port);
// 訪問路勁
String path = uri.getPath();
Log.i(TAG, "path:" + path);
List<String> pathSegments = uri.getPathSegments();
// Query部分
String query = uri.getQuery();
Log.i(TAG, "query:" + query);
//獲取指定參數值
String success = uri.getQueryParameter("success");
Log.i(TAG, "success:" + success);
}
最后外鏈拼在 intent 中拼接 Scheme 啟動我們的 app
Intent intent=new Intent(Intent.ACTION_VIEW,Uri.parse("scheme1://host1:8080/path1?query1=1&query2=true"));
startActivity(intent);
2. 哪些頁面應該接受外鏈啟動
WMRouter 美團路由也是這個思路的,區別是,我們是所有的頁面都可以由外鏈啟動還是由單一頁面接受外鏈啟動?
目前看來主流都是由專門一個頁面接受外鏈啟動,解讀參數,然后再啟動其他頁面的,WMRouter 美團路由在官方文檔里面也是這樣推薦的
另外有一篇關于這個的詳細解釋,很不錯