少年,老夫帶你擼一把Android項(xiàng)目框架,你可想學(xué)?

時(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):

一把梭項(xiàng)目架構(gòu)

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


dalao的架構(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,地址在本文末尾。

圖5
圖6

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


如何下次找到我?

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容