前言
從JavaEE轉(zhuǎn)到Android開發(fā)也2年多了揩抡,開發(fā)的項(xiàng)目也有4,5個(gè)了(公司項(xiàng)目)般堆,其中有3個(gè)項(xiàng)目前期都是自己獨(dú)立開發(fā)转培,從一開始的毫無架構(gòu)到現(xiàn)在對如何架構(gòu)也有一點(diǎn)心得耿芹,所以在此分享出來崭篡,大家一起交流
什么是架構(gòu)
在我看來,軟件架構(gòu)絕對不只是框架的堆砌吧秕,看我看來琉闪,架構(gòu)是為了方便軟件維護(hù)、擴(kuò)展砸彬、安全性颠毙、切入性(我也不知道有沒有人提出過這個(gè)關(guān)鍵字,因?yàn)榈拇_很少看見砂碉,簡單來說我這里說的切入性就是指一個(gè)以前沒有接觸過這個(gè)項(xiàng)目的人蛀蜜,能快速加入到這個(gè)項(xiàng)目中,對項(xiàng)目進(jìn)行維護(hù)增蹭、修改和擴(kuò)展)
維護(hù)性
一個(gè)好的軟件(不一定是成功的軟件滴某,這里說的好只是程序員認(rèn)為的代碼方面)肯定是能方便維護(hù)的,出了問題能快速定位,需要修改時(shí)能快速修改霎奢,并且在一定程度上不會(huì)說一修改就一堆bug户誓,這就是我認(rèn)為的可維護(hù)性,當(dāng)然后面要說到的切入性其實(shí)也算是維護(hù)性幕侠,不過為什么單獨(dú)放出來在切入性時(shí)我會(huì)詳細(xì)說明帝美。至于怎么樣才能使一個(gè)軟件維護(hù)方便,我覺得有以下幾點(diǎn):
1.代碼規(guī)范
一份代碼如果沒有遵循任何規(guī)范晤硕,那么我相信它的可維護(hù)性是很差的悼潭,就算是你一個(gè)人做出來的,估計(jì)過了幾個(gè)月去修改的時(shí)候也會(huì)冒出一句這TM是什么鬼
2.框架穩(wěn)定性
很多時(shí)候很多開源框架剛出來的時(shí)候舞箍,也許功能十分強(qiáng)大女责,但是畢竟剛出來,沒有經(jīng)過充分的測試创译,所以還是會(huì)或多或少存在一個(gè)不穩(wěn)定因子抵知,所以建議在選擇框架時(shí)盡量選擇成熟穩(wěn)定的框架,哪怕功能和性能的確比不上剛出來的框架软族。當(dāng)然這也不是說完全不用剛出來的框架刷喜,畢竟都不用,那么它也永遠(yuǎn)成熟不了立砸,至于到底用不用和怎么用掖疮,本文的框架選擇和使用篇也會(huì)詳細(xì)分析說明
3.封裝
本來想說AOP的,但是個(gè)人覺得很多專業(yè)性的名詞也比不上一些通俗的形容颗祝,畢竟本文主要的目的是讓人理解浊闪,不是提出理論。封裝這個(gè)在Android中是經(jīng)常使用的螺戳,簡單來說就是把一些常用的搁宾、通用的東西進(jìn)行一個(gè)封裝,通過統(tǒng)一入口進(jìn)行調(diào)用倔幼,這樣出問題就只需要修改一個(gè)地方盖腿,就能全部修改過來。同時(shí)封裝也要注意一些常見的坑损同,比如我曾經(jīng)就踩過的context坑翩腐,當(dāng)時(shí)封裝了一個(gè)UIUtils(主要是針對UI相關(guān)的工具類),因?yàn)楹芏喾椒ǘ家褂胏ontext膏燃,所以直接application傳進(jìn)去茂卦,保存了,所有方法都不用傳context组哩,但是這樣卻出了一個(gè)bug等龙,那就是有些操作用application的context是有問題的处渣。當(dāng)然這種還比較好處理,但是如果因?yàn)榉庋b導(dǎo)致內(nèi)存泄露而咆,這就難以查找了,比如你傳入了一個(gè)activity的context幕袱,但是activity已經(jīng)關(guān)閉了暴备,但是因?yàn)槟惴庋b的方法里面還在繼續(xù)使用這個(gè)context,所以activity的內(nèi)存也是不會(huì)釋放的们豌,所以封裝的時(shí)候也一定要注意涯捻,不要給自己挖坑
4.耦合
針對耦合這個(gè)東西相信很多文章都說過了,如何解耦合望迎,不過個(gè)人感覺解耦合這個(gè)東西也要適度障癌,不要因?yàn)榻鉀Q一點(diǎn)耦合,花了大量的代碼辩尊,浪費(fèi)了大量的性能涛浙,所以解耦合這個(gè)東西就一定要把握這個(gè)度,過度設(shè)計(jì)是有問題的設(shè)計(jì)摄欲,這點(diǎn)我是贊同的轿亮。同時(shí)很多時(shí)候封裝會(huì)導(dǎo)致一些耦合的問題,比如我曾經(jīng)一個(gè)項(xiàng)目中就有個(gè)這個(gè)一個(gè)情況:
因?yàn)轫?xiàng)目中使用了滑動(dòng)選取身高的WheelView胸墙,因?yàn)楫?dāng)時(shí)是彈Dialog出來選擇的我注,所以當(dāng)時(shí)想也沒想就直接封裝了一個(gè)Dialog,后面又出來了一個(gè)選取年齡的迟隅,然后又封裝了一個(gè)Dialog但骨,以至于到后面封裝的Dialog有7,8個(gè)了,但是有些界面因?yàn)檫x中后的處理有些不一樣智袭,導(dǎo)致Dialog里面的代碼混亂奔缠,所以后面就直接簡單的封裝了一個(gè)Dialog,傳入需要選中的數(shù)據(jù)集合和選中監(jiān)聽吼野,這樣就用了一個(gè)Dialog就能處理多種數(shù)據(jù)的選擇添坊,還能根據(jù)不同界面分別處理,當(dāng)然這樣也不是萬金油箫锤,畢竟這樣每個(gè)界面都需要自己實(shí)現(xiàn)監(jiān)聽贬蛙,所以很多時(shí)候有利就有弊,至于具體怎么取舍谚攒,就看利是否大于弊
擴(kuò)展性
擴(kuò)展性簡單來說就是當(dāng)程序需要新的功能時(shí)阳准,能否對其進(jìn)行擴(kuò)展以及擴(kuò)展的難度來判斷,如何提高擴(kuò)展性馏臭,我覺得有以下幾點(diǎn):
1.抽象接口
這點(diǎn)相信大家都經(jīng)常遇到野蝇,比如Android的點(diǎn)擊事件讼稚,你想要實(shí)現(xiàn)什么樣的點(diǎn)擊效果,自己實(shí)現(xiàn)一個(gè)點(diǎn)擊監(jiān)聽绕沈,然后設(shè)置給控件就可以了
2.元素重用
很多時(shí)候锐想,很多功能模塊可能使用到相同或者類似的元素,如何Android中的一些布局乍狐,這些如果抽取出公共部分在進(jìn)行擴(kuò)展的時(shí)候方便對其快速擴(kuò)展赠摇,當(dāng)然這個(gè)是項(xiàng)目一開始并不能預(yù)見的,所以需要在開發(fā)中不斷的去重構(gòu)
3.單一職責(zé)
這個(gè)跟其實(shí)就是面向?qū)ο蟮膯我宦氊?zé)浅蚪,比如前面提到的Dialog藕帜,一開始只有選擇身高的功能,看起來是單一職責(zé)惜傲,但是其實(shí)相關(guān)處理也包含在其中洽故,所以那個(gè)Dialog類本身職責(zé)已經(jīng)不單純,當(dāng)然如果一直只有這一個(gè)盗誊,這樣做沒有任何問題时甚,也能看做單一職責(zé),但是如果有多個(gè)選擇和處理的時(shí)候哈踱,就必須對其重構(gòu)了
4.替換性
替換性包含里氏代換但是也不僅僅是里氏代換撞秋,比如常見的Android布局不同,但是其顯示內(nèi)容大致相同嚣鄙,這樣寫布局的時(shí)候就可以對相同內(nèi)容的控件指定相同的id吻贿,這樣就算替換布局,也不用重寫ViewHolder哑子,當(dāng)然對于Adapter也僅僅只需要替換相應(yīng)的布局就ok
5.耦合
這個(gè)和維護(hù)的耦合相同舅列,畢竟很多地方本來就存在交叉,所以就沒有必要再說了
安全性
個(gè)人覺得數(shù)據(jù)安全性并不僅僅是數(shù)據(jù)安全卧蜓,還有程序的一些操作安全性(簡單來說就是避免程序出現(xiàn)一些非崩潰性異常)
1.數(shù)據(jù)安全性
數(shù)據(jù)安全就包括數(shù)據(jù)抓取帐要、數(shù)據(jù)攔截以及數(shù)據(jù)修改。當(dāng)然這些并不能完全避免弥奸,只能是由我們寫出盡量安全的代碼榨惠,比如關(guān)鍵數(shù)據(jù)使用HTTPS以及對數(shù)據(jù)進(jìn)行md5驗(yàn)證完整性,對于數(shù)據(jù)修改盛霎,可以通過多文件多地址保存文件修改記錄赠橙,來確定保存的數(shù)據(jù)是否被修改,畢竟Android只要獲取root權(quán)限愤炸,就能對很多文件進(jìn)行修改了
2.操作安全性
簡單來說經(jīng)常遇到的一個(gè)問題期揪,比如按鈕的點(diǎn)擊事件,有可能這個(gè)點(diǎn)擊事件是請求網(wǎng)絡(luò)或者打開Activity规个,這樣就會(huì)存在事件還未處理完成再次收到事件凤薛,只要你一直猛點(diǎn)姓建,肯定可以的,所以這樣就需要我們隊(duì)控件事件進(jìn)行一些封裝缤苫,比如打開界面的速兔,可以在點(diǎn)擊后禁用按鈕,界面打開完成后才啟用活玲,請求網(wǎng)絡(luò)的可以在開始就禁用按鈕涣狗,請求結(jié)果反饋了才啟用(不管是請求成功或者失敗)
切入性
切入性就是當(dāng)另外一個(gè)從未接觸過此項(xiàng)目的人翼虫,能快速進(jìn)入這個(gè)項(xiàng)目進(jìn)行開發(fā)屑柔,當(dāng)然想要切入性好屡萤,前面的維護(hù)和擴(kuò)展是必須要滿足的珍剑,下面我就說說我認(rèn)為能增加切入性的一些點(diǎn)
1.文檔
開發(fā)都不喜歡寫文檔,這是肯定的死陆,但是每當(dāng)我們?nèi)ソ邮忠粋€(gè)項(xiàng)目的時(shí)候招拙,發(fā)現(xiàn)沒有文檔估計(jì)就要開始罵娘了,所以文檔不僅要寫措译,還要寫的規(guī)范别凤。我認(rèn)為開發(fā)中必須要有的幾個(gè)文檔:代碼規(guī)范文檔(比如包名規(guī)范,文件命名規(guī)范领虹,id命名規(guī)范等等规哪,具體依據(jù)項(xiàng)目情況而定)、接口文檔
2.注釋
每個(gè)類必須要有注釋塌衰,方法也要有注釋诉稍,同時(shí)也要標(biāo)注好方法最后修改人是誰,這樣出現(xiàn)疑問或者問題別人就知道該去找誰了最疆,當(dāng)然有些方法是不需要有注釋的杯巨,比如重寫父類的方法,只是如果方法內(nèi)邏輯很復(fù)雜努酸,可以在方法中添加一些對邏輯的說明服爷。當(dāng)然注釋也不是越多越好,具體注釋該怎么寫获诈,Google最清楚仍源,不能Google百度也行
3.包名
什么類放什么包簡單,但是一但一個(gè)包中的類太多舔涎,也是非常不方便镜会,所以正確的分包也是非常重要的,目前常見的Android分包包括針對功能分包(不是指程序功能终抽,而是指UI,http,bean這些功能)戳表,還有就是模塊分包(這就是程序的功能了桶至,比如login,user等)匾旭,當(dāng)然具體怎么分包需要團(tuán)隊(duì)協(xié)商镣屹,防止一個(gè)包中類太多,而我現(xiàn)在一個(gè)程序很大的情況下价涝,采用的分包是先功能分包女蜈,再模塊分包,比如:
wang.raye.demo
|-activity
| |-user
| |-login
|-fragment
| |--user
| |--login
這樣能減少每個(gè)包的類數(shù)量色瘩,當(dāng)然如果項(xiàng)目本身并不是很大伪窖,可以完全不用這種分包模式,畢竟如果只是小項(xiàng)目居兆,這樣會(huì)使項(xiàng)目變得更加難以閱讀
MVC 還是 MVP
現(xiàn)在針對移動(dòng)端開發(fā)覆山,衍生了很多種架構(gòu),如MVC泥栖、MVP簇宽、MVVM,當(dāng)然這里著重分析MVC和MVP吧享,畢竟MVVM我也只是了解過一下魏割,沒有詳細(xì)接觸,至于什么是MVC和MVP我也不想做過多描述钢颂,這類的文章實(shí)在太多钞它,這里主要分析一下什么情況下用MVC和MVP
至于很多人接觸過MVP后就覺得MVC就一無是處,我個(gè)人覺得這是不對的殊鞭,不同的項(xiàng)目遭垛,不同的業(yè)務(wù),用不同的架構(gòu)钱豁,這是我覺得應(yīng)該做的事耻卡,沒有一種架構(gòu)能適合所有項(xiàng)目開發(fā)(畢竟現(xiàn)在還沒有),所以我們分析MVC和MVP分別在Android以什么樣的方式實(shí)現(xiàn)
MVC
MVC是以XML布局為V(視圖)牲尺,Activity或Fragment為C(控制器)卵酪,數(shù)據(jù)實(shí)體為M(模型),但是因?yàn)閄ML的局限性谤碳,所以其實(shí)我們還是需要在Activity或Fragment中對視圖進(jìn)行操作溃卡,所以這也就是為什么那么多人抵制MVC的原因,因?yàn)檫@也算不上完整的MVC
優(yōu)點(diǎn):開發(fā)迅速蜒简,結(jié)構(gòu)易理解
缺點(diǎn):當(dāng)一個(gè)界面業(yè)務(wù)邏輯一多瘸羡,不方便維護(hù)
MVP
MVP是為XML配合Activity或Fragment為V(視圖),同時(shí)抽象出接口搓茬,界面相關(guān)業(yè)務(wù)抽離出來的P(Presenter)同時(shí)通過視圖接口來更新UI,數(shù)據(jù)實(shí)體為M(模型)
優(yōu)點(diǎn):業(yè)務(wù)發(fā)生變化時(shí)易修改犹赖,同時(shí)能減少修改過程中引發(fā)bug队他,也能將多人協(xié)同開發(fā)充分調(diào)用起來(并不是針對一個(gè)人負(fù)責(zé)一個(gè)模塊的模式,而是多人協(xié)同開發(fā)一個(gè)模塊)
缺點(diǎn):開發(fā)速度會(huì)有所降低
所以對比2種架構(gòu)峻村,發(fā)現(xiàn)MVC適合不需要太多業(yè)務(wù)邏輯和功能性少的APP麸折,比如數(shù)據(jù)展示類應(yīng)用,MVP適合每個(gè)界面有復(fù)雜邏輯以及大型多人開發(fā)的APP
框架選擇及使用
如何選擇框架
1.穩(wěn)定性
如果框架本身就不穩(wěn)定粘昨,那么導(dǎo)致的結(jié)果就是程序本身也會(huì)漏洞百出垢啼,所以選擇框架一定要選擇經(jīng)歷過考驗(yàn)的穩(wěn)定的框架
2.擴(kuò)展性
隨著程序功能的增加,以前的框架可能會(huì)出現(xiàn)功能不足的情況张肾,但是因?yàn)檫@點(diǎn)是不可預(yù)見的芭析,所以我們選擇框架時(shí)一定要了解好框架本身的擴(kuò)展性如何,或者對框架有較深的理解吞瞪,能夠自己擴(kuò)展框架馁启,當(dāng)然有些框架解決的問題比較單一,一般也不用擔(dān)心過多的擴(kuò)展性尸饺,比如Butterknife或PreIOC這類單一性框架进统,但是有些框架經(jīng)常需要配合做一些操作助币,比如圖片加載框架浪听,常見的一些就是清理圖片緩存、獲取圖片緩存大小眉菱、顯示圓角或者圓形圖片迹栓, 常用的圖片加載框架UniversalImageLoader都提供了相關(guān)的方法或接口來實(shí)現(xiàn)
3.封裝性
封裝性是指能否針對框架進(jìn)行二次封裝,以及封裝后的耦合度俭缓,詳細(xì)會(huì)在使用篇說明
如何使用
選擇好了框架千萬不要拿來就用克伊,因?yàn)樵俸玫目蚣芤灿兴窒薜牡胤剑?dāng)然你也可以簡單的在遇到這個(gè)框架不能實(shí)現(xiàn)的時(shí)候华坦,添加另外一種框架愿吹,只是這樣項(xiàng)目會(huì)越來越大,對于APP來說APK也越來越大惜姐,65535 的問題也會(huì)提前出現(xiàn)犁跪,所以為了方便以后有可能出現(xiàn)的切換框架,以及防止初期對框架使用不熟悉而引發(fā)出新的bug歹袁,在選擇好了框架后坷衍,一定要對框架進(jìn)行二次封裝,當(dāng)然有些框架是不需要二次封裝的条舔,比如前面說的單一性的框架Butterknife或PreIOC枫耳,但是像UniversalImageLoader、OKHttp等框架孟抗,必須要進(jìn)行二次封裝迁杨,至于封裝原則钻心,則是封裝后,調(diào)用框架對于調(diào)用代碼來說是透明的铅协,簡單來說扔役,就是對于框架調(diào)用都通過一個(gè)統(tǒng)一的入口進(jìn)入,并且調(diào)用時(shí)警医,不需要傳入任何跟框架相關(guān)的東西亿胸,如果必須要傳入接口,可以通過繼承框架來實(shí)現(xiàn)新的接口傳入预皇,這樣在真正的使用框架的地方侈玄,沒有任何關(guān)于框架的引用
封裝的好處
之所以要這樣封裝,最大的好處就是一旦框架不能滿足需求時(shí)吟温,需要進(jìn)行框架更換時(shí)序仙,只需要換掉框架,同時(shí)修改統(tǒng)一入口處的代碼鲁豪,就能快速的替換整個(gè)框架
結(jié)尾
以上就是我在Android APP架構(gòu)上面的一些心得潘悼,此文可能并沒有教你快速搭建一個(gè)框架,只是指明搭建框架時(shí)需要注意和搭建框架的一個(gè)方向爬橡,當(dāng)然軟件是死的人是活的治唤,具體項(xiàng)目具體處理,畢竟別人的東西只能是借鑒和學(xué)習(xí)其好的思想糙申。同時(shí)也歡迎大家吐槽交流(QQ群:123965382)