時(shí)間從來沒有等過我們,歲月這把捅豬刀.捅得你滿臉都是滄桑.你一定是為工作操碎了心.不知道現(xiàn)在的身處何處,是否有摯愛的人照顧你.過得快樂或委屈?
哦忘了.你是個(gè)有故事的人,"你想ta過得比你要好,希望你永遠(yuǎn)不都會(huì)知道".

有時(shí)在過度勞累之后,腰腿酸痛精神不振,好像身體被掏空,是不是*透支了?

騷年莫慌,老夫帶你擼個(gè)框架,進(jìn)可重振雄風(fēng),退可養(yǎng)精蓄銳!

本篇適合什么樣的人群看?
- 做android有一段時(shí)間了,擼碼就是一把梭。
- 項(xiàng)目開工,但是目前不知道怎么去拆分業(yè)務(wù)和技術(shù)依賴分層。
- 剛剛畢業(yè)出來,進(jìn)了一家不大不小的公司一人我因酒醉,兩眼是...啊,呸呸呸!一人單挑項(xiàng)目。
你能在本篇文章中收獲什么?
- 自己動(dòng)手搭一個(gè)通用型依賴框架
- 學(xué)習(xí)拆分業(yè)務(wù)層和技術(shù)層
- and 吹吹水 裝裝嗶
目錄
- 前言
- 踩坑
- 分析
- 實(shí)現(xiàn)
- 總結(jié)
前言
本篇是從線上的APP中基于一次次的采坑、總結(jié)、分享抽離出來的。但由于商業(yè)原因具有不可公開性。所以不會(huì)提及任何與業(yè)務(wù)有關(guān)聯(lián)的場(chǎng)景用例。以及不會(huì)包含與任何商用項(xiàng)目相干的提及。所有技術(shù)僅僅用于技術(shù)學(xué)習(xí)交流。本篇不會(huì)是一個(gè)純代碼講解的文章。文本中的故事也是一個(gè)虛構(gòu)的。具體技術(shù)實(shí)現(xiàn)請(qǐng)您移步致GIthub,地址在篇尾。特此聲明。
致謝
感謝 jeasinlee、LeoFangQ、Michael、zaneCC 幾位前輩無私的分享才得已產(chǎn)出。
踩坑
什么?要求一個(gè)月后上線?老夫就是一把梭。復(fù)制粘貼東拼西湊就能出來。
上線一周后。小王啊。經(jīng)過產(chǎn)品和老板討論過后,我們下一步要把首頁(yè)上的XX改一下。后面主流程不這樣走了。你接下來...?
fuck!為毛你們之前不想好。這怎么改。代碼是一大坨的。動(dòng)一下可能就會(huì)導(dǎo)致到處都會(huì)崩潰。
項(xiàng)目中的代碼是下面這樣的:


你叫勞資怎么改?當(dāng)初是你說要一個(gè)月上線。這個(gè)項(xiàng)目的代碼以前只有我和神能看懂?,F(xiàn)在只有神了。
分析
想想就氣人,都是你們這幫孫子逼逼逼,連夜加班一個(gè)月趕出來的東西。上線不到一周就要改。這下我可頭大了。之前沒有考慮到后期可能會(huì)出現(xiàn)維護(hù)或者擴(kuò)展。改?還不要了我命。但也沒有更好的辦法了。只好默默的理代碼了。

第二天項(xiàng)目里進(jìn)來了一個(gè)看起來像dalao的氣息的人,dalao看完會(huì)心一笑,小王啊。這個(gè)時(shí)候我們寧可重構(gòu)一把也不可在原有的基礎(chǔ)上改,救得了今天救不過明天。這樣,我向上級(jí)申請(qǐng)延長(zhǎng)一些時(shí)間,我們一起來重新設(shè)計(jì)一把架構(gòu)把。
之前的項(xiàng)目結(jié)構(gòu):

dalao的項(xiàng)目結(jié)構(gòu):

實(shí)現(xiàn)
等一下,dalao。你直接給我上個(gè)圖,我看不懂呀。并且也沒有解釋清楚怎么實(shí)現(xiàn)分離的呢。
dalao咳咳兩聲喃喃道:首先,我們整體的思路是讓現(xiàn)在開發(fā)的APP去依賴AndroidBasicLibs,我們盡量做到精簡(jiǎn)只需要在Gradle依賴它就行了。
dependencies { compile 'xxx.xxx.xxx:AndroidBasicLibs' }
AndroidBasicLibs作為一個(gè)通用型的依賴庫(kù),它擁有三只麒麟臂,分別是basekit、common、uihelper。
AndroidBasicLibs
basekit
basekit是一個(gè)基于MVP+RXjava的的基礎(chǔ)框架,它承載了MVP的設(shè)計(jì)風(fēng)格,我們的上層APP只需要繼承它的BaseActivity、BaseModel 、IBaseView 、BasePresenter 就可以了。上層要做的僅僅只是往對(duì)應(yīng)的層填充獨(dú)有的業(yè)務(wù)。如下圖5是Base。 圖6是APP中的業(yè)務(wù)單元。具體用法請(qǐng)參考GIthub,地址在本文末尾。


common
common是一個(gè)通用型的工具箱集,它可以對(duì)上層所依賴的第三方框架做解耦操作。如何理解這句話呢。假如我們要做圖片加載,圖片加載框架你肯定不會(huì)自己重復(fù)造輪子。在沒有common層時(shí)。項(xiàng)目1.0的依賴的是ImageLoader,2.0的時(shí)候發(fā)現(xiàn)這個(gè)庫(kù)有BUG需要將ImageLoader替換成主流的glide框架。但因?yàn)轫?xiàng)目中多處都有使用到ImageLoader的API。無法做到加載圖片處只改一行代碼,就能讓所有的業(yè)務(wù)都換了新的加載工具。這時(shí)common的威力就顯而易見。,中間做解耦,上層調(diào)用common。common調(diào)用第三方依賴。當(dāng)然這只是一個(gè)列子。還有很多地方都可以進(jìn)行二次封裝。比如網(wǎng)絡(luò)請(qǐng)求、視圖動(dòng)畫等我們把這些封裝都放在common。下面是圖片加載的二次封裝實(shí)現(xiàn)代碼。
//用接口統(tǒng)一約束Api
public interface ILoader {
void init(Context context);
void loadNet(ImageView target, String url, Options options);
void loadResource(ImageView target, int resId, Options options);
void loadAssets(ImageView target, String assetName, Options options);
void loadFile(ImageView target, File file, Options options);
void clearMemoryCache(Context context);
void clearDiskCache(Context context);
class Options {
public static final int RES_NONE = -1;
public int loadingResId = RES_NONE;//加載中的資源id
public int loadErrorResId = RES_NONE;//加載失敗的資源id
public static Options defaultOptions() {
return new Options(JConfig.IL_LOADING_RES, JConfig.IL_ERROR_RES);
}
public Options(int loadingResId, int loadErrorResId) {
this.loadingResId = loadingResId;
this.loadErrorResId = loadErrorResId;
}
}
}
//具體的實(shí)現(xiàn)
public class GlideLoader implements ILoader {
@Override
public void init(Context context) {
}
@Override
public void loadNet(ImageView target, String url, Options options) {
load(getRequestManager(target.getContext()).load(url), target, options);
}
@Override
public void loadResource(ImageView target, int resId, Options options) {
load(getRequestManager(target.getContext()).load(resId), target, options);
}
@Override
public void loadAssets(ImageView target, String assetName, Options options) {
load(getRequestManager(target.getContext()).load("file:///android_asset/" + assetName), target, options);
}
@Override
public void loadFile(ImageView target, File file, Options options) {
load(getRequestManager(target.getContext()).load(file), target, options);
}
@Override
public void clearMemoryCache(Context context) {
Glide.get(context).clearMemory();
}
@Override
public void clearDiskCache(Context context) {
Glide.get(context).clearDiskCache();
}
private RequestManager getRequestManager(Context context) {
return Glide.with(context);
}
private void load(DrawableTypeRequest request, ImageView target, Options options) {
if (options == null) options = Options.defaultOptions();
if (options.loadingResId != Options.RES_NONE) {
request.placeholder(options.loadingResId);
}
if (options.loadErrorResId != Options.RES_NONE) {
request.error(options.loadErrorResId);
}
request.crossFade().into(target);
}
}
//暴露給上層加載用的Factory
public class LoaderFactory {
private static ILoader loader;
public static ILoader getLoader() {
if (loader == null) {
synchronized (LoaderFactory.class) {
if (loader == null) {
loader = new GlideLoader();
}
}
}
return loader;
}
}
uihelper
uihelper是一個(gè)自定義View的依賴。所有不含業(yè)務(wù)型的View都應(yīng)該放在這個(gè)module。
APP
APP就是上層的項(xiàng)目了,注意在androidStudio的module中除了可以傳遞依賴以外,還可以有自己獨(dú)立的依賴,所以我們可以在APP中對(duì)業(yè)務(wù)類型的依賴添加在此處。如:后端SDK、友盟統(tǒng)計(jì)、微博分享等,只屬于該項(xiàng)目中才會(huì)出現(xiàn)的就應(yīng)當(dāng)放在此處。
來我們?cè)賮砜匆谎蹣?gòu)架圖,是不是理清楚多了。

對(duì)比:
一把梭項(xiàng)目架構(gòu):
優(yōu)點(diǎn):
- 快速開發(fā)
- 無腦堆業(yè)務(wù)
缺點(diǎn):
- 維護(hù)成本高
- 可閱讀性差
- 擴(kuò)展性低
AndroidBasicLibs構(gòu)架:
優(yōu)點(diǎn):
- 可擴(kuò)展
- 可重用
- 可維護(hù)
- 更快的速度開發(fā)
- 底層與業(yè)務(wù)抽離
缺點(diǎn):
- 前期搭建費(fèi)時(shí)間
- 依賴結(jié)構(gòu)的層級(jí)略多
總結(jié)
項(xiàng)目的架構(gòu)設(shè)計(jì)絕不是一成不變的套路,應(yīng)該根據(jù)業(yè)務(wù)的類型去做模塊拆解。并且實(shí)際上個(gè)人認(rèn)為很多時(shí)候開發(fā)第一個(gè)版本的項(xiàng)目即便你考慮去搭建一套框架去寫。但會(huì)因?yàn)檫M(jìn)度和協(xié)同合作方面等因素導(dǎo)致理想狀態(tài)不太一樣。在線上穩(wěn)定住后,就應(yīng)該考慮去重構(gòu)你的項(xiàng)目。見過一位同事的編碼風(fēng)格是邊寫業(yè)務(wù)邊重構(gòu)之前的代碼。會(huì)導(dǎo)致與你協(xié)同開發(fā)的同事。翻閱過后就懵逼了。每次都要重讀一遍。這不是一個(gè)好辦法。最好與團(tuán)隊(duì)中成員達(dá)成一致的意見后開整。
本來這篇我并不想發(fā)表的,怕是自己學(xué)藝不精誤導(dǎo)了新人。加上工作上的變動(dòng),近期略顯倉(cāng)促。希望各位看官多多給點(diǎn)評(píng)。覺得有收獲就點(diǎn)個(gè)喜歡就是對(duì)我最大的鼓勵(lì)。
最后本框架的地址是:https://github.com/wwah/AndroidBasicLibs
如何下次找到我?
- 關(guān)注我的簡(jiǎn)書
- 本篇同步Github倉(cāng)庫(kù):https://github.com/BolexLiu/DevNote (可以關(guān)注)
