我們團(tuán)隊(duì)把一個(gè)10萬(wàn)行安卓代碼的舊項(xiàng)目(電商系統(tǒng)管理臺(tái)App),使用Small框架做了插件化改造枫浙。把項(xiàng)目分成了10多個(gè)插件模塊,解除了業(yè)務(wù)模塊之間的代碼耦合,為業(yè)務(wù)功能的快速迭代和多團(tuán)隊(duì)并行開發(fā)做好基礎(chǔ)架構(gòu)谍夭。遷移期間遇到了一些坑,但最后在大家的努力下基本得到了解決憨募,也感謝Small的作者光亮對(duì)我們提出的issue的快速響應(yīng)紧索。
Small的應(yīng)用場(chǎng)景
開發(fā)時(shí):讓你完全透明的像開發(fā)普通工程一樣完成插件開發(fā)
編譯時(shí):自動(dòng)化的幫助你分離各個(gè)公共庫(kù)、業(yè)務(wù)模塊插件(插件僅保留自身的代碼跟資源菜谣,達(dá)到最小化)
運(yùn)行時(shí):運(yùn)用最少量的Hook無(wú)縫的將各個(gè)插件并入宿主珠漂,讓代碼跟資源完全融合晚缩,自由調(diào)用
所以撇開開發(fā)者不用關(guān)注的(2)來(lái)說,Small的目的就是要做到保證開發(fā)者「暢快開發(fā)」的體驗(yàn)媳危,以及App使用者「順暢使用」的體驗(yàn)荞彼。
插件化方案有很多,很難有統(tǒng)一的形態(tài)待笑,各有各的適用場(chǎng)景鸣皂。如果你需要分發(fā)你的插件給別人使用,Small無(wú)法滿足滋觉;如果你想利用插件化拆分签夭、重組自己的應(yīng)用,我認(rèn)為Small在這方面已經(jīng)走在了前面椎侠,并且將不斷完善第租,做到極致。
插件化的工程(開發(fā))結(jié)構(gòu)
關(guān)于工程(開發(fā))結(jié)構(gòu)的討論
1.依賴管理:Small使用同一個(gè)ClassLoader加載不同的插件我纪,因此不同插件中的依賴在運(yùn)行時(shí)對(duì)其他插件和宿主都是可見的慎宾,所以依賴的管理一定要收斂,統(tǒng)一管理浅悉,避免各自引入趟据。
2.插件之間的通信:插件的開發(fā)一般都是多team并行的,插件之間應(yīng)避免直接調(diào)用來(lái)減少耦合
3.資源的使用:由于small的特性术健,runtime各插件的資源也是可以相互訪問的汹碱,但在開發(fā)上需要組織和統(tǒng)一管理好,包括命名和安全的使用資源荞估。
老項(xiàng)目集成Small插件的官方建議
基本原則
宿主中不要放業(yè)務(wù)邏輯咳促。只做加載插件以及調(diào)起主插件的操作。
重構(gòu)步驟
- 拆lib.* - 公共模塊插件
把各個(gè)第三方庫(kù)拆出來(lái)做成一個(gè)個(gè)lib.*
插件模塊勘伺,包括統(tǒng)計(jì)跪腹、地圖、網(wǎng)絡(luò)飞醉、圖片等庫(kù)冲茸。
把老項(xiàng)目積累的業(yè)務(wù)公共代碼(utils)分離出來(lái)封裝成一個(gè)lib.utils插件
把基礎(chǔ)的樣式、主題分離出來(lái)封裝成一個(gè)lib.style插件 - 拆app.* - 業(yè)務(wù)模塊插件
把業(yè)務(wù)模塊拆成app.*
模塊缅帘,他們可以依賴lib.*
模塊轴术,顯示調(diào)用lib.*
中的各個(gè)API
相對(duì)獨(dú)立的業(yè)務(wù)模塊先拆,比如“詳情頁(yè)”钦无、“關(guān)于我們”逗栽,如果剩下的業(yè)務(wù)不好拆,先放一個(gè)插件里
如果都不好拆铃诬,先把全部業(yè)務(wù)做成一個(gè)app.main主插件
插件之間的通信
插件之間的通信/互調(diào)
目前的插件之間的通信僅限于Small.openUri()祭陷,用來(lái)調(diào)起其它插件的頁(yè)面。
但有些時(shí)候趣席,我們還是需要操作一下其它插件兵志。 比如說在跳轉(zhuǎn)到另一個(gè)插件的頁(yè)面之前,需要查詢一下對(duì)方插件的一個(gè)狀態(tài)宣肚。
我覺得應(yīng)該借助v4包的LocalBroadcastManager來(lái)實(shí)現(xiàn)
老項(xiàng)目集成Small插件化遇到的坑
插件中通過startActivityForResult獲取不到數(shù)據(jù)問題想罕。如果啟動(dòng)的Activity是SingleTask啟動(dòng)模式,有這個(gè)問題霉涨。
插件lib2依賴插件lib1按价,導(dǎo)致同時(shí)依賴lib1和lib2的插件app1編譯時(shí)出現(xiàn)找不到類的情況。
一個(gè)lib依賴另一個(gè)lib笙瑟,buildLib時(shí)出現(xiàn)Duplicate package錯(cuò)誤
一個(gè)lib依賴另一個(gè)lib楼镐,buildLib時(shí)出現(xiàn)下面的錯(cuò)誤,目前還沒有找到解決吧往枷,clean工程和cleanLib都做了框产,但還不行,只好把依賴去掉了才行错洁。原因未知如果插件用到了百度地圖等秉宿,需要把相關(guān)的功能放在一個(gè)插件下面,注意那些很大的.so是放在插件里面的
Small插件中是支持aar引用的屯碴,實(shí)際使用過了
Small 0.9版本的插件限制了包名描睦,因此已有代碼復(fù)制過來(lái)會(huì)有改變。特別要注意layout文件中自定義ban件包名的改動(dòng)导而,能夠通過編譯但運(yùn)行時(shí)會(huì)找不到類忱叭。1.1版本支持自定義包名了
通過assembleRelease單獨(dú)編譯一個(gè)插件出現(xiàn)下面的錯(cuò)誤,但有的電腦能正常單獨(dú)編譯插件嗡载,原因未知窑多,已經(jīng)提issue。
下午2:30:54: Executing external task 'assembleRelease'...
Configuration on demand is an incubating feature.
Incremental java compilation is an incubating feature.
FAILURE: Build failed with an exception.
* What went wrong:
A problem occurred configuring project ':app.me'.
> Could not find property 'android' on project ':app'.
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
BUILD FAILED
- 第三方SDK的meta洼滚,Service等需要在宿主App的manifest中聲明埂息,例如百度地圖SDK的定位Service。
- lib.utils刪除資源后出現(xiàn)編譯錯(cuò)誤遥巴,解決辦法是刪除public.txt千康,重新編譯lib和bundle
- 老代碼一些業(yè)務(wù)上耦合比較緊的頁(yè)面不容易拆開,可以作為一個(gè)模塊存在铲掐,我們最后拆成了6個(gè)大模塊拾弃。
- 目前1.0版本不支持gradle并行編譯,在gradle.properties中打開
org.gradle.parallel=true
編譯會(huì)報(bào)錯(cuò)
gradle buildLib -q
[Small] [Small] building library 1 of 2 - app (0x7f)
[Small] [Small] building library 1 of 2 - app (0x7f)
building library 2 of 2 - lib.utils (0x73)
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':lib.utils:preBuild'.
./build-small/intermediates/small-pre-link/aar/app-D.txt (No such file or directory)
參考gradle文檔
這個(gè)不是Bug摆霉,有依賴的工程肯定是不能并行編譯的
后面遇到問題會(huì)持續(xù)更新
更新
Small框架0.9升級(jí)1.0時(shí)出了很多bug豪椿,主要是在windows上編譯有些問題奔坟,但現(xiàn)在新版本已經(jīng)修復(fù)了。
2016年8月25日使用Small插件化的千米電商云App已經(jīng)在各大應(yīng)用市場(chǎng)上線了搭盾!歡迎體驗(yàn)啊